Notifications
Clear all

Working with ArduPID for Motor Speed Control

63 Posts
6 Users
9 Likes
3,091 Views
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

Grtz,
Ray


   
Quote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2042
 

@electricray1981

Have you been able to figure out the problem?

Unfortunately I can't test your code as my robot base uses a mega and doesn't have WiFi connections and all those other header files.  It would be a bit tedious to try and extract the pid code to look for issues. Where did you get the pid code?

I think there is a pid library that you can use.

 

 


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

@robotbuilder 

Thanks for asking. I have partly fixed the issue but found another issue probably related to it. So I will try to give all the info and measurements here to see if someone can point out what I am doing wrong or how to find the issue.

In first post I could not see the PID regulation doing its work there was no reaction on the output signal. Well this came probably because I used a to low Kp. I followed some tutors on the internet and started to increase Kp up to oscillation occurs (this was at a Kp of 1500) than I divided this value by 2 which is advised as a good starting point. Doing this I can see the PID (actualy the P-gain) is doing work. This seems good.

 

image

The above image shows that when the speed drops (blue line) the output signal (PWM in magenta) is increasing. But the speed value is never reaching the green line (Setpoint speed). 

Yesterday evening I noticed that this offset is proportional. Setting a speed of 0.25m/s the end speed will be 0.18m/s. But when I set a speed of 0.5 m/s the final speed will be 0.36 m/s. 

About two or three weeks ago I had a pretty good speed reading. I checked the pulses on th eencoders with my scope and compared them with the software outputs. Only thing I have changed is the filter, first I have a moving average filter and I went to a exponential filter. Maybe this was a ig mistake and today I will try to go back and see if I have a better outcome.

I was also suggested to change my approach, I thought that when I control the speed of the motors to be equal I will be able to drive straight but possibly this is wrong and should base this control on other parameters....I don't yet and will have to experiment a bit (which is the fun part of all this).

 

Grtz,
Ray


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

Ok I did some tests to see how the software is behaves and I think I know where the problem is but how to solve is the next question. Below is my SpeedSensing function, which uses interrupts and takes values of pasing interrupt times and places them into a circular buffer called e.g. PulsePeriodLeft at this bufer the average is computed to get a more stable value.

When I remove the moving average filter I have by the way the same issue. So I guess the problem lies somewhere at the interrupts. I think this because also when the motors are at 0 rpm the last value will be vissible offcourse this is not correct as it should be 0. Looking to the code it makes sense cause if no interrupt occurs nothing will change. So at hte end it all has to do with my programming skills and it seems to me there are some bugs here.

void SpeedSensing()
//This functions determines the current speed of Left and Right wheels
{

    cli();                                                                                //stop interrupts
    prevPulseUsCopyRight = prevPulseUsRight;
    pulseUsCopyRight = pulseUsRight;
    prevPulseUsCopyLeft = prevPulseUsLeft;
    pulseUsCopyLeft = pulseUsLeft;
   
    sei();                                                                                //allow interrupts
    pulsePeriodRight = pulseUsCopyRight - prevPulseUsCopyRight;                           //Length of one pulse period
    pulsePeriodLeft = pulseUsCopyLeft - prevPulseUsCopyLeft;

    PulseTimesRight.Add(pulsePeriodRight);
    PulseTimesLeft.Add(pulsePeriodLeft);
    MoveAverageSpeed();

    if(pulsePeriodRight != 0 || pulsePeriodLeft != 0) 
    {
    rpsRight = (float)slotUs / (float)AvgPulseTimeRight ;                                    //Compute RPS value
    AvgVelocityRight = (PI * WheelDiameter) * rpsRight;                                      //Compute measured speed in m/s right wheel
    rpsLeft = (float)slotUs / (float)AvgPulseTimeLeft ;                                      //Compute RPS value
    AvgVelocityLeft= (PI * WheelDiameter) * rpsLeft;                                         //Compute measured speed in m/s left wheel   
    }
}

Grtz,
Ray


   
ReplyQuote
 RCC1
(@rcc1)
Member
Joined: 3 years ago
Posts: 23
 

@electricray1981 When using the PID control are you just using the "Proportional" part of the PID (gain)?  With a process control loop if you use just the proportional part of the PID you will never get setpoint and measurement to agree.   As the measurement gets closer to the setpoint the feedback error gets smaller.  There is a point where the error is so small you would have to have a very small proportional band setting (very large gain) to make the output move any more.   It will look like it is always controlling with a small offset.   You need the "Integral"  function of the PID along with the proportional to to get setpoint and measurement to become equal.  Try googling PID functions: ---> Proportional. Integral, and Derivative.

