Autonomous R/C Car - Part 2 - Taking Control

Synopsis: This is Part 2 in a series of turning an R/C car into an autonomous vehicle and presents how to generate pulse width modulation signals (PWM) required to control a steering servo and motor controller of a typical R/C car. The purpose is to replace the manual radio control mechanism with a microcontroller which can ultimately control the vehicle autonomously. Example code and tests are presented with video showing the motor and steering under microcontroller control. Navigation algorithms will be covered in future articles. The focus of this article is how to make the car turn and change speed using an ATMega128 AVR microcontroller.

Below is a video of the example test code exercising the R/C car on the workbench. I have the car propped up on a small pedestal, a Sparkfun enclosure lid, so it won’t run away (handy enclosure :-) After the video are extensive details of implementing PWM for servo control on ATMega128 AVR.



R/C car showing PWM waves on scope

The photo above shows the car under test with the scope showing the PWM waves generated by the micro - yellow for steering and blue for motor. The test setup is being driven by an ESawdust designed AVR development board.

In Part 1 of this series, we reverse engineered the signals coming from the R/C radio receiver in the car and learned how the speed and steering signals controlled the stock vehicle. This confirmed the typical servo PWM wave form for both steering and motor control.

Our next task is to emulate these signals with our own microcontroller so we can start working up the stack of tasks needed to take autonomous control of the vehicle. That’s what this article is about.

Why Not Use Existing Servo Libraries for the AVR?



There are many servo libraries out there which we could use, but one of the main objectives of this project is to learn autonomous control from the ground up, so deriving certain fundamental operations like steering and motor control is both part of the fun and a main objective.

ATMega128 Development Board - The ESawdust RAD-GPS



We chose an ATMega128 AVR micro instead of an Arduino (ATMega168 or 328) because ATMega128 has two serial ports and I expect to need both serial ports for the firmware as we get into the higher level navigation code. The serial ports will support the GPS data stream and also a data radio for diagnostics or external control.

The ATMega architecture also has builtin timing support and PWM functionality, so it was a natural choice for autonomous vehicle control. We could “bit-bang” a PWM wave if we wanted to but that would be a waste of processor cycles since the timing work can be handled by the micro directly in hardware, and we just need to figure out the setup and control. We need the processor cycles for navigation computation and situational awareness (obstacle detection and avoidance), not manually making a PWM wave.

Last fall, I designed a PCB which carries an ETT AVR (ATMega128) stamp board, has builtin in hardware support for a 4Hz Falcom FSA03 GPS and XBee data radio. The board is called a RAD-GPS (which stands for “radio-gps”) and I’m planning on making the board available on the ESawdust store shortly. The board has a builtin wide-range input power supply and regulator up to 1 amp, builtin 3.3-to-5V signal level conversion to safely interface the 5V AVR micro to the 3.3V GPS and 3.3V XBee. It also includes a prototyping area, status LEDs, breakouts for ATMega128 AVR, XBee and GPS and finally supplies a JTAG interface for programming and debugging. This article covers ATMega128, so any dev board for ATMega128, such as the ETT AVR dev board, could be easily adapted also.

Here’s a shot of the RAD-GPS we’re using as the AVR dev board for our autonomous r/c car:

ESawdust RAD-GPS

While the RAD-GPS is able to support any of the XBee modules because they are electrically pin compatible, I plan to use the XBee 900 Pro. From my experience in the great pumpkin launch last fall, I was impressed with the XBee 900 Pro and its great comms. I expect I’ll use the loose wire antenna version of the XBee 900 Pro rather than the rubber duck shown in the image above.

For very long range applications, the RAD-GPS PCB also supports the FreeWave MegaMini 2, a high-end, 1 watt, 902-928 MHz data radio capable of comms up to 60 miles line of sight ( directed RF ), but that’s total overkill for an autonomous R/C car, so we’ll stick with XBee.

Overview of PWM with ATMega128



Not surprisingly, it’s critical to sidle up to the ATMega128 datasheet at Atmel in order to understand how to set up PWM. ATMega128 is perfect for controlling up to 3 servos with one timer and 3 servos is more than we’ll need for this project.

In addition to one main timer for PWM set up, ATMega128 supplies 3 registers per timer which govern the duty cycle of up to 3 different PWM waves connected to 3 different pins on the micro. There are other convenient functions of the ATMega which allow the programmer to specify both inverted and non-inverted waves as well as pure 50% duty cycle waves. We’ll use the non-inverted wave form for servos since we mainly just want to specify how long the signal should be held high rather than how long the signal will be low.

There are various modes of PWM supported by ATMega128 which is both good because it’s flexible, but bad because it makes you have to dig a lot to find what you need. We’ll use what’s called Phase and Frequency Correct PWM which uses an up-down counter for the period and a variable threshold to adjust the duty cycle of the PWM wave. The data sheet has this to say about it:

