This site was created to share some experiences with the nice little
Orangutan controllers from Pololu corp. The Pololu folks are great engineers but despite
enthusiastic promises of examples to come, they do not have time to
provide software support for their products. They need help
from their users!
I'll be happy to post code/links/comments from others here. For example,
Cathy Saxton of Idle Loop Software Design has posted an extensive C++
code
example
for the Orangutan that is well worth studying. Here, I provide some
vanilla C examples.
The Orangutan is based on the ATmega8 (more recently, the ATmega168) controller from Atmel, which is great because of the availability of excellent free development software -- especially WinAVR. I use PIC processors when I can because the instruction set and hardware is so simple, but usually program them with MPLAB in assembler. PIC C compilers are expensive, and not as flexible as the fine gcc compiler.
The down side is that the built-in hardware of the ATmega chips is extremely flexible and the documentation is very hard to read. So, just getting a simple pulse-width modulated motor controller working can be difficult unless you have examples to follow.
NOTE: There are many differences in this family of processors, especially some of the register names and control bit assignments for peripherals such as the timers, UART, interrupts etc. are different. Therefore, code written for the mega8 will not necessarily run on, or may not compile for the mega168. The compiler will warn about incorrect register names, but check the data sheet!
I use a laptop running WinXP Pro connected to the AVRISP mkII programmer. I now use AVRStudio 4 and GCC for development, because WinAVR doesn't yet fully support the mkII programmer -- which is too bad because Programmer's Notepad is a better editor than the one that comes with Studio4.
"WheelWatcher" modules (WW-01) from Nubotics,
attached to the servo bodies, were used to measure the wheel rpms.
The code examples provided by Nubotics were useful to figure out how to
use the modules, but the examples are very complex--far more complex than
they need to be to get the basic job done. I monitored only the right
wheel in my tests. The clock output of the WW-01 (purple lead) was
connected to INT0 (PORTD.2), which unfortunately is required as an output
to the Orangutan LCD module. An INT0 interrupt routine counts segment
ticks and measures the period of wheel rotation (time between segment
ticks), accumulating average values. The direction of wheel rotation and
total travel distance was monitored by the same routine, with the WW-01
direction bit (orange wire) connected to PORTC.5. The distance and
direction information were not used in my example, but could be used for
dead reckoning. This setup was later used to implement error-correcting
proportional (PID) speed control, as PWM alone is insufficient for robust
speed control. See below.
My opinion of the WheelWatcher? Glad you asked! A good idea, but
far too limited in mounting flexibility for different
motor/wheel configurations--I now have a spare set that won't fit my other
applications. It is also very noisy. If you try to estimate rpm's by measuring
the time between encoder ticks (128 per revolution), you see a huge stripe-to-stripe
variation, such as seen at right for wheels rotating at a fixed PWM setting corresponding
to about 30 rpm.
Also, if you make a mistake applying the black-and-silver reflective
wheel sticker, the replacement cost is outrageous -- $5.00 plus
shipping at one supplier.
The wheel speed is measured by computing the period between segment ticks. Timer0 increments at 3.9 kHz and when it overflows, an interrupt causes a counter to be incremented, extending the resolution to 24 bits. As each wheel tick occurs, a time stamp is saved and used to compute the period after the next tick. This approach fails when the wheel is stalled, so a more complete routine (see PID code below) should include a timeout to indicate zero velocity. A low pass filter is absolutely required to iron out the noise in the data (see above).
The LCD display on the Orangutan is good for accumulating the information on the wheel rotational period as a function of the PWM setting, but care has to be taken to disable the INT0 interrupt and reinitialize the LCD output bit on PORTD, as they can't be used by the two routines simultaneously. The actual test code, not cleaned up for publication, is WW_pwm_test.c, which requires routines to run the LCD display lcd_or.c (modified from an example posted on the Polulo user forum) and one of the PWM modules mentioned below. This program steps through a series of PWM settings, measures the wheel rotational period, and outputs the period and PWM setting on lines 1 and 2 of the LCD display, respectively. The wheel speed is calculated in arbitrary units as 1/(period*0.256E-3)--see graphs below.
The on-board LB1836M dual H-bridge has four modes of operation (taken from the data sheet, available from Pololu, or here.)
IN1/3 | IN2/4 | OUT1/3 | OUT2/4 | Mode |
H | L | H | L | Forward |
L | H | L | H | Reverse |
H | H | L | L | Brake |
L | L | Off | Off | Coast |
Forward and reverse modes are obvious, however the difference between the "brake" and "coast" or output-off modes is less obvious. Brake mode is in principle supposed to stop the motor quickly by acting as a short circuit across the motor terminals, but the current must flow through one of the output surge suppressor diodes as well as an output transistor, depending on the direction of motor rotation. The forward voltage drop across these components limits the effectiveness of the braking action Conversely, in "output off" mode the outputs do not conduct current and the motor coasts or freewheels. Either mode can be used for PWM control but the results are quite different (see below).
In my implementation, three interrupt service routines are required (TCNT1 Overflow, Compare matches TCNT1=OC1A and TCNT1=OC1B). The basic routines pwm_brake.c and pwm_coast.c were written to implement this approach and to study current draw and wheel rotational period (converted into robot forward speed) as a function of the PWM settings. Hopefully this example will be useful to others. Please feel free to offer corrections or suggestions. The example driver program pwm_modetest.c can be used to call one of the two PWM routines and exercise the motors.
If the foregoing is not completely clear, well, there is always the ATmega8 chip documentation...
I measured the overall current draw of the entire robot (Orangutan plus motor controller plus WheelWatcher) by a multimeter in series with the battery leads. With the motors off in "coast mode" about 60 mA was consumed. With motors off in "brake mode" about 120 mA was consumed, due to ~60 mA base current for the output transistors in the motor driver chip. Maximum current draw for "free air" rotation (right wheel only) was around 250 mA in brake mode PWM. The current draw is of course higher if the robot is running on the ground.
For brake mode, the wheel speed is very linear with the PWM
setting but the current consumption is generally high and very nonlinear
with the PWM setting. Graphs of the results are posted below. As you can
see, the current draw is highest when the motor is running at about half
speed. This makes perfect sense: for half of the PWM cycle, the rotational
energy is being dissipated by the braking action. However, the speed
setting is quite robust, that is, low speed performance is good, and the
robot maintains its speed rather well if a bit of resistance is applied to
the wheel. Why this should be so is less obvious, and it is not true
for coast mode.
.
For coast mode, the wheel speed is a nonlinear function of the PWM setting, and tops out at well below PWM setting=255. The current draw at low to intermediate speeds is quite a bit lower than with brake mode, but about the same when both are maxed. However, at intermediate speeds, the wheel rotational rate is not robust and slight resistance applied to the wheel causes it to slow down rather quickly. In the graphs at right, wheel speed is in arbitrary units (AU).
The algorithm is proportional/differential (PD) with an additional low-pass filter. I found it was not necessary to include an integral term.
Works great! Take a look at the all-in-one example pid_kd_all.c.
Tuning means to determine the mysterious constants Kp and Kd, which in turn will depend on the system: motor gear ratio, battery voltage, overall weight, friction, phase of moon, etc. There are entire web sites (plus thick, four-color, expensive, dense & excruciatingly boring undergraduate engineering texts) devoted to the process. Sadly, no standard notation seems to have arisen, not even the sign of the error term seems to be standard! It is easiest to start with floating point calculations and then convert to integer math for speed. You will probably need 8 MHz CPU clock or higher.
The basic operation of tuning is fairly simple: you begin with small Kp and Kd=0, and increase Kp until first the system response (e.g. motor speed) stabilizes, then further increase Kp until the response starts to oscillate. Back off Kp a bit, then, increase Kd in order to damp down the oscillations. In theory, there should at most one overshoot in response to a step change in speed.
For more detail, one place to start is the WikiPedia entry.
However, Parallax Inc. has a great example of PID process control using
the Basic Stamp.
Finally, there is always Jeff Bachiochi's lovely example of magnetic
levitation in Circuit Cellar
(Issue 18, Dec-Jan 90-91), in which only 15 lines of BASIC code were
required to levitate a steel sphere with an electromagnet. For another
amusing example, see the PID-Pong levitating ping-pong ball challenge by
Tom Cantrell in issue #50 of the same publication.
To facilitate tuning, you should have a means of studying the
operation of
the algorithm at fairly high speed. Following an example C program posted
by
Nubotics,
I used high speed RS232 output
(38400 baud) using the built-in UART to capture the action of the PD
algorithm at 0.1 second intervals on a PC. All that is needed is a time
stamp, the
current speed and PWM settings, which is all the main loop of the test
program does after posting a requested speed. The output was captured by
HyperTerm and loaded into a spreadsheet for plotting. I found that
the values for Kp and Kd were not critical, with Kp in the range of 0.1 to
0.2 while Kd needed to be about 0.4 to 0.6. After an evening of
experimentation, I settled on Kp=0.15 and Kd=0.40. See the plots below for
some examples, with comments.
The graphs below show the speed and
PWM setting for about 30
seconds. The horizontal axis is clock ticks.
Note:I
physically grabbed
the wheel at two points during each test (for a few seconds) to slow it
down or stop it, so that I could observe the response.
The RS232 AVR to PC transmit-only interface is port-powered from the PC DTR, pin 4 (or RTS, pin 7) and it worked well at 38400 baud using a USB to serial adapter. Similar designs can be found in many places on the web, see for example the PICList site. A schematic diagram is available here. The diode is probably necessary as on my adapter, the RTS and DTR pins swing from about +8V when the port is open to -8V when the port is closed, which could damage the transistor. I built the circuitry into a D9 subminiature shell as shown above.
I based the controller on some very nice example code which included a command interpreter monitoring the serial port, distributed with WinAVR (stdiodemo.c, uart.c, etc.). This code included an elegant input line editor that accepts the delete character and word keys (^W), retype line (^R), etc., so it is very convenient when using a terminal program to test servos! The current implementation accepts, via the serial port, servo control and other commands of the form "C nnn" where C is a one-character command and nnn is an integer argument. Check out this screenshot.
Currently, I have the Baby O controlling two steering servos,
either independently or synchronously, using commands such a "F 150" to
set the forward steering to neutral. The motor driver is an electronic
speed controller that accepts standard servo input pulses (Tekin Rebel).
I use Timer1 to control the steering servos with 0.01 ms resolution
(better
than one degree resolution in the steering angle) and Timer2 to control
the motor
with 0.05
ms resolution steps. This leads to 10 forward and 10 reverse speeds, which
may not be adequate. Timer interrupt routines control the servo output
pulses
on PORTC. This approach consumes a miniscule amount of CPU time and at
present, only about 7.8 KB of the 16 KB flash memory.
Three problems were encountered - two concerned the resolution of the timers and the high clock speed of the Baby O. Surprisingly, 20 MHz is a disadvantage, because an 8 bit timer overflows at 76 Hz, even when using the divide-by 1024 prescaler. To get a 1.5 ms servo neutral pulse requires just 29 increments of the 8-bit timer, limiting the resolution to 0.05 ms (20 settings, 19-39, to cover full scale travel). For a 16 bit timer this is not a problem as 468 increments of CPU clock/64 = 1.5 ms pulse, but there is only one 16 bit counter on the Baby O. For this reason, Timer1 was used to control the two steering servos while Timer2 controls the motor (which leaves one more low-resolution servo control line free). If anyone knows how to obtain higher resolution from an 8 bit timer clocked at 20/1024 MHz, please let me know.
I used the Pololu USB to RS232 adapter to talk to the controller, and here is where I encountered the third problem: if the Baby O is not powered up when the USB adapter is plugged into an active port, the Baby O will be weakly powered up from TX, through the input protection diodes on port pin PD0. This caused it to respond unpredictably to the AVRISP programmer, in some cases not at all. The problem was solved by connecting a 10K ohm resistor between TX on the USB adapter and RX (PD0) on the O. A photo of the completed project (off truck) is shown at right.
Code
for the project follows:
servo_control.c
uart.c
uart.h
Feel free to email
me with comments, questions or suggestions.
Copyright (C) 2006 by S. James Remington. All rights reserved.