RCC1


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2042
 

@electricray1981 

The only guy I remember that was controlling a robot base from a computer was @inq with his time of flight experiments.  I assume he was using PID control maybe he can chime in...

I would have too work my way up with actual hardware step by step like this,

https://www.electroniclinic.com/arduino-dc-motor-speed-control-with-encoder-arduino-dc-motor-encoder/

and then worry about sending a speed request value from a remote computer once I got the PID working correctly.

I haven't tried PID control yet except on a little virtual robot experiment.

 


   
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 

@electricray1981, @robotbuilder,

Hay!  My ears are burning. 😆 

I've barely scanned your thread.  Sounds like PID, motors and something about control from computer.  I'll read through your thread later.  I didn't get to where you asked a specific question yet.  So... I'll throw some things out, and see if anything sticks.

PID - This is a project I did to build a ventilator.  The pressure as a function of time for a ventilator must have a very special shape.  I used pressure sensors in the mask to feedback and PID was used to regulate an intake and exhaust valve setting to track the curve.  Here is the project page InqVent.  The PID does not control the blower motor as the response time of the blower was way too slow to be useful.  It is controlled, but just to achieve peak pressure.  All the control is on an ESP8266, however, it also hosts a web interface that is plotting real time data to a laptop, tablet and phone simultaneously.

More recently, I did a robot, but it was using stepper motors as I feel its far easier to control the robot accurately with those instead of motors, encoders and PID.  This is not a common opinion on this forum.  Most prefer the encoder route.  Below is a YouTube on one of the test runs.  It also shows using the same ESP8266 and hosting a web interface to control the robot from a phone, while the laptop shows the joy stick position and other real-time telemetry.  Here is the full build thread showing the evolution of the build. https://forum.dronebotworkshop.com/user-robot-projects/inqling-junior-robot-mapping-vision-autonomy/

 

 

 

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

@rcc1 

Thanks for your comment, I do indeed only use the P because it was a good starting point I read. But seeing your comment what I have now is probably normal behaviour although I have an error of about 28% which I think is pretty high. 

If I set 0.25 m/s I reach 0.18 m/s and this difference is proprtional so when I double the setpoint the difference is doubled.

I'll have a look further in PID tunning, your comments makes me think nothing is wrong with the software (although I see some bugs but those have nothing to do with the error I get now)

Grtz,
Ray


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

@robotbuilder 

Thanks for your help. The link you've posted is basically what I am doing except that I use ArduPID.h library for the PID control. In your link they hardcoded the algorithm by themself instead of using a library.

Grtz,
Ray


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

@inq 

