<div dir="ltr">Hi Murray,<br><br>I mostly have questions. I'll be a bit verbose 'cause I might be able to reuse some of this with my team.<br><br>1. You said you were using a Thunderborg motor controller. Is it this one: <a href="https://www.piborg.org/motor-control-1135/thunderborg">https://www.piborg.org/motor-control-1135/thunderborg</a><br><br>2. What is your purpose - what are you trying to accomplish? Controlling an arm for example is different than controlling a differential drive. Are you controlling for speed? For position? For differential rotation? <br><br>3. I see the same problems with nomenclature all the time with my team and the systems we work with. With the api we use, setpower() might be a proxy for setting a normalized voltage or might be a proxy for set speed depending on how we've configured motor control and encoders. But looking up the thunderborg, I don't see evidence for any kind of feedback. There's no mention of current monitoring built into the motor controller. It also doesn't seem to have inputs for quadrature encoders; and I didn't see any mention in your post about adding your own encoders in the system. <br><br>Maybe you over simplified the psuedo code, but going from what you posted, it seems you are just doing a get_power that simply returns the power you previously set in the prior run of the loop. I don't see any new feedback entering your algoritm. It seems to me that if your target power (normalized voltage) is unchanging, your error will always be zero and you'll never adjust the power at all. Is your error changing? <br><br>4. The concept of stacking PIDs always seems weird to me. Not that we never do it. There are cases where we might want a lower level motor controller to set a steady velocity while a higher level controller drives toward a final position. But these are controlling two different things. Only the low level one is controlling the motor. The other is controlling the target for the low level controller. Typically the low level one would normally operate at a finer grain time scale than the higher level controller. Otherwise it doesn't make sense to have two levels. Make sure you really need two controllers. It can make debugging much harder and might not be adding that much benefit.<br><br>We have to think clearly about what we are trying to do. For a simple DC motor we typically only have control over one output and that is voltage. The voltage will generally relate to speed given a ton of real world caveats. Note I didn't say the voltage is perfectly linearly proportional to speed. It's because of those caveats that we take input from encoders to correct the system. But we need to decide what we are trying to control and that will give us how we calculate your error. Yup, velocity is just the derivative of position and automatically calculates in your PID algoritm if it's based on encoder input. But if you want to control velocity then your error should be the difference between your target and measured velocities.<br><br>5. Going back to your psuedo code - I don't see any correction for the time between loops. I see this in a lot of PID implementations - they often assume a constant time between calls. Because we use cooperative multitasking we never assume that we have a constant elapsed time and we always normalize inputs to  the amount of elapsed time between feedback measurements. Be aware this also intrisically converts measurements to velocity, but the benefit is that if your control loop changes duration or varies in duration, it won't require you to retune your PID constants.<br><br>6. Also in your psuedo code - I don't see any handling for integral windup. That's a whole other level of discussion, so I won't go into it now. I would just be careful about introducing the integral - we find it to be the most tricky of the terms to tune and control. This could also bring up a whole discussion of the regimes over which each of the P, I and D terms operate - which could also be an extensive discussion. The question is, do you need the I term at all? It's the term than brings in the near-zero precision to your control system, but can also wreak a lot of havoc if you don't have windup control. David talks about a leaky integrator as one of his favorite anti windup mecanisms. There are a variety of others and the optimal combination might be application specific.<br><br>7. I just saw your update. So you do have encoders in the system. But you might not be using them yet? Again, is your error actually ever non zero? You have to calculate your error from the difference between what your sensors/encoders are actually measuring and what you want them to be (your setpoint). It makes sense to us to first scale our encoder input to some kind of natural units. For example we will calibrate the number of ticks_per_meter or ticks_per_degree (or radian) that maps our encoder readings to the real world. Drive your robot for one second, measure how far it moved, divide into the number of ticks it moved during that run. Then we'll have a low level functions that scale those encoder values to our real world units and change them to velocities if needed.<br><br>I'm not sure about this, but I think I see some dissonance in your phrasing about adjusting the voltage so you should somehow need to get voltage as an input to the error calculation? Don't dwell on the voltage. It's not an input to the error calc. It's just what you adjust against. It's the job of the measured error and the PID constants and calculation to adjust it. If you share your actual code we might be better able to help.<br><div><br></div><div>HTH,</div><div><br></div><div>Karim</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Apr 21, 2020 at 5:24 PM Murray Altheim via DPRGlist <<a href="mailto:dprglist@lists.dprg.org">dprglist@lists.dprg.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">No, Doug, unless you got something wrong your description is very helpful, one<br>
of the more helpful descriptions of the P, PI, PD, and PID variations, and why<br>
one might use each. Indeed, in all the descriptions I've found online this<br>
was one of the more helpful, nice, straightforward, and with enough detail to<br>
actually help out on the variations, thanks!<br>
<br>
What I did last night was resurrect the controller I'd written that uses a<br>
value I'd mistakenly called "power" (in the code as well) when I meant the<br>
value sent to the motor controller. It now acts as a PID controller for what<br>
I'm assuming is actually voltage.<br>
<br>
In some ways that was largely successful. I was tweaking the P, I and D<br>
constants (as one does) but I think it was mostly functional. I was using<br>
the value from GetMotor() to set SetMotor(). Probably voltage. Maybe power,<br>
I don't know the guts of the ThunderBorg firmware, but a value between 0.0<br>
and 1.0.<br>
<br>
I think David A. in his video mentioned that there wasn't a direct relationship<br>
between voltage and velocity, but between torque and velocity. You can see his<br>
explanation (in describing the PWM of an H-bridge controller) at 20:15:<br>
<br>
    <a href="https://www.youtube.com/watch?v=8CXReb7f0Eo" rel="noreferrer" target="_blank">https://www.youtube.com/watch?v=8CXReb7f0Eo</a><br>
