Not a control engineer

I'm building a robot that needs control better than I've ever needed to think about before. The details don't matter, suffice it to say I'm using
the opportunity to learn about PID.
I think I understand the idea, but I'm having trouble getting a sample working. I coded a simple simulation of a CarBot in Python and am trying to use PID to keep it going in a straight line, but I'm not totally succeeding.
In fact, I'm not even succeeding with just a PI algorithm. Graphing the output (of sideways velocity, which is what I've chosen my variable to be) I see that it oscillates and then settles down, but not at the setpoint.
I've done a lot of manual tuning (maybe "messing around with the knobs" would be a better way to put it) and this is about the best I can get. Shouldn't I be able to hit the setpoint on the nose with PI?
(I realize this situation has no deadtime and thus no need of the D in PID--I'll add that once I get PI working. Also, there's some useless math in there--division by 1, subtraction of 0, etc--that's because I want to make sure I know where to put the real numbers later.)
#!/usr/bin/python
class Car: x_pos = 0 y_pos = 0 xv = 1 yv = 0
def Step(self): self.x_pos += self.xv self.y_pos += self.yv
self.yv += .001
def PrintPos(self): print self.x_pos, self.y_pos
def PrintYV(self): print self.yv
bot = Car()
last_y = 0 yi = 0 last_yv = 0
KP = .7 KI = 0.2 KD = 0.0
for t in range(100): bot.Step(); bot.PrintYV();
yv = (bot.y_pos - last_y)/1 yi = yi + (yv - 0)*1 yd = (yv - last_yv)/1
last_y = bot.y_pos last_yv = yv
# print yv, yi
bot.yv += (-(KP * yv + KI * yi - KD * yd))
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
After some more thinking and poking around, I think I'm just expecting too much when I say I should "be able to hit the setpoint on the nose". I hit .001, which is pretty close. And even if I extend my simulation out to 1e5 time steps it stays there solidly.
Hopefully the analog of this error in my actual robot won't be of a magnitude to cause a problem.
PhysicsGenius wrote:

Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Genius,
If you do the math, you will see that a linear system with an integrator in the loop makes overshoot inevitable. Overshoot is not only awkward, it wastes energy. Appropriate nonlinearity can eliminate overshoot from second-order systems at least, and thinking along those lines can lead you to better configurations than PID. For starters, read my blurb at http://users.erols.com/jyavins/servo.html
Jerry
--
Engineering is the art of making what you want from things you can get.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Jerry Avins wrote:

I understand "overshoot" to be how much I go over and then have to backtrack (not an issue I'm having), not a consistent, stable error (the issue I'm having).
If by "an integrator" you mean an integral term, I don't understand that either. I thought it was the derivative term that addressed overshoot while the integral term addresses persistent offset.
Overshoot is not only awkward,

Since I can't even get PID working (or at least I can't tell IF I have it working), I don't think I'm prepared to go off into even more theoretical territory yet.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Translation: "Since I can't even get the race car smoothly into gear, I don't think I'm prepared to mess with a sedan." PIDs are among the most difficult boxes to tame, particularly as most descriptions are as far off the mark as the belief that an airplane's throttle controls speed and the elevators control climb. (In the steady state, it's the other way round.) There are useful insights to PIDs in Tim's Wescott's article http://www.embedded.com/2000/0010/0010feat3.htm
I hope you don't mean "Don't bother me with theory, I just want it to work." Unless you introduce non-linearities of the right sort, the integrator term guarantees that the response to a command step will include overshoot. The theory is simple: while the output has not yet caught up to the command, the error will accumulate in the integrator. An error in the opposite direction, with equal product of error and time, will be needed to restore the integrator to balance. This is entirely apart from the integrator's altering the set point (re-set) to compensate for steady-state loads. Two simple non-linearities can largely fix this: disable the integrator if the error is large and dump it entirely if the integrated and actual errors differ in sign.
The derivative term applies damping if the phase isn't shifted too far. that tends to damp out oscillations, but it can also slow the recovery from overshoot enough to cause equal overshoot in the opposite direction. When recovery from overshoot results in a new overshoot not less than the last one, it's oscillating. A loop in which not only the error but also the command signal is differentiated performs poorly.
Jerry
--
Engineering is the art of making what you want from things you can get.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Jerry Avins wrote:

In this I have to disagree with Jerry -- until you can get a simple linear model of a PID working you shouldn't worry about non-linear controllers. So delete that line where you add in the 0.001 velocity...
Now, in the real world you can easily build a system that will not be unconditionally stable _without_ anti-windup. You do not have such a system here, so you don't have to worry.
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Genius,
Go with Tim, but remember what I wrote for later. I confess that I had overlooked your not having any hardware yet.
To change the topic, below is a deliberate example of accurate but dishonest excerpting.
Jerry
--
Engineering is the art of making what you want from things you can get.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Tim Wescott wrote:

Aha, so this is what he meant by non-linear. Well, the real object I'm going to be controlling is non-linear in this sense, so I guess I *should* worry about a non-linear controller.

"Anti-windup", eh? I guess I'll have to re- (and re-re-re-) read your article to figure out what that means and how to add it.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Jerry Avins wrote:

I was under the impression that PIDs were the bread and butter of control and that your original link was caviar. Apparently that's not the case.

If I just wanted it to work without knowing how, I wouldn't be doing this at all. It's just a fun project with no practical aspect at all.

I understand each of these words, but not all put together. I found that embedded.com article when I was searching before and it was one of the most helpful, being targetted at programmers instead of engineerings. Are there any other sources that slant this way? Most of the tutorials I've found online assume I already know a lot of control theory jargon and math, which I don't.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Jerry's link is one (fairly good) way to implement anti-windup in PID controllers. There are worse ways -- including the one in my Embedded Systems paper, but in that case I felt that an anti-windup scheme that was easy to understand was better than one that would generally perform better but add more memes to be absorbed (and words against an already crowded word count).

There are some other (hopefully) useful articles on my website -- in particular you may find http://www.wescottdesign.com/articles/zTransform/z-transforms.html to be useful, but don't let that stop you from reading the rest.
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Tim Wescott wrote:

I definitely need the lower meme count version.

Is there a version of the PID/PhD article with the images inline?
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Not that I can distribute, unfortunately -- ESP owns the rights. When I get the chance I need to write a version that I can call my own and post properly.
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:
...

The figures are links in http://www.embedded.com/2000/0010/0010feat3.htm
Jerry
--
Engineering is the art of making what you want from things you can get.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Jerry Avins wrote:

Unfortunately you can't just print the article and have the figures come out -- I've received that comment before.
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

The increments accumulated in the integrator approach zero as the position approaches the setpoint. If you build the integrator with an insufficient number of digits the integration will stall (accumulate zeros instead of infinitesimal increments) at some small error.
--
John Popelish

Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
John Popelish wrote:

I'm not an expert on this, but I don't think "number of digits" is a problem with Python. If you run the program I posted and view the output, you'll see a LOT of digits there. It seems more like it just gets locked in around .001.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Possibly because in your "Step" routine you're adding 0.001 to self.yv after you update the positions? You're servoing quite nicely to bot.y_pos not changing, which is what you've coded for -- and bot.yv is zero before each call to bot.Step().
Just a note: While you're pondering careful coding habits, you should also ponder the difference between "lots of digits" and "lots of meaningful digits".
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Tim Wescott wrote:

I know what the program part is doing and what meaningful digits are. But shouldn't the error be building up in the integrator and eventually be wiped out?
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
PhysicsGenius wrote:

Good
Yes, and it is. If you trace _carefully_ through the code you'll see that the integrator is just the right value so that when the Step() function is called the y velocity is zero -- but then the Step() function adds 0.001 to that y velocity, which is what you are seeing and wondering about.
It isn't a control problem -- it's a garden-variety bug in your code.
--

Tim Wescott
Wescott Design Services
  Click to see the full signature.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload
Tim Wescott wrote:

Oh, I see what you are saying. I was thinking it wasn't a bug because in a real device an acceleration can happen at any time, so it doesn't matter where I put it. But the situation here is that I'm accelerating just before *printing*, which gives the *illusion* that the PID isn't working when it really is.
I guess this is why I did it in software first. Thanks for the tip.
Add pictures here
βœ–
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Add image file
Upload

Polytechforum.com is a website by engineers for engineers. It is not affiliated with any of manufacturers or vendors discussed here. All logos and trade names are the property of their respective owners.