The phase and frequency correct Pulse Width Modulation, or phase and frequency correct PWM mode (WGMn3:0 = 8 or 9) provides a high resolution phase and frequency correct PWM wave- form generation option. The phase and frequency correct PWM mode is, like the phase correct PWM mode, based on a dual-slope operation. The counter counts repeatedly from BOTTOM (0x0000) to TOP and then from TOP to BOTTOM. In non-inverting Compare Output mode, the output compare (OCnx) is cleared on the compare match between TCNTn and OCRnx while counting up, and set on the compare match while downcounting. In inverting Compare Output mode, the operation is inverted. The dual-slope operation gives a lower maximum operation fre- quency compared to the single-slope operation. However, due to the symmetric feature of the dual-slope PWM modes, these modes are preferred for motor control applications.



According to the datasheet, this is the “preferred for motor control” mode (motor being the servo) and generating a 20ms PWM wave is not pushing the limits whatsoever of this mode (as you’ll see from the calcuations we do below.) In fact, having it be relatively “slow” is just fine for us. For other applications, ATMega also has “fast PWM” modes where the counter is only up or down, but the up-down counter works better for PWM for servos.

You’ll see in a moment why an up-down timer counter works so well for generating a PWM wave.

It’s worth noting in an overview of ATMega PWM is that in order to get a PWM signal out of the micro, specific multi-function pins, must be set for a data direction of output. The multi-function pins on the ATMega128 that are internally wired for PWM output are OC1A, OC1B, and OC1C (port B, pins 5, 6 and 7.). We won’t need any interrupts from PWM - we just want to set it up to constantly drive PWM output and simply adjust the duty cycle of the waves to stop, start, speed up, slow down, and turn the car.

In an overview, it’s helpful to lay out the registers and terms involved in setting up PWM because it can get somewhat confusing when you get deeper into it. A summary follows, but these will be explained farther into the article.

  • ICR1 - input capture - the register that holds the TOP target counter value - essentially governs how long the PWM wave period is
  • OCR1A, OCR1B, OCR1C - 3 registers which can be used with 3 different output pins to support 3 different PWM duty cycles using the same timer, TIMER1 (hence the name, OCR1, a register). The A, B and C, correspond to ATMega128 output pins that will carry the PWM signal.
  • OCR1A, OCR1B, OCR1C - output pins on the micro connected to the internal PWM logic - these are the spigots for PWM where the servo signal wires will be connected. On an ATMega128, these are PORT B, Pins 5, 6, and 7.
  • TCNT1 - the counter register that holds the up-down count value of TIMER1

Calculating PWM Timer and Register Values



One of the main formulas for computing PWM register setup is given in the Atmel datasheet on page 130. It is:

OCnxPFCPWM = clk_IO / ( 2 x N x TOP )

Looks nasty, but it’s very easy.

N” is a timer prescale factor of 1, 8, 32, 64, 128, 256, or 1024. You can think of the timer prescale like gears in a car. A prescale of 1 causes the timer to run at the maximum possible clock speed. Each of the other prescalers is like gearing down the clock from the fastest possible speed to one that fits the conditions you need to match. First gear would be a prescale of 1024, second gear, 256 and so on until you get to a prescale of 1 which is the highest gear for the timer.

TOP is the ICR1 register value that the timer will count up to and then back down to 0 for each period. It’s the target count. The larger it is, the longer it takes the timer to count up and back down.

OCnxPFCPWM is the ATMega datasheet way of saying “frequency in hertz.”

Computing TOP



The larger the TOP value is, the longer it takes to count up and count down which has the effect of making a longer PWM wave period. So, we use TOP to help control the servo signal period. Keep in mind this only controls the full period of the wave and does not control the duty cycle. More on that later.

As a reminder, our objective is a 20ms PWM period (as measured in Part 1 of this series), and the steering and motor control duty cycles were in the range of 1ms to 2 ms of 20ms (5% to 10%.) The frequency, in Hz, of a 20ms period is:

1 cycle / 20ms == ? Hz

You can figure that out by converting to cycles per second (Hz) :

(1 cycle * 1000ms) / ( 20ms * 1sec ) == 1000 cycles / 20sec = 50 cycles / sec = 50Hz

We need this number to plug in to the Atmel PWM formula, the OCnxPFCPWM parameter, and figure out the other parameters.

The ETT AVR Stamp has a 16MHz system clock, so this will become the clk_IO parameter in the formula, and the ATMega fuses need to be set to use the external oscillator as the system clock source.

Lets do some sample computations and see how close we can get to a 20ms period.

If the system clock is 16MHz, then the timer clock can be prescaled 1, 8, 64, 128, 256, or 1024 which means the timer can run at any of these frequencies:

