|
From: Jeff E. <je...@un...> - 2010-10-26 22:27:11
|
It's been mentioned many times, most recently by Jon Elson, that it
would be nice if pid could take a better velocity estimate than the one
it gets by performing a two-point numeric differentiation of the error
term.
I got around to implementing this and testing it very lightly on
servo-sim. It looks promising!
servo-sim uses the software encoder counter, which has a high-quality
velocity estimate encoder.#.velocity. This estimate can be hooked to
the modified pid component as follows:
halcmd: net Xvel-fb encoder.0.velocity pid.0.feedback-deriv
I interactively modified the gains from the ones in the servo-sim:
Component Pins:
Owner Type Dir Value Name
18 float I/O 12 pid.0.Dgain
18 float I/O 0 pid.0.FF0
18 float I/O 1 pid.0.FF1
18 float I/O 0 pid.0.FF2
18 float I/O 0 pid.0.Igain
18 float I/O 100 pid.0.Pgain
and ran the following exercise program with FO set to 120%:
G0 X0 Y0 Z0
O100 repeat [100]
G1 X.1 F6
G1 X0
O100 endrepeat
M2
Without the new feedback-deriv pin hooked up, following is terrible and
feedback derivative estimate is all over the place:
http://emergent.unpy.net/files/sandbox/pid-trad.png
(green/cyan trace: cmd, fb; purple trace: feedback-deriv estimated from
feedback position; tan trace: pid error)
With the new feedback-deriv pin hooked up to the velocity estimate,
following is much better thanks to the high-quality velocity estimate
http://emergent.unpy.net/files/sandbox/pid-bettervel.png
(green/cyan trace: cmd, fb; purple trace: feedback-deriv from encoder
based on timestamps; tan trace: pid error; scales all the same as before)
Note that with encoder.0.scale=6000, a 1-count error is 166u, so the
following went from "all over the map" to "+-2 counts".
Both screenshots use the same pid tuning parameters. Only the source of
the feedback derivative (velocity) differs.
The patch is here:
http://emergent.unpy.net/files/sandbox/0001-pid-accept-external-command-deriv-feedback-deriv.patch
Jeff
|
|
From: Jeff E. <je...@un...> - 2010-10-27 00:31:50
|
There are actually two patches required. http://emergent.unpy.net/files/sandbox/0001-pid-read-command-feedback-only-once.patch http://emergent.unpy.net/files/sandbox/0002-pid-accept-external-command-deriv-feedback-deriv.patch pid-read-command-feedback-only-once should be applied first. Jeff |
|
From: Jon E. <el...@pi...> - 2010-10-27 01:30:04
|
Jeff Epler wrote: > It's been mentioned many times, most recently by Jon Elson, that it > would be nice if pid could take a better velocity estimate than the one > it gets by performing a two-point numeric differentiation of the error > term. > > I got around to implementing this and testing it very lightly on > servo-sim. It looks promising! > > > > Without the new feedback-deriv pin hooked up, following is terrible and > feedback derivative estimate is all over the place: > http://emergent.unpy.net/files/sandbox/pid-trad.png > (green/cyan trace: cmd, fb; purple trace: feedback-deriv estimated from > feedback position; tan trace: pid error) > > With the new feedback-deriv pin hooked up to the velocity estimate, > following is much better thanks to the high-quality velocity estimate > http://emergent.unpy.net/files/sandbox/pid-bettervel.png > (green/cyan trace: cmd, fb; purple trace: feedback-deriv from encoder > based on timestamps; tan trace: pid error; scales all the same as before) > > Note that with encoder.0.scale=6000, a 1-count error is 166u, so the > following went from "all over the map" to "+-2 counts". > > Both screenshots use the same pid tuning parameters. Only the source of > the feedback derivative (velocity) differs. > I am a bit confused as to why the following error goes from about .0015" (off scale) to .0002". The change in the feedback-deriv is exactly as expected, and what I was looking for, all those nasty sampling time induced spikes are gone. At the particular velocity Jeff tested this at, the spikes are about equal to the velocity, for a signal-noise ratio of 1:1, which is certainly a BAD situation for a control loop. In the second trace, there is still (I think) some position quantization, which is unavoidable, but it is VASTLY better than without the correction. So, I think this proves that this is something that I should continue on. Thanks Jeff, for resurrecting this variant of PID. I wonder if we could get this committed to the development head? It could either be a different component, or a command-line option to the standard PID component. If I don't make it out to Wichita, I definitely intend to try to put the velocity estimate into the hal_ppmc driver shortly. The UPC firmware has what I believe are the requisite functions. There is a 16-bit timestamp running at 1 MHz, and a 16-bit latch that records the timestamp at each encoder transition. If I can get this working, I will then add the function to the UPC and PPMC boards. Jon |
|
From: Jeff E. <je...@un...> - 2010-10-27 01:47:57
|
> > http://emergent.unpy.net/files/sandbox/pid-trad.png > > http://emergent.unpy.net/files/sandbox/pid-bettervel.png > > (green/cyan trace: cmd, fb; purple trace: feedback-deriv from encoder > > based on timestamps; tan trace: pid error; scales all the same as before) > > > > Note that with encoder.0.scale=6000, a 1-count error is 166u, so the > > following went from "all over the map" to "+-2 counts". > > > > Both screenshots use the same pid tuning parameters. Only the source of > > the feedback derivative (velocity) differs. > > On Tue, Oct 26, 2010 at 08:29:52PM -0500, Jon Elson wrote: > I am a bit confused as to why the following error goes from about > .0015" (off scale) to .0002". I'm not sure why myself. I am nearly certain the only thing I changed between the two captures was the use of the software encoder's velocity output. I suspect that under the traditional pid setup and with a real machine this is the kind of tuning that is simply *so* bad that you'd immediately e-stop it and lower something (namely the Dgain). Why it behaves specifically like you see in the graph, I'd be reluctant to speculate. > The change in the feedback-deriv is exactly as expected, and what I > was looking for, all those nasty sampling time induced spikes are > gone. At the particular velocity Jeff tested this at, the spikes are > about equal to the velocity, for a signal-noise ratio of 1:1, which is > certainly a BAD situation for a control loop. In the second trace, > there is still (I think) some position quantization, which is > unavoidable, but it is VASTLY better than without the correction. > So, I think this proves that this is something that I should continue > on. Indeed. I chose the velocity to be well under 2 counts per servo cycle (but still in the realm where velocity estimation is very good) in the hopes of making the old code look as *BAD* as possible. > I wonder if we could get this committed to the development head? It > could either be a different component, or a command-line option to the > standard PID component. Actually, the modified pid is intended to be entirely compatible with the original pid without any ini/hal changes. The way I accomplish this is a little bit gross (it treads on a hal implementation detail, the "dummysig") but it means less disruption or duplication of code (at_pid, I'm looking at you!). > The UPC firmware has what I believe are the requisite functions. > There is a 16-bit timestamp running at 1 MHz, and a 16-bit latch that > records the timestamp at each encoder transition. If I can get this > working, I will then add the function to the UPC and PPMC boards. Cool! I recall that with hostmot2 it took a number of iterations on the driver side to deal fully with direction reversals and counter overflows in all the corner cases, but the timestamp is the basic building block that makes the high-quality velocity estimae possible. Jeff |
|
From: Jeff E. <je...@un...> - 2010-10-27 14:48:10
|
On Tue, Oct 26, 2010 at 08:29:52PM -0500, Jon Elson wrote: > I wonder if we could get this committed to the development head? This is now availble in the "master" branch for all to enjoy. Jeff |
|
From: Jon E. <el...@pi...> - 2010-10-27 15:54:17
|
Jeff Epler wrote: > On Tue, Oct 26, 2010 at 08:29:52PM -0500, Jon Elson wrote: > >> I wonder if we could get this committed to the development head? >> > > This is now availble in the "master" branch for all to enjoy. > > Thanks, and also thanks for the excellent description of how it works when you committed it. I didn't know a hal component could tell whether a pin was connected or not. Jon |
|
From: Jeff E. <je...@un...> - 2010-10-27 16:10:02
|
On Wed, Oct 27, 2010 at 10:54:07AM -0500, Jon Elson wrote: > Thanks, and also thanks for the excellent description of how it works > when you committed it. I didn't know a hal component could tell > whether a pin was connected or not. It's a dirty trick and I'd rather see it get as little use as possible.. Jeff |
|
From: Jeff E. <je...@un...> - 2010-10-27 16:10:33
|
On Wed, Oct 27, 2010 at 11:09:46AM -0500, Jeff Epler wrote:
> On Wed, Oct 27, 2010 at 10:54:07AM -0500, Jon Elson wrote:
> > Thanks, and also thanks for the excellent description of how it works
> > when you committed it. I didn't know a hal component could tell
> > whether a pin was connected or not.
>
> It's a dirty trick and I'd rather see it get as little use as possible..
("it" being the trick, not the new capabilities of pid!)
Jeff
|
|
From: Andy P. <an...@an...> - 2010-10-27 16:38:30
|
On 27 October 2010 17:09, Jeff Epler <je...@un...> wrote: > It's a dirty trick and I'd rather see it get as little use as possible.. Is there an easy, elegant way to make it less dirty? I can see many uses for it (encoders going automatically into counter-mode, bldc drivers knowing whether to use encoder or hall-sensor feedback spring to mind) -- atp |
|
From: Jeff E. <je...@un...> - 2010-10-30 16:13:49
|
On Wed, Oct 27, 2010 at 05:38:24PM +0100, Andy Pugh wrote: > On 27 October 2010 17:09, Jeff Epler <je...@un...> wrote: > > > It's a dirty trick and I'd rather see it get as little use as possible.. > > Is there an easy, elegant way to make it less dirty? I can see many > uses for it (encoders going automatically into counter-mode, bldc > drivers knowing whether to use encoder or hall-sensor feedback spring > to mind) You're right. Either I shouldn't use the trick here, or other components should feel free to use the trick. Which do we want to do? First, let me summarise what the trick *is*. After hal_pin_new returns, the pin points at the internal "dummysig". The dummysig exists so that it's always permitted to dereference a pin, regardless of whether a pin is connected. When a pin is connected to any signal, the address it points to is some other address than the dummysig. If the dummysig address is stored just after hal_pin_new, this enables: * test in constant time and from a RT thread whether the pin is connected to any signal * generate a value internally onto the dummysig that is later read as the pin's value when the pin is not connected to any signal There are some reasons to *not* use this trick in hal components: * In the future we might see a way to change the design in a way that makes the trick unusable (for instance, something that doesn't require the dummysig at all) * The hal lock is held during the time between hal_pin_new and the copy of the dummysig address. Instead, the assumption that makes this safe is that the only possible pin-linker is the same halcmd that is waiting for this component to be ready. We might want to violate this assumption sometime in the future (and you can probably violate it now just by running two halcmds at the same time) * The trick depends on whether a signal is connected, not whether a signal with a writer is connected. So for instance, connecting a signal and then scoping the signal will modify the component's behavior. (thanks to SWP for identifying this problem) What other considerations (uses or problems) are there than the ones I've identified? Shall we decide to use this trick freely, or to use it not at all? If we decide to use it freely, should we create some kind of API to make its use more explicit? Jeff |
|
From: Jon E. <el...@pi...> - 2010-10-30 05:33:01
|
Jeff Epler wrote: > On Tue, Oct 26, 2010 at 08:29:52PM -0500, Jon Elson wrote: > >> I wonder if we could get this committed to the development head? >> > > This is now availble in the "master" branch for all to enjoy. > I've been working on putting the velocity estimation into my hal_ppmc.c driver. Does anyone have any suggestions for debugging a HAL driver module? I now get : Default Trap Handler: vector 14: Suspend RT task dfab4800 PPMC: shutting down And by turning on all the print statements, I see the initialization seems to be getting done, so it is something happening when the driver functions get control, I guess. I'll keep looking at the code, it isn't a very large addition of code. Thanks, Jon |
|
From: Chris R. <ch...@ti...> - 2010-10-30 14:49:02
|
On Sat, Oct 30, 2010 at 12:32:50AM -0500, Jon Elson wrote: > Jeff Epler wrote: > > On Tue, Oct 26, 2010 at 08:29:52PM -0500, Jon Elson wrote: > > > >> I wonder if we could get this committed to the development head? > >> > > > > This is now availble in the "master" branch for all to enjoy. > > > I've been working on putting the velocity estimation into my hal_ppmc.c > driver. > Does anyone have any suggestions for debugging a HAL driver module? I now > get : > Default Trap Handler: vector 14: Suspend RT task dfab4800 > PPMC: shutting down Google gave me some hints: http://mail.rtai.org/pipermail/rtai/2003-January/002047.html However I think you might find it faster by staring at the code you've added. Look closely wherever you dereference a pointer to make sure that the pointer points where you think. Remember that array[...] is also a pointer dereference. > I'll keep looking at the code, it isn't a very large addition of code. Pastebin a git diff for us if you don't spot it. Chris |
|
From: Andy P. <an...@an...> - 2010-10-30 15:02:25
|
On 30 October 2010 15:48, Chris Radek <ch...@ti...> wrote: >> Default Trap Handler: vector 14: Suspend RT task dfab4800 A *pointer in the headers that is used without a kmalloc, hal_malloc or the ppmc equivalent of tram_register? DAMHIK -- atp |
|
From: Jon E. <el...@pi...> - 2010-10-30 16:15:41
|
Chris Radek wrote: > However I think you might find it faster by staring at the code > you've added. Look closely wherever you dereference a pointer to > make sure that the pointer points where you think. Remember that > array[...] is also a pointer dereference. > I commented out all changed sections, and then put them back in one at a time. I finally figured out I had a mistake in the nesting of curly braces, and had a section with an array outside the loop, so it got an n+1 array subscript, which was probably the cause. Still working on it, but at least making some progress! Thanks, Jon |
|
From: Jon E. <el...@pi...> - 2010-10-30 17:08:55
|
Well, before I can do anything with velocity estimation, I need to just
get the
scaled velocity pin exported. I added a couple more elements to the encoder
structure :
hal_float_t *vel; /* output: scaled velocity */
hal_float_t min_speed; /* parameter: min speed for velocity
estimation */
and exported the pin to HAL :
retval = hal_pin_float_newf(HAL_OUT, &(slot->encoder[n].vel), comp_id,
"ppmc.%d.encoder.%02d.vel",bus->busnum,bus->last_encoder);
if (retval != 0) {
return retval;
}
and then wrote a value to it :
*(slot->encoder[i].delta) = pos.l - slot->encoder[i].oldreading;
*(slot->encoder[i].vel) = (pos.l - slot->encoder[i].oldreading) /
(slot->encoder[i].scale * read_period);
The first line is old code and writes out the velocity in raw encoder
counts, the second line is the new one
that writes out the velocity scaled by both linear scaling factor and
sampling period.
I see the pin and can watch it with Halscope, but the vel pin always has
a value of zero, even though the
line above produces a correct unscaled delta value.
Any ideas, have I missed something to get the value exported properly to
the HAL pin?
Jon
|
|
From: Jon E. <el...@pi...> - 2010-10-30 17:16:38
|
OOps, sorry, I found it. I had disabled the if-true part of code, but the else was still getting executed and zeroing out the value! Jon |
|
From: Jon E. <el...@pi...> - 2010-11-01 05:13:54
|
Well, after an AMAZING amount of fooling around with barely one page of code, I finally have MOST of the velocity estimation working in the ppmc driver. I'm still not sure it is right, though. One quirk, which I think is inherent in the way that HAL component encoder.c works, and which I copied, is that a dithering encoder gets the wrong velocity. The first count of the dither gets the right velocity, as there was no movement for some time before. The second count, if immediately following the first one, cancels out the movement, but it gets twice the velocity, as these are seen as two counts in a short period of time. I suppose you could look back two servo periods, and if the position delta between now and n-2 is zero, then velocity should be zero in that case. Maybe I implemented the code wrong. Under some conditions it seems like it might be an improvement, but it is not as smooth as I had hoped for. Now, maybe the problem is the servo I am using for tuning is so jittery (as it does not yet use the velocity info) that it creates a lot of these dithery counts. Jon |
|
From: Andy P. <an...@an...> - 2010-11-01 10:30:14
|
On 1 November 2010 05:13, Jon Elson <el...@pi...> wrote: > One quirk, which I think is inherent in the way that HAL component > encoder.c works, > and which I copied, > is that a dithering encoder gets the wrong velocity. The first count of > the dither gets > the right velocity, as there was no movement for some time before. The > second count, > if immediately following the first one, cancels out the movement, but it > gets twice the > velocity, This sounds like a problem related to the one I am having with my resolver converter, and one possible solution would be a tracking filter (as used by PCW on his resolver converter, apparently). I have not started on it yet, but I think the concept is to model the output like a physical system, with a position, virtual velocity and virtual acceleration, and to have quite heavy filtering on the acceleration. This is vaguely related to an algorithm I have used to extract velocity data from noisy position data in the past: i) create a least-squares fit polynomial to a patch of the data- x = a + bt + ct^2 + d t^3 .... ii) calculate the derivative explicitly for the current value of t: V = b + 2ct + 3d^2 ..... This might be a bit computationally expensive for a real-time application though. It has the advantage over simpler smoothing algorithms of not losing detail. -- atp |
|
From: Jon E. <el...@pi...> - 2010-11-01 16:09:31
|
Andy Pugh wrote: > This sounds like a problem related to the one I am having with my > resolver converter, and one possible solution would be a tracking > filter ( There was a chip made by ST, the L290, a quadrature to voltage converter. It decoded the quadrature into + and - moves, and then had two freq-voltage converters, one for each direction. It then took the difference of the two outputs. Dithering would cause that chip to output a near-zero result. I think I could come up with a similar scheme for the velocity estimate. The current scheme uses a velocity that is derived from time between pulses, and is therefore signless. But, I think I can use the position delta to assign a count-by-count sign to the velocity from the timestamps. I'll have to work this out on paper first. Jon |
|
From: Chris R. <ch...@ti...> - 2010-11-01 16:29:37
|
On Mon, Nov 01, 2010 at 12:13:36AM -0500, Jon Elson wrote: > One quirk, which I think is inherent in the way that HAL component > encoder.c works, > and which I copied, > is that a dithering encoder gets the wrong velocity. When Seb K was working on the hostmot2 driver, one of the kinds of wrongness we found (and he fixed) was http://timeguy.com/cradek-files/emc/encoder-velocity.png This plot was from a real machine with minor dithering at very low speed. I do not think the software encoder component is quite right near zero speed. I think Seb started with that code and improved it to get the good results we have from hostmot2 now. You might want to browse the git history to see what he did? The changes 84e73cbd534 and especially fed9d8e5a58 look relevant. Chris |
|
From: Jon E. <el...@pi...> - 2010-11-02 01:54:10
|
Chris Radek wrote: > On Mon, Nov 01, 2010 at 12:13:36AM -0500, Jon Elson wrote: > > >> One quirk, which I think is inherent in the way that HAL component >> encoder.c works, >> and which I copied, >> is that a dithering encoder gets the wrong velocity. >> > > When Seb K was working on the hostmot2 driver, one of the kinds of > wrongness we found (and he fixed) was > > http://timeguy.com/cradek-files/emc/encoder-velocity.png > > This plot was from a real machine with minor dithering at very low > speed. I do not think the software encoder component is quite right > near zero speed. Yup, I believe you are right. The plot certainly shows a SERIOUS defect! At least with the parameters I'm using for my testing, it doesn't look anywhere that bad, the velocity spikes are only about 2X the nominal speed if there was no dithering. > I think Seb started with that code and improved it > to get the good results we have from hostmot2 now. You might want to > browse the git history to see what he did? > > The changes 84e73cbd534 and especially fed9d8e5a58 look relevant. > I looked briefly at hostmot2/encoder.c but couldn't even tell where the timestamp/velocity code was. Maybe that was the wrong file. Thanks, Jon |
|
From: Sebastian K. <se...@hi...> - 2010-11-02 02:48:13
|
On 11/01/2010 09:54 PM, Jon Elson wrote: > Chris Radek wrote: >> I think Seb started with that code and improved it >> to get the good results we have from hostmot2 now. You might want to >> browse the git history to see what he did? >> >> The changes 84e73cbd534 and especially fed9d8e5a58 look relevant. >> >> > I looked briefly at hostmot2/encoder.c but couldn't even tell where the > timestamp/velocity > code was. Maybe that was the wrong file. > That's the right file. It's in the hm2_encoder_instance_process_tram_read() function. That function gets run for each encoder channel, every time through the servo loop. The comment on line 832 introduces the fix to the "velocity spike when reversing direction" problem. The solution that Peter suggested, that I implemented, is to report velocity=0 for the edge that signals a direction reversal. The hard part of writing that code was figuring out how to do it while handling timestamp roll-over correctly. I made this little chart to help me think about it: <http://highlab.com/~seb/emc2/hm2-encoder/> I think it's still mostly up-to-date. Maybe I should add it to src/hal/drivers/mesa-hostmot2/doc? It adds up to just 88 KB, but i'm not sure if it makes any sense to anyone but me.... (Hm, the hm2 encoder code has an old FIXME from back in February, for a bug Micges reported that I was never able to reproduce... Probably time to shrug and remove it.) -- Sebastian Kuzminsky |
|
From: Steve S. <ste...@ne...> - 2010-11-02 17:08:16
|
OK all you BeagleBoard fans, here is your next "got to have" platform. More power, more I/O, and the expansion connectors even seem to offer some I/O with the bits in consecutive order. http://www.pandaboard.org/ Cheers, Steve Stallings |
|
From: Jeff E. <je...@un...> - 2010-11-02 18:37:05
|
The document looks useful to me. please feel free to add it on master. Jeff |
|
From: Jon E. <el...@pi...> - 2010-11-03 04:07:39
|
Sebastian Kuzminsky wrote: > That's the right file. It's in the > hm2_encoder_instance_process_tram_read() function. That function gets > run for each encoder channel, every time through the servo loop. The > comment on line 832 introduces the fix to the "velocity spike when > reversing direction" problem. > > The solution that Peter suggested, that I implemented, is to report > velocity=0 for the edge that signals a direction reversal. > It took me a couple tries the get this right. My first cut was to compare the last delta to the current move, if the signs were opposite, that was a reversal. But, of course, that's not what you said, as it fails to notice the condition of move +, no move, move -. So, I had to record the sign of the last non-zero move. Thanks for the hint! And, thanks Jeff, for the additions to PID. I connected the velocity out of my new ppmc encoder version to the feedback-deriv input of the new PID, and it seems to be working! The X looks SO GOOD that at first I thought the pid error output was broken! I had to blow it up tremendously to see the remaining error. But, that axis has the really good Pittman brushless motor on it. I may have cooked my Y axis motor or the servo amp running spirals or something at the CNC Workshop, it is acting a bit sluggish. But, I observed I could turn up the D term WAY higher than before without getting those nasty grinding noises. So, I really think this is a significant improvement, especially for drives without any other form of velocity control. It is probably much less important with real velocity servo amps, for instance. Jon |