<br>
"the speed of the motors is not really proportional to the voltage, it's actually<br>
the torque of the DC motors is proportional to the current."<br>
<br>
To clarify my difficulty, what I think I'm doing wrong is that my current PID<br>
controller takes the value I'm sending the robot (I think voltage) as the input,<br>
does its PID computation, then sets the output (again, as I think voltage),<br>
whatever is sent to SetMotor().<br>
<br>
What I am not able to do is figure out how to change the input to the *velocity*<br>
I'm getting from the encoders. Yes, I can certainly use the velocity measurement<br>
as input (I have a function for that), but because I can't "set velocity", only<br>
the value I sent to SetMotor(), which I think is voltage or some surrogate, I<br>
can't figure out what the relationship is between velocity and voltage in the<br>
code. It's not a direct relationship.<br>
<br>
So what I've done (so far, last night) is take my "voltage"-based PID controller<br>
and wrap it with a velocity-based PID controller. I'm not sure if that's either<br>
necessary or sensible, but that's where I'm at right now.<br>
<br>
This will (very likely) be easier to deal with in a conversation, so I'm<br>
looking forward to today's/this evening's video conference.<br>
<br>
Cheers,<br>
<br>
Murray<br>
<br>
On 22/04/20 3:58 am, <a href="mailto:secretary@dprg.org" target="_blank">secretary@dprg.org</a> wrote:<br>
[...]<br>
> Now take this scenario, your robot with encoder feedback is moving on a > level floor at a given speed set by your PID (or P, or PI, or PD) controller,<br>
> then it comes to the ramp. If you have a P controller the speed will change<br>
> to a certain level as the controller increases the voltage (and thus the <br>
> current and torque) to some value that is lower than your level speed but<br>
> higher than it would be without any feedback. The reason that it doesn't<br>
> maintain perfect speed control is because the feedback value comes close to > the desired (reference) value, a P controller loses its ability to change<br>
> the output, in a sense it runs out of steam the closer it gets. So for a P<br>
> controller there will always have an offset from the desired speed set by<br>
> your reference signal. Often this is all you need. If you have a PI <br>
> controller, the I portion of the controller will allow your output to match<br>
> your reference setting at the expense of adding more instability (however,<br>
> it maintains response even as speed approaches reference). For most robots<br>
> this is usually all you need. Adding a D component (i.e., PID vs PI) can<br>
> add response with reduced oscillations in output if done right, but it adds<br>
> a level of trickiness to the tuning.  I often use only a P, or PI controller,<br>
> in my robot depending on what I am doing. I often wrap a velocity PI inside<br>
> of a heading P controller. In the case of a line following robot, a PD > controller is often used. What the D does is add response to rapid changes.<br>
> The robot will not run at the reference setting with PD, for that can only<br>
> be done with an I component in the controller. Note that both I and D have<br>
> a dt component, you must keep loop time constant or adjust for varying it.<br>
> Also, please note that the above is somewhat general, people with more<br>
> knowledge may have a better understanding than me.  I hope others will <br>
> jump in and correct me where I have gone off base.<br>
...........................................................................<br>
Murray Altheim <murray18 at altheim dot com>                       = =  ===<br>
<a href="http://www.altheim.com/murray/" rel="noreferrer" target="_blank">http://www.altheim.com/murray/</a>                                     ===  ===<br>
                                                                    = =  ===<br>
     In the evening<br>
     The rice leaves in the garden<br>
     Rustle in the autumn wind<br>
     That blows through my reed hut.<br>
            -- Minamoto no Tsunenobu<br>
<br>
_______________________________________________<br>
DPRGlist mailing list<br>
<a href="mailto:DPRGlist@lists.dprg.org" target="_blank">DPRGlist@lists.dprg.org</a><br>
<a href="http://lists.dprg.org/listinfo.cgi/dprglist-dprg.org" rel="noreferrer" target="_blank">http://lists.dprg.org/listinfo.cgi/dprglist-dprg.org</a><br>
</blockquote></div>