|
Servo Programming - 2 (With Ramping) |
|
<= Back to Co-Processor page Let's consider something other than a wheeled robot for a bit. Consider a 2 legged (Biped) robot. The simplest versions are like the BigFoot from Milford Instruments Ltd. and the new Toddler from Parallax. You can see BigFoot on Milford's website or download the (Manual (14 pp - PDF - 293KB) . Toddler is covered on the Parallax' website and the manual is the Advanced Robotics Text (152 pp - PDF - 2.0 MB). These robots consist of a body, two legs, two servos, and 2 big feet. One servo moves the legs forward and backward and the other swings the body from side to side to put all the weight onto each foot in turn for walking. Both of the servos have to be ramped from position to position. Ramping controls how fast the servo moves in response to a command to go to a different position. Without a way to ramp the servos they would try to go from one position to the other at full speed. Servos typically travel their total range in about 0.2 seconds. You can imagine what would happen if you tried to lean the robot from all its weight on one foot to all the weight on the other that quickly. It is way too fast for any reasonable size robot. Either the robot could fall over or the servo might be damaged. If you are curious about the overall Toddler walker program, see the Parallax Advanced Robotics Text that is hot-linked above. Excellent code descriptions and alternatives are given in the there. For simplicity, we'll just look at the servo drive section. To slow the servo movement on a BS2 based robot without a co-processor, something like the following subroutine is executed. |
| MoveTiltServo: | |||
| FOR Pulses = Tiltleft TO TiltRight STEP TiltStep | |||
| PULSOUT TiltServo, Pulses | 'each period gets a new value | ||
| PULSOUT StrideServo, StrideLeft | 'you have to keep giving the | ||
| ' stride-servo pulses even | |||
| ' though it doesn't move | |||
| PAUSE 17 | 'finish out 20 ms for servo - processor stopped | ||
| NEXT | 'loop back | ||
| RETURN | |||
|
Where you come into the routine tilted left and finish tilted right. TiltStep is used to determine how fast the transition happens. The stride servo stays at the same position during this whole move. Likewise, the stride servo needs a similar routine. |
| MoveStrideServo: | |||
| FOR Pulses = Strideleft TO StrideRight STEP StrideStep | |||
| PULSOUT TiltServo,Tiltleft | 'you have to keep giving the | ||
| ' tilt-servo pulses even | |||
| ' though it doesn't move | |||
| PULSOUT StrideServo, Pulses | 'each period gets a new value | ||
| PAUSE 17 | 'finish out 20 ms for servo - processor stopped | ||
| NEXT | 'loop back | ||
| RETURN | |||
|
Here the tilt servo stays at the same position while the stride servo moves slowly. By now you must know I'm going to mention about the fact that the Stamp is stopped during the PULSOUT and PAUSE instructions. Obviously the servo controller can easily take care of the servo that isn't changing its position. But what about the other one? Without ramping, you would have to send new pulse lengths to at least one of the servos every 20 ms. The Stamp would have to keep computing all the pulse values and use 100 percent of its power while doing it. That won't do! The solution is a "ramping" servo controller. This controller goes from where it was to where you tell it to be at a controlled rate. It is called ramping because, if you plot the position versus time, you get a sloped line that looks like a ramp. How difficult is it to control this ramping stuff? - Not very! Our servo controller normally breaks the 1 millisecond to 2 millisecond pulse widths into 256 steps. The ramp step is the amount added to the position value of the servo for each 20 ms cycle. The ramp resolution is 1/4 bit per step. The servo actually steps one whole bit value at a time but the fractions are remembered in the Co-Processor to allow really slow ramps (up to 20 seconds!). See the chart for the ramp-value (0-31) versus ramping times With eight servo channels, it is unlikely that all channels would need ramped at the same value so each servo controller channel has it's own ramping value. If the ramp value is zero, no ramping occurs. No ramping means the servo goes from position to position as fast as it is able. A Co-Processor reset sets all the ramp values to 0 (or no ramping). That is why we didn't have remember to set it in the previous (non-ramping) examples. Ramp rates don't affect holding your position so you don't have to change them when the servo isn't moving. Ramp rates can be constants and the programming is really simple. For grins, let's use a variable for the ramp rate in the code below. The differences from the code on the first servo programming page are bolded.You can change ramp rates any time - not just at initialization time. Now let's see some code. |
| ServVal_0 | VAR BYTE | 'to load servo 0 delays |
| ServVal_0_Old | VAR BYTE | 'remember old servo 0 delays |
| ServVal_1 | VAR BYTE | 'to load servo 1 delays |
| ServVal_1_Old | VAR BYTE | 'remember old servo 1 delays |
| ramp_0 | VAR BYTE | 'servo 0 ramp rate |
| ramp_1 | VAR BYTE | 'servo 1 ramp rate |
| Initialize the program | |||
| ServVal_0 = 127 | |||
| ServVal_0_Old = 0 | |||
| ServVal_1 = 127 | |||
| ServVal_1_Old = 0 | |||
| ramp_0 = 24 | '6 counts/20ms | ||
| ramp_1 = 24 | '6 counts/20ms | ||
| AUXIO | '2p40 only | ||
| SEROUT 10, 1021, [116] | |||
| SEROUT 10, 1021, [116] | 'Reset Co-Processor | ||
| 'Reset turns servos off | |||
| ' and turns ramping off | |||
| SEROUT 10, 1021, [144,ramp_0] | 'write ramp rate to servo 0 | ||
| SEROUT 10, 1021, [145,ramp_1] | 'write ramp rate to servo 1 | ||
| 'Ramp rate of x will incr or decr | |||
| ' pulsewidth by x/4 per 20 ms | |||
| ' up to a max of x = 31 | |||
| 'write ramp command = 144 + ch# | |||
| Main: | |||
| GOSUB action1 | 'do the random wander about | ||
| ' subroutine (not shown) | |||
| GOSUB action2 | 'do the IRPD subroutine (not shown) | ||
| GOSUB action3 | 'do the bumper subroutine (not shown) | ||
| GOSUB action4 | 'figure out servo values from results of | ||
| ' previous subroutines | |||
| IF (ServVal_0 = ServVal_0_Old) THEN DoServ1 | |||
| 'Only need to send servo data | |||
| ' if different from last time. | |||
| SEROUT 10, 1021, [136,ServVal_0] | 'Set Servo 0 = Left side | ||
| ServVal_0_Old = ServVal_0 | 'remember value sent | ||
| DoServ1: | |||
| IF (ServVal_1 = ServVal_1_Old) THEN DoneServ | |||
| 'Only need to send servo data | |||
| ' if different from last time. | |||
| SEROUT 10, 1021, [137,ServVal_1] | 'Set Servo 1 = Right side | ||
| ServVal_1_Old = ServVal_1 | 'remember value sent | ||
| DoneServ: | |||
| GOTO Main | 'keep looping doing this stuff | ||
|
On the other page this code was for a wheeled robot. Here I'm claiming it is for a biped walker. Actually it is good for both. The benefits for ramping were explained for a walker. If you use it on your wheeled robot, the servo motors don't try to go from full forward to full reverse instantly. This is a much smoother motion which is a lot easier on the servos. The primary code changes would show up in the action4 subroutine. The translation for a desired direction for wheeled robots and walkers is quite different. One advantage of the pause code is that you automatically know when the movement is done. Of course, that is because you were stopped and waiting for it to finish. Wait! You remember don't you - we have timers. Simply set a timer for the length of time it will take to do the move and go off and compute something else with a clear conscience! Not only that, since there are eight timers, you can use a separate one for each of the servos and by testing the returning status byte, you'll know which one(s) finished when. That code is left as an exercise for the "serious minded student". <= Back to Co-Processor page |