Prescale of 1: 16MHz / 1 = 16MHz
Prescale of 8: 16MHz / 8 = 2 MHz
Prescale of 64: 16MHz / 64 = 250 KHz
Prescale of 128: 16 MHz / 128 = 125 KHz
Prescale of 256: 16 MHz / 256 = 62.5 KHz
Prescale of 1024: 16 MHz / 1024 = 15.625 KHz

We’re trying to arrive at an appropriate prescale value and a TOP value that will generate a 50Hz signal. Using the Atmel formula, if we do a little algebra, solve for TOP, and make the formulate slightly more readable, we get:

TOP = clk_IO / (2 x N x Desired-Frequency )

Plugging in a few prescale values, we can see what values of TOP might be reasonable:

Prescale of 1: TOP = 16,000,000 / ( 2 X 1 x 50Hz) = 160,000
Prescale of 8: TOP = 16,000,000 / ( 2 X 8 X 50Hz) = 20,000
Prescale of 64: TOP = 16,000,000 / (2 X 64 X 50Hz ) = 2,500

Since TOP value needs to fit into ICR1, which is a 16 bit register, we can see that a top value of 160,000 will not fit, but 20,000 will fit in ICR1 and so we can use a prescaler of 8. A prescale of 64 would probably work for this application, but we can get better timer resolution using a prescale of 8 which will result in a more accurate clock period. That makes 20,000 for ICR1 (TOP) the better choice.

So, the critical values we need to set up to get a 50hz PWM signal are ICR1(TOP) == 20,000 and Timer1 prescale of 8. With a Timer1 prescale of 8, using a 16MHz system clock, the timer is cut to a 2MHz signal. We’re using the CodeVision compiler and wizard, so to set up the clock frequency for Timer 1, we choose what CodeVision calls the 2,000 KHz clock. 2,000 KHz == 2MHz.

ICR1, while it’s a 16-bit register can be addressed in bytes, so 20,000 is 0x4E20 and ICR can be initialized as ICR1H = 0x4e and ICR1L = 0x20.

The Forest from the Trees



We need to pull back a second because we’re really only half way to creating a PWM wave to control a servo. The only thing we have managed to do so far is derive the values needed to tell the micro how long the wave is. The period, 20 ms, is a 50Hz wave. We know from reverse engineering the signals in Part 1 of this series, that this wave needs to have a duty cycle (one part on, one part off) that varies. The varying duty cycle is the key to controlling the speed and direction via a servo or motor controller.

There’s another ATMega128 register that controls the duty cycle of a PWM wave and that is the OCR1 A, B, or C (for timer 1). As I mentioned earlier, one timer can control 3 servos and the way it does it is through these 3 registers, one for each servo / output pin. OCR1A is for the output pin OC1A, servo 1. OCR1B is for the output pin OC1B, servo 2. OCR1C is for OC1C, servo 3. Since all servos work on the same 20ms period, one timer, with the help of 3 registers, can generate independent control signals for 3 servos. Once I saw how this worked, I think it’s an ingenious design feature of the AVR (perhaps other micros use the same concept as well.)

Changing the PWM Duty Cycle - the Key to Controlling a Servo



The larger OCR1(abc) value is, the longer the PWM wave will be held high. For servo control, the two extreme ends of travel we found were 1 to 2ms with a neutral position being about 1.5ms. For the throttle, it was even slightly less outer range - 1.2ms to 1.8ms. Out of a 20ms wave, we need to make 1 to 2ms of it high in order to control the servos.

To understand why changing the OCR(abc) register changes the duty cycle, it’s helpful to draw what’s going on and visualize it (see the “pencast” below). OCR(abc) is a threshold value. The timer is a counter and we configured it as an up-down counter - the up-down counter is part of what PWM with phase and frequency correct mode does. So, the timer counter, TCNT1, will cross the OCR threshold twice. It crosses once as it’s going up and once as it’s coming down. When it gets back to 0, the timer period has ended, though that doesn’t mean the PWM edges start or finish there. They start or finish on the threshold cross-over.

The key to understanding the OCR threshold is knowing that the OC1 output pins will change state each time an OCR threshold value is passed by the TIMER1 counter, TCNT1 - passed either going up or coming down.

You can visualize the TCNT1 as a saw-tooth wave where the two cross over points where the timer counter is equal to the OCR threshold will define a time-span when the pulse is either high or low. The time span is the distance between two cross-over points on the saw-tooth.

Here’s a simulated hand-sketch of what’s going on - if you play this sketch, you’ll get a “chalk-talk” on the relationships between the timer counter and OCR threshold value. This basic diagram is available in the Atmel datasheet, but this treatment gives a slightly more dynamic view of what’s happening:



