Finally got some code I'm not too ashamed of. The attached program is for static positioning of the servo to any rotational angle between 0 and 999. It will always pick the shortest path, regardless of rotation direction. So if you have something attached to the servo with a cable that's connected to a base, take care with your calls to go_to to make sure the cable doesn't get wrapped around the shaft.
The program is written so that more than one cog can be started to operate more than one motor. Once I get things tucked into PASM, one cog should be able to handle several motors. But I've got a few more things to prove out in Spin first.
(Yeah, I know it's PICAXE, but that's not far off from PBASIC, and folks can translate.)
On "360 Degree Feedback": It's a fairly well-known management concept, so there's bound to be ambiguities in the term. In fact, Parallax might want to think of a cool sounding name for the product class to differentiate it, so typing in the phrase "360 degree feedback" won't bring up a lot of useless (to us) hits. I'll let their trademark gurus at it. At the moment, I'm leaning towards "the What-Goes-Around-Comes-Around Doohickey."
Sure, this was today's quickie experiment for my Roboto column. I'll convert this Picaxe BASIC to PBASIC soon too, just need to adjust the timing differences. Note that this simple first pass does not yet handle encoder high/low transistion.
#picaxe 08m2
#no_data
pause 500
setfreq m8 ' use m8 for better pulsin resolution, must use PULSOUT since no M8 for SERVO
'FORUM EDIT
'RIGHT WHEEL MASTER, LEFT WHEEL SLAVE
'c.1 left servo output
'c.2 right servo output
'c.3 left encoder input
'c.4 right encoder input
'Left encoder increases clockwise (reverse) 6-216
'Left servo pulses 320forward 305deadslowforward 300stop 292deadslowreverse 280reverse
do
pulsin 4,1,b1 ' read right encoder 6-216 pulsin decreases CCW (reverse right)
b0=222-b1 ' swap encoder values L/R to match
pulsin 3,1,b1 '20-120 read left encoder pulsin decreases CCW (forward left)
if b1>b0 then CCW ' if encoder value>goal then CCW
CW:
b2=b0-b1' error
w3=250
if b2<12 then let w3=260:endif
if b2<5 then let w3=293:endif
if b2<1 then let w3=300:endif
goto sendpulse
CCW:
b2=b1-b0' error
w3=350
if b2<12 then let w3=330:endif
if b2<5 then let w3=307:endif
if b2<1 then let w3=300:endif
sendpulse:pulsout 1,w3 ' send pulse to left servo
loop
'Finally got around to more testing. I wanted to measure the servo's static (holding) torque at various pulse widths. To do that I constructed a test stand and put a lever arm that presses down on a precision scale:
The lever arm is five inches from the output shaft axis to the little tip that presses on the scale, which I set up to measure in ounces. So anything the scale displays has to be multiplied by five to get the torque in ounce-inches. I jumpered the servo pins for Vin, but supplied Vin with a five-volt supply. (It's the only supply I had handy that could keep a steady voltage across different load conditions.) I measured the force on the scale at several speed offsets from 1500 usec. Here's a graph that shows the results:
25 oz.-in. isn't too shabby for such a small motor -- especially one that's geared for high speed! And with a higher Vin, one should expect to get even higher values.
To measure dynamic torque at various speeds, one would need a dynamometer. I don't have one, nor a clue how I would make one.
VERY rough, but this video shows what I've been up to, recording and playing back a path on a modified Boebot. It's far from perfect, I just went for my usual minimalist approach with a tiny Picaxe processor and the shortest code possible. The servo has some quirks, there's a big deadband at 1.5ms (making the speed response nonlinear), and it's very particular about control pulse timing, so the servos kinda chug along here. There are some fudge factors to tweak, the playback path is longer than the recorded path. Plenty of room for improvement, but I wanted to get this rough demo video in the can for my Mr. Roboto December column, which is overdue. Must write now!
I'll have to clean up and comment my Picaxe code before sharing, but it actually does work as shown. Hopefully a BS2 version will work as well or better.
VERY rough, but this video shows what I've been up to, recording and playing back a path on a modified Boebot. It's far from perfect, I just went for my usual minimalist approach with a tiny Picaxe processor and the shortest code possible. The servo has some quirks, there's a big deadband at 1.5ms (making the speed response nonlinear), and it's very particular about control pulse timing, so the servos kinda chug along here. There are some fudge factors to tweak, the playback path is longer than the recorded path. Plenty of room for improvement, but I wanted to get this rough demo video in the can for my Mr. Roboto December column, which is overdue. Must write now!
I'll have to clean up and comment my Picaxe code before sharing, but it actually does work as shown. Hopefully a BS2 version will work as well or better.
Doing this with a closed-loop feedback control loop? If not, I'd expect results similar to the high-speed servo without encoders. Either way, I'm thrilled you're talking about it in Mr. Roboto!
Full feedback, recording 50 pairs of position data points as I push it along.
BTW Ken, I'm writing the article now. How many total Boebots sold? You mention half a million students have used it here:
That's a pretty funny video. Who's the ding-dong, anyway?
As for the number of Boe-Bots sold, we're somewhere over a million units - for certain if we add in ActivityBots and ShieldBots and count the number of robot chassis we've produced since 1997.
I think I have finally figured out the zero-crossing issue. Between the inertia, the backlash, and the discontinuity at 359/0, it is *interesting*.
The attached file provides control of up to 4 feedback servos in a single PASM cog. There are five methods:
FBS.start(CP0, FP0, CP1, FP1, CP2, FP2, CP3, FP3) starts the PASM cog and assigns pin numbers. If any servo is not present, both the CPx and FPx must be specified as zeros (for the missing servo(s)).
FBS.SetPos(servo, degrees) causes the indicated servo to drive toward the indicated position. It does not take the shortest path; it does not (permanently) cross zero. This allows arbitrary positioning without cable wrap issues.
FBS.Width(servo, Usec) causes the indicated servo to be issued pulses of the indicated width until a new command is issued. The width is specified in microseconds.
FBS.GetPos(servo) returns the current position in degrees.
FBS.CheckDone(servo) returns zero if the previous SetPos is complete, not zero if it is not complete.
The PASM program divides the 20 msec heartbeat into four 5-msec intervals (one for each servo). For each interval, it adjusts the pointers to hub memory to point to the parameters for this servo. It then determines if a servo is present. If not, it merely waits out the internal.
If a servo is present, it finds the current theta. If we are not seeking (that is if we last saw a FBS.width command), it sets up the pulse width. If we are seeking, it determines if it has crossed the 359/0 discontinuity during the last heartbeat. It calculates a pulse width based on which direction to go, whether we are on the wrong side of zero, and how big is the error, according to some gain rules. It waits out the interval and then issues a pulse to the appropriate control pin using counter A.
I still want to provide logic the anticipate overshoot, but thought this might be interesting.
I left in a ton of instrumentation for the time being.
the playback path is longer than the recorded path
How are you handling this? One way to do teaching pendants is to store the amount of change every nth of a second, and you calculate the delta between data points to play back. Another is to store timestamps at each signal change. I'm guessing you're not using the latter, because there are no discrete signals to react to. (There are *differences* in the PWM from the Hall effect, but those need to be polled at regular intervals anyway, so I'm not sure you get any benefit.)
The playback time is about three seconds longer than the record time, which means the covered path will be lengthened.
As for the number of Boe-Bots sold, we're somewhere over a million units - for certain if we add in ActivityBots and ShieldBots and count the number of robot chassis we've produced since 1997.
Well 2017 is pretty much a wrap, maybe that's when you made my Grow-Bot...
Now THAT'S worth celebrating. Hopefully you're still planning to have another event in 2018? Please firm up your plans so people can manage their vacations accordingly, I wanna see all my Forumista friends in Rocklin next year!
Hopefully you're still planning to have another event in 2018? Please firm up your plans so people can manage their vacations accordingly, I wanna see all my Forumista friends in Rocklin next year!
If it's after the Siskiyous thaw and before the valley gets unbearably hot, I might be there.
I'm looking at both encoders, and recording a datapoint when either one hits 20 encoder ticks (adjustable). Pretty sure the recording routine is solid. Playback is where the issues are, there's a whole lot going on. I made several simplifying assumptions. That big deadband at 1.5 ms messes up the linear speed response, for one. I'll get back to the software, I just needed to slam something on Youtube showing record/playback for my December column, which is already late! I can link to improvements in the copy.
As Phil noted, this servo is extra sensitive to control pulses. You can't just throw a PAUSE 20 in between pulsouts or bad stuff happens. I'm getting some servo stuttering, possibly because I never send the same pulsout twice. Currently, each loop I look at the remaining distance & error and calculate a new pulsout to adjust. Dollars to doughnuts the servo "doesn't like that".
I just needed to slam something on Youtube showing record/playback for my December column, which is already late!
Uh huh. That's what happens when you submit a great article to a magazine. The editor comes back with unctuous flattery: "Hey, you write reeeal good! We need someone with your amazing talent to do a monthly column. The pay isn't that great, but you'll be a freaking rock star to our readers! How 'bout it?"
If you succumb, you enter the world of deadline hell. Comes the looming deadline for the next issue, and you think, "Geez! It seems like I just finished writing the last column. I need to come up with a project quick!"
The topic "Position detection" in the feedback servo blurb (page 5 of 7) begins with a note about being careful when crossing the 359/0 discontinuity.
I have attached two logic analyzer grabs that show what happens in some detail. The feedback signal is the second trace labeled s2.
In 0to359 we see a crossing from zero degrees to 359 degrees. The very long low period for 0 is followed by a very long high period for 359.
If the program measured the short high pulse and misses the very long low pulse but instead gets the following very short low pulse, it gets the wrong answer. Also if it measures the low pulse first, it could get two adjacent very long pulses.
But you are guaranteed to get valid data if you can 1) measure adjacent pulses and 2) measure the high pulse first. The following PASM code does just that:
waitpne FBPinMask, FBPinMask 'get pulse width: wait for feedback low
waitpeq FBPinMask, FBPinMask 'then high
mov work1, cnt 'beginning of high feedback pulse
waitpne FBPinMask, FBPinMask 'wait for end of high pulse
mov HiPer, cnt
waitpeq FBPinMask, FBPinMask 'now wait for end of low pulse
mov LowPer, cnt
' andn outa, p3
sub LowPer, HiPer 'calculate low period
sub HiPer, work1 'and high period
mov y, HiPer 'calculate total period
add y, LowPer 'total period in tics
In 359to0 we see a crossing from 359 degrees to 0 degrees. The very short low period for 359 is followed by a very short high period for 0.
If the program misses pulses or measures the low pulse first, it could get the wrong answer. But the code above always works.
Now this is in PASM. I am as sure as of anything that you cannot make those guarantees in any BS-2, so you have to test for very long or very short total period. Likely spin is too slow as well. I do not know about C (the blurb says test) or other machine compiled code.
If you succumb, you enter the world of deadline hell. Comes the looming deadline for the next issue, and you think, "Geez! It seems like I just finished writing the last column. I need to come up with a project quick!"
'Been there. Never again.
-Phil
Exactly! JonnyMac also warned me how quickly time flies between article deadlines. It never stops.
Comments
The program is written so that more than one cog can be started to operate more than one motor. Once I get things tucked into PASM, one cog should be able to handle several motors. But I've got a few more things to prove out in Spin first.
-Phil
Or is that non-PC terminology?
Code or it never happened.
(Yeah, I know it's PICAXE, but that's not far off from PBASIC, and folks can translate.)
On "360 Degree Feedback": It's a fairly well-known management concept, so there's bound to be ambiguities in the term. In fact, Parallax might want to think of a cool sounding name for the product class to differentiate it, so typing in the phrase "360 degree feedback" won't bring up a lot of useless (to us) hits. I'll let their trademark gurus at it. At the moment, I'm leaning towards "the What-Goes-Around-Comes-Around Doohickey."
Thanks,
-Phil
Sure, this was today's quickie experiment for my Roboto column. I'll convert this Picaxe BASIC to PBASIC soon too, just need to adjust the timing differences. Note that this simple first pass does not yet handle encoder high/low transistion.
Code attached as a text file.
The 20 msec period is broken into four chunks, one for each servo. I go through a 5 msec loop four times using context for one of four servos.
If Parallax was to "comp" me with 2 more servos and a couple of wheels I could test with four (hint hint)
Edit: I'll put the correct file in the next comment
We've all done it. Usually the answer hits me a quarter-second after I click POST to share my stupid question with the world.
The lever arm is five inches from the output shaft axis to the little tip that presses on the scale, which I set up to measure in ounces. So anything the scale displays has to be multiplied by five to get the torque in ounce-inches. I jumpered the servo pins for Vin, but supplied Vin with a five-volt supply. (It's the only supply I had handy that could keep a steady voltage across different load conditions.) I measured the force on the scale at several speed offsets from 1500 usec. Here's a graph that shows the results:
25 oz.-in. isn't too shabby for such a small motor -- especially one that's geared for high speed! And with a higher Vin, one should expect to get even higher values.
To measure dynamic torque at various speeds, one would need a dynamometer. I don't have one, nor a clue how I would make one.
-Phil
Such a rigorous scientific approach.
All done in ounces and inches!
-Phil
I'll have to clean up and comment my Picaxe code before sharing, but it actually does work as shown. Hopefully a BS2 version will work as well or better.
nicely done,
Mike
Doing this with a closed-loop feedback control loop? If not, I'd expect results similar to the high-speed servo without encoders. Either way, I'm thrilled you're talking about it in Mr. Roboto!
Ken Gracey
BTW Ken, I'm writing the article now. How many total Boebots sold? You mention half a million students have used it here:
That's a pretty funny video. Who's the ding-dong, anyway?
As for the number of Boe-Bots sold, we're somewhere over a million units - for certain if we add in ActivityBots and ShieldBots and count the number of robot chassis we've produced since 1997.
Nice work on the encoder feedback setup, too!
Ken Gracey
The attached file provides control of up to 4 feedback servos in a single PASM cog. There are five methods:
FBS.start(CP0, FP0, CP1, FP1, CP2, FP2, CP3, FP3) starts the PASM cog and assigns pin numbers. If any servo is not present, both the CPx and FPx must be specified as zeros (for the missing servo(s)).
FBS.SetPos(servo, degrees) causes the indicated servo to drive toward the indicated position. It does not take the shortest path; it does not (permanently) cross zero. This allows arbitrary positioning without cable wrap issues.
FBS.Width(servo, Usec) causes the indicated servo to be issued pulses of the indicated width until a new command is issued. The width is specified in microseconds.
FBS.GetPos(servo) returns the current position in degrees.
FBS.CheckDone(servo) returns zero if the previous SetPos is complete, not zero if it is not complete.
The PASM program divides the 20 msec heartbeat into four 5-msec intervals (one for each servo). For each interval, it adjusts the pointers to hub memory to point to the parameters for this servo. It then determines if a servo is present. If not, it merely waits out the internal.
If a servo is present, it finds the current theta. If we are not seeking (that is if we last saw a FBS.width command), it sets up the pulse width. If we are seeking, it determines if it has crossed the 359/0 discontinuity during the last heartbeat. It calculates a pulse width based on which direction to go, whether we are on the wrong side of zero, and how big is the error, according to some gain rules. It waits out the interval and then issues a pulse to the appropriate control pin using counter A.
I still want to provide logic the anticipate overshoot, but thought this might be interesting.
I left in a ton of instrumentation for the time being.
How are you handling this? One way to do teaching pendants is to store the amount of change every nth of a second, and you calculate the delta between data points to play back. Another is to store timestamps at each signal change. I'm guessing you're not using the latter, because there are no discrete signals to react to. (There are *differences* in the PWM from the Hall effect, but those need to be polled at regular intervals anyway, so I'm not sure you get any benefit.)
The playback time is about three seconds longer than the record time, which means the covered path will be lengthened.
Or are you using some other technique?
Well 2017 is pretty much a wrap, maybe that's when you made my Grow-Bot...
But per https://en.wikipedia.org/wiki/Boe-Bot, 1998 was the debut of the BoeBot, so 2018 is the 20th anniversary of the world's most popular robot!
Now THAT'S worth celebrating. Hopefully you're still planning to have another event in 2018? Please firm up your plans so people can manage their vacations accordingly, I wanna see all my Forumista friends in Rocklin next year!
-Phil
If you need me to build you a flamethrower to speed up that thaw, just say the word.
I'm looking at both encoders, and recording a datapoint when either one hits 20 encoder ticks (adjustable). Pretty sure the recording routine is solid. Playback is where the issues are, there's a whole lot going on. I made several simplifying assumptions. That big deadband at 1.5 ms messes up the linear speed response, for one. I'll get back to the software, I just needed to slam something on Youtube showing record/playback for my December column, which is already late! I can link to improvements in the copy.
As Phil noted, this servo is extra sensitive to control pulses. You can't just throw a PAUSE 20 in between pulsouts or bad stuff happens. I'm getting some servo stuttering, possibly because I never send the same pulsout twice. Currently, each loop I look at the remaining distance & error and calculate a new pulsout to adjust. Dollars to doughnuts the servo "doesn't like that".
'Course, that isn't an issue with PASM. Hee, hee.
If you succumb, you enter the world of deadline hell. Comes the looming deadline for the next issue, and you think, "Geez! It seems like I just finished writing the last column. I need to come up with a project quick!"
'Been there. Never again.
-Phil
I have attached two logic analyzer grabs that show what happens in some detail. The feedback signal is the second trace labeled s2.
In 0to359 we see a crossing from zero degrees to 359 degrees. The very long low period for 0 is followed by a very long high period for 359.
If the program measured the short high pulse and misses the very long low pulse but instead gets the following very short low pulse, it gets the wrong answer. Also if it measures the low pulse first, it could get two adjacent very long pulses.
But you are guaranteed to get valid data if you can 1) measure adjacent pulses and 2) measure the high pulse first. The following PASM code does just that:
In 359to0 we see a crossing from 359 degrees to 0 degrees. The very short low period for 359 is followed by a very short high period for 0.
If the program misses pulses or measures the low pulse first, it could get the wrong answer. But the code above always works.
Now this is in PASM. I am as sure as of anything that you cannot make those guarantees in any BS-2, so you have to test for very long or very short total period. Likely spin is too slow as well. I do not know about C (the blurb says test) or other machine compiled code.
Exactly! JonnyMac also warned me how quickly time flies between article deadlines. It never stops.