Thanks for your comment. You have made a pretty awesome bot (I like it's sound it makes me think of Star Wars bots 😆).

What you have done is basically what I'm trying to accomplish with different hardware I suppose. I'm able to ctroll the motors from my GUI which I made with MegunoLink. And I receive speed feedback and PWM outputs back and later I want to add more sensors but for now the goal is to make it drive in a straight line. I thought I could do this by controlling the speed of the motors and make them equal. 

The issue/question I have is that the speed setting is never reached. RCC1 pointed allready something out to me which I did not know (remember anymore from school) is that the setpoint is never reached when using only the P gain, so I will start playing with this first and see if I get improvements.

I have a bug in the code which I'm unable to fix at the moment but that's related to other things nevertheless important to fix but I will come back to that later. I would have to change the code a bit so it becomes smaller and easier for other people to read. The above posted code is too long and has stuff which is not related to any issue so I think I would have take it out in order someone could help me.

All you guys thanks for the feedback I appreciate all the help.

Grtz,
Ray


   
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 

@electricray1981,

Here are a couple of bookmarks I found in my browser that I'm sure I must have found helpful at one time.  😉 

https://www.crossco.com/resources/technical/how-to-tune-pid-loops/

https://www.pidtuning.net/fivedeadlymistakes.html

Depending on what your end goals are and you're allowed to do with your project, there is an automatic PID tuning algorithm in the Marlin, 3D Printer code base.  I had a student interested in creating an automatic PID tuner and I pointed him toward it.  That code base is quite large, but searching for "PID", got me to the pertinent code.

 

Doing some dumpster diving in my old code, here are the pertinent pieces.  The tuning documents above basically say you tune them in order P,I,D.  It sounds like you have P pretty close and @rcc1 is definitely right about having to have more than just P.  Here is my PID function for the ventilator.  The constants at the top were the starting points and got the error down below 10%.  The full program had these configurable and using the tuning process, I believe I had the error down well below 1%.

_config->Kp = 3200;
_config->Ki = 2000;
_config->Kd = 0;

void InqVent::setValves(s32 time, s32 targetPressure)
{
    // Calculates positions of both valves.
    // Assumes valves will handle numbers outside of their range.
    // > 0 - Exhale closed, Inhale open 0 to 100
    // < 0 - Inhale closed, Exhale open 0 to 100
    static const s32 MAX_INT = 2147470000;
    static s32 lT = 0;  // Last time 
    static s32 lE = 0;  // Last error
    
    // Deviation from where we want to be.
    s32 err = targetPressure - _pressure;     

    // Derivative portion - on what it suppose to be, not error - EVALUATE
    s32 dt = time > lT ? time - lT : time + _config->msPerCycle - lT;
    if (dt == 0)
        return;
    s32 deriv = (err - lE) * 1000 / dt;
    lT = time;
    lE = err;

    // Integral portion
    _sum = min(MAX_INT, max(-MAX_INT, _sum + err));
    
    // Combine
    s32 value = _config->Kp * (err + _config->Ki * _sum / 100000 + 
        _config->Kd * deriv / 100000) / 10;
    _inhale.setMilli(value);
    _exhale.setMilli(-value);
    //Debug::log("tP=%d  cP=%d  err=%d  val=%d  i=%u  e=%u\n",
        //targetPressure, _pressure, err, value, _inhale.get(), _exhale.get());
}

 

Good luck with your project.

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

@inq 

 

Thanks for the info those links will help me a lot. I think I'm finally back at the position of trying to tune further.

I noticed these bugs I wrote about in previous posts that they had to be fixed first before I could tune them. This is because they relate to the speedsensing of the motors and offcourse if this sensing is not correct how can one tune the speed of the motor.

So I had in the code that I posted some functions to create a circular buffer which I used to compute a moving average. But inside it there were bug that when the speed was 0 the values were not adjusted accordingly so...motor at 0 rpm my PID control still received the old speed value. 
You don't need to have a PHD to understand that this is completely wrong. I have been struggling where this problem was but found it.

Than I noticed as well that when the code received a 0 value and computing the average on 0 values I got "inf"values instead of 0 which I wanted.
This made me decide to use a filter lib (I use now an Expenential Filter with a weight of 4). This gives me pretty stable speed readings. Ok still with a error of about 20% (when only using P) but now when the speed get zero the control loop sees 0.

This all took me from this moring up to now but I think I can starting trying to tune the motors FINALLY.

This is really a cool project I'm learning a lot about programming an controls and electronics....all the afford and mistakes worth IMHO

Grtz,
Ray


   
ReplyQuote
ElectricRay1981
(@electricray1981)
Member
Joined: 1 year ago
Posts: 119
Topic starter  

Besides of all this I would like to give a compliment to all people here on the forum with their positive comments that really help people who are trying to learn something. 
Lately I see on e.g. the Arduino forum so many negative, sacastic answers to questions which really doesn't help someone to learn something. I personally never post a question on a forum to get the answer for free I ask it because I get stuck and don't know how to solve it. No one has to write the code for me I really want to do it for myself. But sometimes there are answers on certain forums that makes you think dropping the thing youre trying to do. 

But this forum is a bit different I see so a big thumb up for all people that have tried to help me lately here

BR

 

Ray

Grtz,
Ray


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6930
 

@electricray1981 You are the kind of member the forum needs more of. So many come here and simply say my thing doesn't work, help me. You think I exaggerate but it's true.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2042
 

@electricray1981 

Besides of all this I would like to give a compliment to all people here on the forum with their positive comments that really help people who are trying to learn something.

Remember that a lot of people on forums may well also be learning, the reason they are here in the first place, and there may not be that many experts to help.

A question might be left orphaned by itself because time passes and no one is reading an older post which is what I thought might have been happening with your post so I tried to bring it to other's attention.

Had I done some wifi and pid projects I probably could have skimmed through your code to tell you where the issues were.  Sadly I haven't been motivated for some time now to do anything.  The hardware is collecting dust unused.

Having others take an interest in your project can itself be an impulse to making the extra effort to solve it yourself.

The biggest error I see people make when looking for someone to take an interest in their project is simply not providing all the information such as the code they wrote and the hardware parts and wiring. It also helps if the project goal is provided if only to get a possible interested party involved.

 


   
ReplyQuote
Page 1 / 5