The final detail in setting up PWM on the ATMega128 is to enable PWM output pins. This is simple. There are 3 multi function pins on PORT B that are for PWM output of Timer 1. We’ll use two of them, Port B, Pin 5 (OC1A) and Port B, Pin 6 (OC1B). These correspond to the Timer1, OCR1A and Timer1, OCR1B registers. To enable these pins for PWM output, we simply set up the data direction registers for output on those pins. Internally, the ATMega makes the PWM logic connection with the output pins and you’re good to go.

Summary for PWM Servo Control



OCR1A is the register we’ll set to steer the car and we’ll use OCR1B to adjust the speed forward or backwards. The ICR1 register (TOP) is the way we adjust the period of the wave and it will never change after it’s set (it can change to make varying periods, but we don’t want it to change because the period is set at 20ms for servo control.) The other setup registers just tell the timer what mode to be in (Pulse Width Phase and Frequency Corrected) and what wave form to output (inverted or non-inverted.)

The image below shows a snippet of code in the test program that exercises the steering servo as seen in the video:

SampleSteeringTestCode

The full test code CodeVision AVR project can be downloaded if you’d like to look at the whole thing. The main file to study is autoveh.c. This is the exact code/project used to exercise the vehicle in the video at the start of the article.

Involving the Boy



I’m always looking for opportunities to involve my son in this project since I want to make this autonomous R/C vehicle a major learning opportunity for him. Obviously, this article shows that an easy concept such as PWM has more than a few considerations and a pretty deep section of a data sheet to wade through. I don’t expect my 11 year old to be able to derive all this so this is an area I got running first and then went over the explanations with him. Ultimately, this code will be encapsulated in methods like “turn_right( how_much )” and “throttle_forward( how_fast )” that will make more sense to a junior coder.

I really expect to have Nakoa much more involved in the next phase (roadmap outlined toward the end of this article.)

Even so, I found some tasks for him to do in this phase. For this article, his part was to solder the jumper board for the servo connections to the micro and also to solder a 25 pin header to the left of the ATMega128 socket so we could tap the PWM pins on the micro. Here’s the start of the servo jumper board - signal and power will come into these from the RAD-GPS controller board:

Servo jumper board

Nakoa soldering the 25 pin header to the side of the AVR:

DSCF4833

Testing



So, that’s all a lot of interesting PWM theory, but here’s how it looks in the real world, on the bench for testing:

DSCF4839

The two wires, Green and Yellow, coming out of the AVR micro are from Port B pins 5 and 6 which are the two PWM pins we discussed above.
AVR PWM pins
They connect to the servo jumper board we made along with power and ground. The two signal wires from the micro connect with the signal wires of the servo lines coming from the car - one for throttle and one for steering. Two 3-wire servo cables go from the jumper board to the car. There’s a common ground between the RAD-GPS dev board and the vehicle’s power supply which is created by connecting the RAD-GPS dev board ground to the ground wire coming from the servo on the car.

The movie at the beginning of the article shows it all together and working and shows in real-time the relationship between the length of duty cycle of the PWM wave and how the car will steer or move.

You can download the test code, written for the CodeVision AVR compiler, used to run the car through its paces and the test code that was running when the video above was shot.

Roadmap



We’re finished with Phase 1 and successfully completed our objective: gaining control of the vehicle with our microcontroller and bypassing the standard radio. We’re ready to move into Phase 2 with our autonomous R/C car: learning how to navigate and experiment with various algorithms to steer and move in real space.

The Phase 2 objective is simple to state: We want to create a waypoint, send it to the vehicle, have the vehicle drive straight to the waypoint. Doesn’t matter how fast, slow, ziggy, zaggy it is, the objective will be to just get it to drive autonomously to a single point and recognize when it gets there. I suspect we’ll need to implement a pickle switch in Phase 2.

Phase 3 is going to address obstacle detection and avoidance. I suspect this will be the most difficult phase and one that will never be complete, so the trick with this phase is to define what is “good enough.” Part of this objective will be to figure out the appropriate sensors as well as defining the test cases the vehicle will need to deal with and hope those cases will be sufficient for the live course.

Phase 4 objective will be to receive a list of waypoints that defines a course, optimizing speed around a course, and recognizing when it’s finished. I suspect this phase will stress our obstacle avoidance algorithms so we don’t outrun our sensors and end up with a spectacular crash.

Landon Cox
www.ESawdust.com

Other articles in this series:
Building Bots with Kids
Autonomous R/C Car - Part 1 - Reverse Engineering Signals
Autonomous R/C Car - Part 3 - GPS
Autonomous Vehicle - Part 4 - Mechanical, Firmware
Autonomous Vehicle - Part 5 - Race Day


Autonomous R/C Car testing
asdfasdf