Sunteți pe pagina 1din 29

Autonomous Automa c Automobiles

Senior Capstone Design


David Trethewey
Michael Jones
Adviser: Dr. Christopher Rose
May 12, 2011

Abstract
Overview of project.

Mo va on and Objec ve

In the modern world technology plays an ever-increasing role in all of our lives. Cars are no different; all cars today are controlled by sophis cated computers, and have yet more sophis cated
computers for comforts and ameni es. One car, the Mercedes-Benz S-Class, is considered to be a
technology preview for what will appear commonplace in average cars in ten to twenty years. For
instance, it was the rst to use seatbelt pretensioners, the devices that lock the seatbelt when it
is pulled too hard. The technology currently in this car inspired the idea for this project. The car is
equipped with a RADAR guidance system, which monitors the environment and can apply braking
to avoid an accident if the driver isnt pushing down hard enough, or even if the driver is doing
nothing at all. There are also lane sensors, which alert the driver if the vehicle starts to leave its
lane without the use of a turn signal, so all the driver has to do is put the car into cruise control and
listen for the beeps. There is also sophis cated body tracking technology to monitor the alertness
of the driver to keep the driver from falling asleep behind the wheel, and a n infrared camera that
can allow the driver to see far ahead of where headlights at nigh me normally can. But this extra
technology to tailor specically to the driver begs the ques on: Why do we need a driver at all?
The car contains much of the technology needed to drive itself, plus a lot of wasted technology
on the user controlling it. It has a GPS Unit, so why can it not just take and address and go there
itself? This ques on is the fundamental ques on behind this design project.
Our objec ve was to develop and test both autonomous vehicle control methods and wireless
vehicle communica ons that would allow mul ple cars to nego ate the same space. That is, our
goal was to develop a way for robo c automobiles to share the road. The ideal way to build this
would be to build a robo c interface to our real cars, a ach wireless transmi ers and drive the cars
around on real roads. Owing to insurance concerns this is an infeasible construct, so we chose to
model the system with R/C Cars. They are smalla foot to two feet longso we can use a closed
o area to create a road. We also made some simplied assump ons in the environment. The real
cars have thousands of dollars of advanced RADAR systems and video sensors to monitor the road.
For our simulacrum we built an induc ve guidance system to keep the car in the lane, and used
several cheap(er) ultrasonic rangenders to locate nearby obstacles. The brain of the cars is the
Parallax Propeller Proto Board, an eight core microprocessor, while wireless communica ons are
handled by the XBee Wireless Radio device. These are actually realis c components in a real-life
automobile control system, though not in proto board form. Using these parts we create a moving
wireless mesh network.

Robo c Vehicle Design

All of the vehicles were once normal R/C Cars, but have been torn down to just the chassis, motor(s) and wheels. Each car has a motor controller, ba ery pack, and a pla orm on which to mount
2

the electronics.

2.1

R/C Retrot

Two of the cars happened to be repurposed from an older project, so these already had a pla orm
for the electronics, and even breadboards and Electronic Speed Controllers. These have a drive
motor to the rear wheels and a servo for steering up front. Ba ery holders are a ached on top of
the main chassis support of the vehicle. Both the ESC and the steering servo are controlled using
servo input commands. The controller available was the Futaba MC230CR FET Speed Controller.
It can go in forward and reverse, with the width of the servo pulse determining how fast or slow
it should go. The ESC is programmable by the following direc ons:
1. Send the servo command corresponding to neutral to the ESC
The main power should be OFF.
2. Press and Hold the Bu on on the ESC for at least half a second
You will hear a conrma on beep, and the LED will start to blink at a regular interval.
3. Send the servo command corresponding to Full Speed (Forward) to the ESC
4. Press and Hold the Bu on on the ESC for at least half a second
You will hear a conrma on beep, and the LED will start to double blink.
5. Send the servo command corresponding to Full Brake to the ESC
6. Press and Hold the Bu on on the ESC for at least half a second
You will hear a conrma on beep, and the LED will turn o.
Since servos normally use a range of 1ms-2ms peak, 50Hz PWM signal with 1.5ms as the middle
point, these are what we used. The reverse func on can be used a er the ESC is brought back to
neutral; then the brake range is the revere range, with a 1ms-wide pulse signifying full reverse.
These cars have a sophis cated rack-and-pinion steering system, so the servo in the front only has
a limited range, about 90 degrees. Through tes ng we calibrated the rou nes to each steering
servo so we didnt try to oversteer the servo. This process is described as part of the PID Coltroller
sec on. Below are images of the two cars; the top image is of the clear, and the bo om is of
frost.

2.2

RoboTyco

The fourth car was a Tyco R/C Car in its former life, and it had a slightly dierent drive mechanism;
each pair of wheels on either side are a ached to a motor via a series of gears, so each side is
driven by a separate motor. It is four-wheel drive, similar to the four-wheel drive system of a
pickup truck where it usually has to be locked on. This is great for o-roading, and in our case
it greatly simplies steering since the wheels themselves dont turn to steer. In this car to turn
le , just run the le motor slightly slower than the right. Since the right wheels now are covering
more ground than the le , the car has no choice but to turn le . A li le breadboard contains the
new speed controller for the Tyco, the L293 Quadruple Half-H Motor Driver. This controller uses a
MOSFET network to turn the motors on and o and segregate the motor power supply from the
logic supply.
Figure 1: Quadruple Half-H Driver for two motors.

The drive for each motor requires two inputs: A and B. The ENx pins shown in 1 are always high
if our system is on, since that is the equivalent of pu ng the car in Drive. whereas when ENx is
low the system is in Neutral, the wheels can spin more or less freely, although they are, in fact,
s ll a ached to the motor1 . For each motor, when A is high and B is low the motor rotatesat full
speedin one direc on, and does the reverse when B is high and A is low. If both are high or both
are low the motor is in a BRAKE mode, where it locks the rotor into posi on. This is because the
two terminals are essen ally ed together, causing the induc ve load of the motor to be turned
back on itself. In this mode the vehicle comes to a stop very quickly. So this can turn the motor
in both direc ons at full speed and stop it. In order to get a specic speed we will have to rely on
Pulse Width Modula on.
Below is an image from above of RoboTyco.
1

Although note that this par cular controller, in the par cular opera on it is in, cannot be in neutral unless the
power to the Quad Half-H is removed. This is due to the way the outputs behave and interact in high-impedance
mode.

2.3

Propeller Microcontroller

The propeller microcontroller from Parallax, Inc, is an interes ng beast. It really is like every other
hobbyist robo cs microcontroller, except for one thing: it has eight cores, referred to as cogs.
This is ideal for the robo cs applica on, as there are many things on a robot that need constant
a en on. In a conven onal microcontroller mul tasking is pre y dicult. There are sensors and
motors and transceivers that all need clock cycles in order to maintain the robot. But, any me
you need to do something on the propeller, you just put it in a cog and it runs in parallel. In
the case of the motor controls, the speed values can be put into registers of common ram, to be
changed whenever an external ac onsuch as a reading from a sensor, or an immediate HALT
signalneeds control. In this setup there is no need for interrupts, and indeed, they do not actually exist in the propeller assembly language. Just pass a value by reference and messages can
easily be passed between cogs.
The design is meant to be simple, but powerful. The internal ALUs in each cog do not even
support mul plica on or divisionthey do, however, contain log and sine look-up tables to do
the math anyway. The cogs all share the 32 I/O pins. Each cog is serviced in sequence by the hub,
so that every eight clock cycles a cog gets to perform a task. Some care does have to be taken
so as to not use the same pin in clashing contexts, but the overall simplicity of everything else
makes this easier. There are also no Analog-to-Digital converters on the device, so we added an
MCP3202 to the proto board. It provides two 12-bit analog inputs in a serial interface using four
6

Figure 2: Propeller Proto Board USB Layout

I/O pins. These analog inputs are used in the induc ve wire guidance system described in detail
in Sec on 2.4. For the vehicles with ESCs and a steering servo, two pins are required for the drive
system. Since the H-Bridge needs two inputs per motor, four in all are needed for that car.
This microprocessor is a pre y novel concept, but it is dierent than other designs and that
takes some ge ng used to. Luckily, the propeller has a secret weapon: The Propeller Object
Exchange at h p://obex.parallax.com/. The exchange is a place where the propeller community
can share their objects with others, and where anyone can get an object to use. There are many
objects, and we made use of the OBEX for many of the hardware components we had selected.
The par cular microcontroller we purchased was the Parallax Propeller Proto Board USB, which
contains an on-board USB-to-serial converter that can be used to program the propeller as well
as control another USB device. The board contains the propeller chip itself, an external crystal
and oscillator circuit, a 5Volt and 3.3Volt regulator, and is covered everywhere else with plated
through holes. Some are ed to specic pins, while some are for ground and posi ve voltages,
and others are for special purposes or anything at all.
1. I/O Pin Headers Around the propeller chip are through holes for each of the propellers
I/O pins, as well as pins for VDD (+3.3V) and VSS (Ground). We soldered in header pins to
each pin so we could jumper to the various inputs and outputs.
2. SERVO Controls The pins in the top-right are for Servo Controls or for anything that
needs +5V source, Ground and signal connec ons. These pins are connected to pins 0-3
on the propeller. The jumper to the le selects the servo power supply, here selec ng the
regulated 5V supply. The ba ery input itself is selected if the jumper is ipped2 .
2

Not recommended when running o of 9 Volt ba eries. This se ng is for running o of 6 Volt ba eries.

3. Keyboard/Mouse and VGA The propeller supports VGA (and NTSC) video as well as P/S2
keyboard and mouse connec ons. The resistors in the bo om-le and the through holes
are shaped for these connec ons, although they are unused. The propeller contains the
ability to transmit those signals through the USB, so your computer acts as a terminal. This
was used for exploratory purposes, but not in the actual design of the project.
Since the propeller does not have a na ve ADC, we used an MCP3202 Analog-to-Digital Converter.
The ADC comes in an 8-pin DIP, and it is interfaced with a standard known as SPI, Serial Programmable Interface. The device has two inputs, and the 12-bit value sent over the SPI is
ADV = 4096

VSIG VSS
VDD VSS

where ADV = 0 if VSIG VSS and ADV = 4095 when VSIG VDD . The OBEX contains an
extensive object that allowed us to easily u lize the device.

2.4

Wire Guidance System

In the cars of the future, all sorts of fancy systems are in use to monitor the environment, most of
which are very expensive and physically large. So for our system our roadways consist of a 24AWG
enamel-coated magnet wire. The vehicles will then follow this wire using a system referred to as
Induc ve Wire Guidance. The wire is laid out in loops, and hooked up to the generator. The generator then sends an AC signal at around 50kHz down the wire. The signal is generated using a 555
mer and a tuned LC circuit. The 555 mer creates a square-wave signal at the frequency desired,
and the inductor-capacitor circuit shapes it into a sinusoid. According to Ampres circuital law,
a change in current through a wire generates a magne c eld around the wire. In normal magne c eld conven on, it is said that the eld curls around the wire, perpendicular to the direc on
of current ow. So by sending a sinusoidal current down the wire we generate a magne c eld
whose strength changes as a sinusoid. Inductors are just coils of wire, and the specic inductors
we used hereand are necessary for this type of opera on are cylindrical, with a ferromagne c
core to amplify the magne c eld. A magne c eld induces a current in the coil that is proporonal to the eld strength. Therefore, the sinusoid induced in the inductor is the same sinusoid
as the current owing through the guidance wire. Magne c eld strength falls o propor onally
as it gets further from the source, so the induced voltage across the coil can be used to tell how
far it is from the wire.
Now that we have this, we can follow the wire with the cars. One inductor is posi oned to the
le of the right wheel, and the other mounted just to the right of the le wheel. By pu ng the
proper capacitor in parallel a specic frequency can be selected easily. From here, an opera onal
amplier is used to amplify the signal. No ce that the signal is now vS (t) = A sin (t), and A
is propor onal to the inverse of the distance the sensor is from the wire. This is an Amplitude
Modulated (AM) signal. Using a simple envelope detector the signal is demodulated, and this
signal can be fed into the ADC. The general design of the circuitry is taken from a circuit posted
by a Lego Mindstorm hobbyist and adapted for our use. We had to take calibra on readings and
8

Figure 3: An induc ve guidance system and the generated magne c eld.

tune the ampliers to give approximately equal inputs to the propeller. Then we could use the
values as part of the controller to steer the robot over the wire.

2.5

Collision Detec on System

The full-sized cars use millimeter-wave RADAR systems. While they work very well for full-sized
cars, on our small scale and for our budget ultrasonic rangenders are more appropriate. For
tracking the cars or obstacles in front of the car one PING))) Ultrasonic Rangender. In order to
tell if another vehicle is behind the car, we also ed a rangender in the rear. This acts as the
rear-view mirror for the system.
The ultrasonic sensors are very simple, and use only one I/O pin. To use the rangender, the
following steps are taken:
1. Send a pulse down the signal line to the PING))) This pulse tells the ping to start its rou ne.
A er a short delay the PING))) emits an ultrasonic chirp at 40kHz.
2. Wait for the signal line to go high the rangender pulls the signal line high when the pulse
is sent out. It stays high un l the signal is received back.
3. Measure the me it takes for the signal line to go low When the PING))) hears its echo it
de-asserts the signal line, or it mes out.
Since sound travels through air at approximately 343 meters per second, the distance to the object
is
distance = speed time = 343m/s t ms
9

distance mm = 343 t ms
It is convenient (because the propeller object returns it) to talk about me in milliseconds and
distance in millimeters. Note that the speed of sound is aected by temperature, barometric
pressure and humidity, but it will aect all rangenders equally, so we can safely ignore it.

2.6

Wireless Communica on

Wireless Communica on is essen al to bridging the gap to autonomous roadways. Informa on


sent among the cars will let them know of each others inten ons. The XBee RF Module by Digi
Interna onal ts the bill nicely. The devices use the (admi edly crowded) 2.54GHz wireless spectrum and the same channels used by WiFi networks. However, these are not 802.11 devices;
they use the 802.15.4 standard. This standard was designed to be used for low data-rate, higheciency communica ons in mobile embedded devices, which is perfect for our needs. The
802.15.4 standard consists of a Media Access Control layer on top of a hardware layer, and is
designed to be bare-metal, but also provide essen al services of a modern protocol. The standard supports collision detec on and avoidance and real- me data transfer through guaranteed
me slots. There are many other great features of 802.15.4, but this project does not interface
with it directly, so it is sucient to note that it is used by the XBee devices.
The XBee devices actually present themselves to the user as a simple serial data interface. In
transparent mode, two paired XBees act as a drop-in replacement for an RS-232 line. The devices
support a more advanced mode, API Mode, which is what we used for the wireless communicaon between vehicles. In API mode all data is framed with essen al informa on and transmi ed
as a packet. In this mode the XBees support both broadcast and point-to-point communica on.
The structure of the data frame is shown in gure 4. The frame is simple: the byte 0x7E is sent,
Figure 4: Generic Data Frame Structure

indica ng the start of the frame, then the length of the frame is sent, telling the receiver how far
to read. All of the data is checksummed for added integrity. Every me a transmit or receive is
performed this data has to be parsed, adding complexity over the transparent mode. However,
the advantages far outweigh the complexity issues. Now the network can be fully Ad-hoc. When
two cars get close, they no fy each other of their presence. They can then send data directly to
each other in order to no fy the other vehicle of various statuses. When they get too far apart
they terminate the link, which really means not sending data anymore, because there is nothing
to build up or tear down. Each XBeeand by extension each vehiclehas a unique address which
can be found in packets received from the radio and can be used to send messages only to that
radio. The two specic packets we deal with are the transmit (TX) and receive (RX) packets, shown
in 5. When transmi ng, the XBee sends a frame ID with the data to the address specied in the
10

Figure 5: TX/RX Packet Data Frame Structure


TX Packet

RX Packet
des na on address block. If received the remote XBee will send an acknowledgment3 using the
frame ID. On the receiving end the packet contains the data being sent, along with who sent it and
the received strength of the signal. Using these data we designed the communica ons logic for
the devices shown in 4.3

Designing the Track

For our test track we wanted to test specic features. We did not want to test how well the
hardware in our second-hand R/C cars worked, and this included turning radius. So when laying
out the wire we were careful not to make the turns that ght. We did, however, want to test the
collision avoidance system. We se led on a gure-eight design, with the track crossing itself in
the middle to form an intersec on.
The notches in the track are there so that the car knows it is in the intersec on. When the
induc ve sensors go over the loca on neither one will read high. Because they are both low the
algorithm will a empt to drive straight, and it will pick up the line on the other side, but for a small
amount of me the car will get the (no) signal it needs. Having an intersec on allows us to test
more condi ons than with a simple loop. We will be able to nd out if the cars can nego ate it
themselves, or if an intersec on requires a third party to be a gatekeeper.

Programming The Propeller

As men oned above the propeller has a vast collec on of objects at the object exchange. We were
able to nd objects for controlling the MCP3202 ADC, the XBee radios, the servo controllers and
the PWM generator needed for the H-Bridge driver4 . Using these parts the code in the appendix
was wri en. At its heart, the system is controlled by a PID-derived controller that follows the wire,
and the behavior is altered based upon the wireless communica ons and the readings from the
3
4

For this specic project ACKs are ignored.


ADC_Input_Driver.spin, XBee_Object_2.spin, Servo32v7.spin and pwmAsm, respec vely

11

Figure 6: Rough Sketch of Track (Not to scale)

rangenders. Specic examples in this sec on are for the ESC R/C cars, but the concept is very
similar for the H-Bridge controller. Most parts are completely unchanged, in fact.

4.1

PID Controller

A PID Controller is a type of feedback system that has a Propor onal part, an Integral part and a
Deriva ve part. The error is measured, and based upon that error the system adjusts to correct for
it. With just propor onal control the system can only react to the instantaneous error, leading to
a ji ery system that is constantly overshoo ng the ideal. By adding in the integral part the system
can react to an accumula on of error. If the system is consistently o in one direc on the integral
part of the feedback can correct for this. The integral feedback is especially useful while going
straight, as it prevents the wobble of a propor onal system that is constantly moving from one
side of the wire to the other. The deriva ve part responds to chances in the error. The system can
respond more quickly to larger changes in error, and slower when the error isnt changing much,
allowing the propor onal and integral controls to be more prominent. The deriva ve control helps
when turning, because of the large change of the wire veering away from the vehicle.

e (t)
f (t) = Kp e (t) + Kp e (t) dt + Kd
dt
Of course, in our system this exact controller would be dicult to implement. To get the behaviors of a PID controller the following code was used:
repeat
12

pre_cnt := cnt
error := chanval[0] - chanval[1] * chanmax[0] / chanmax[1]
'channel 0: Right, channel 1: Left
P := Kp * error
I += Ki * error
D := Kd * (error - pre_error)
cur_pos := 1000 #> (cur_pos + cur_speed * (P + I + D) / 1_000_000) <# 1000
if (cur_pos > 0)
servo.set(steer_pin, cur_pos * (max_left center) / 1_000 + center)
else
servo.set(steer_pin, cur_pos * (center max_right) / 1_000 + center)
pre_error := error
waitcnt(pre_cnt + clkfreq / 50)
This code runs once every 50th of a second, or 20 milliseconds. This is the period of the servo pulse,
so it gives the steering servo me to align itself. First the error is calculated as the dierence of the
readings from the ADC, corresponding to how far to the right of center the vehicle has dri ed. the
P term is propor onal to this error. The I term is an accumula on of this error. This behaves like
the integral control, because integra on is just a sum with very small change in me. The D term is
propor onal to the change in error from the previous me. Again, this acts like the real deriva ve
control. the current steering posi on, cur_pos, is then changed by the sum of the three parts,
scaled by the current speed. This is to prevent the car from over-steering at lower speeds. That
posi on is then mapped to real servo pulsed to drive the steering servo in the opposite direc on
of the error. This is not a PID controller, but it is in the PID family. This modied PID controller
allows the cars to follow the wire system.

4.2

Collision avoidance

The controller for steering above is great if there is only one vehicle on the road. But since we
have three, we need to be mindful of collisions. Here we use the front mounted rangender.
if (ping_ranges[0] > 2 * f_dist)
cur_speed := (cur_speed + accel) <# 1000
elseif (ping_ranges[0] > f_dist)
cur_speed := (cur_speed + (ping_ranges[0] f_dist) * accel / f_dist) <# 1000
elseif (ping_ranges[0] > f_dist / 2)
cur_speed += (cur_speed - (f_dist ping_ranges[0]) * 2 * accel / f_dist) #> -1000
13

else
cur_speed := -500
servo.set(esc_pin, cur_speed * (max_forward neutral) / 1_000 + center)
A following distance, f_dist, is specied in the constants deni on block. This corresponds to
our two-second rule. When the vehicle is clear of the following distance (range is greater than
twice the following distance) the car accelerates up to its maximum speed. When the vehicle
starts to get close to the vehicle in front it accelerates more slowly to avoid running down a
much slower vehicle by accelera ng too quickly. If the vehicle gets inside the following distance it
slows, decelera ng twice as fast as it accelerates on the other side of the following distance. This
should keep it right around the following distance. The next term relates to the panic state. If a
vehicle gets within half the following distance it applies the brake, stopping as fast as possible. If
a sta onary object is in the road the vehicle will come to a complete stop, and when it is removed
it will keep going. The two parts together represent the autonomous control of the vehicle. More
complex behavior is exhibited in the wireless communica on code, which allows the cars to react
to what other cars are doing, or are going to do.

4.3

XBee Wireless

With the controllers above running the vehicles can react to most things, but not everything. With
wireless communica on, however, extra scenarios can be accounted for. For example, when one
car is following another the gap can be maintained without communica on, but if one car slows
rapidly, say, for an exit ramp that gap will shrink quickly before expanding again. Intersec ons are
also dicult, because there are complex interac ons that need to take place, and sensing the cars
coming in the other direc on can be dicult. However, if the cars can announce their inten ons
to each other the process can become much simpler. The XBee Object from the OBEX also takes
care of API mode packet parsing, so the essen al features of the packetsthe data and who sent
itare the only things we needed to deal with. The rst part of the main loop is copied below:
if ( ctr < cnt clkfreq ) ' don't try to pair more than every second
if ( ping_ranges[0] >= clr_rng_f )
front_addr := -1
if ( ping_ranges[1] >= clr_rng_f )
rear_addr := -1
if ( front_addr == -1 or rear_addr == -1 )
xb.API_Str(BC_addr, string("seeking"))
ctr := cnt
seeking := ( front_addr == -1 or rear_addr == -1 )
In this part, the vehicle disassociates paired vehicles if they have moved out of range of the
PING)))s. This housekeeping is required so that it can pair with another vehicle that may come
14

up from behind or that it might catch up to5 . It then checks if it should search for other cars to
pair with. If it is not paired wither in the front or back then it ini ates pairing by broadcas ng the
message seeking. When other cars receive this message they will reply in a manner outlined in
the message recep on part of the loop. The next part of the loop, below, is a check for entering
the intersec on.
if ( chanval[0] < chanthresh and chanval[1] < chanthresh )
if ( in_intersection )
in_intersection~
else
in_intersection~~
xb.API_Str(BC_addr, string("intersection"))
As described in sec on about the track, there are notches built into the track just before and
just a er the intersec on. Here the induc ve sensors will not see much at all. The threshold
(chanthresh) is measured beforehand so that as long as the vehicle is over the wire at least one
sensor will be above that value. When the car enters the intersec on it announces to everyone
nearby that it does so. Again, the handling for this is in the message recep on part, which is where
we are now.
xb.API_RxTime(20)
if ( xb.RxIdent == $81 )
case xb.RxData
string("seeking"):
if ( front_addr == -1 and ping_ranges[0] < clr_rng_f )
neg_addr := xb.srcAddr
xb.API_Str(neg_addr, string("front"))
elseif ( rear_addr == 1 and ping_ranges[1] < clr_rng_f )
neg_addr := xb.srcAddr
xb.API_Str(neg_addr, string("rear"))
string("front"):
if ( seeking )
if ( rear_addr == -1 and ping_ranges[1] < clr_rng_f )
rear_addr := xb.srcAddr
xb.API_Str(rear_addr, string("pair_front"))
string("rear"):
if ( seeking )
if ( front_addr == -1 and ping_ranges[0] < clr_rng_f )
front_addr := xb.srcAddr
5

In our par cular test setup this cannot happen because there is only one lane, but for a more roust implementaon and for clarity it is here.

15

xb.API_Str(rear_addr, string("pair_rear"))
string("pair_front"):
if ( xb.srcAddr == neg_addr )
front_addr := neg_addr~~
string("pair_rear"):
if ( xb.srcAddr == neg_addr )
rear_addr := neg_addr~~
string("intersection"):
if ( xb.srcAddr <> rear_addr and xb.srcAddr <> front_addr and (not in_i
cur_speed -= 100
if ( rear_addr <> -1 )
xb.API_Str(rear_addr, string("slowdown"))
string("slowdown"):
if ( xb.srcAddr == front_addr )
cur_speed -= 100
if ( rear_addr <> -1 )
xb.API_Str(rear_addr, string("slowdown"))
The rst thing to do is actually receive the message. This is done with a 20ms meout so that the
loop doesnt get stuck innitely. For this exercise we only want to parse receive packets, which
are denoted by the hex value 0x81 in the API iden er block. The seeking message starts a
pairing process. The vehicle makes its best guess as to whether it is in front of or behind the
broadcas ng car, and sends that back to the car. If the origina ng car agrees with the decision it
sends back a pairing message. Then both cars take note of the address of the car in front of/behind
them. Now the cars can send each other important informa on. In this case that informa on is
slowdown. This allows the car to no fy the following vehicle that it is slowing down. When a
slowdown message is received the vehicle slows down, and sends the same message to the car
behind it. This will negate at least some of the rubber-banding that would happen without such
preemp ve ac on. The only message le , then, in the intersec on message. Receiving this
message means someone near you has entered the intersec on. A check is made to see if the car
in front (or behind) is the one who sent it, because there is no need to yield to that car. If not,
then a slowdown message is sent to keep from colliding in the center. These methods create a
complete system of autonomous vehicles.

Future Work

This system is just the start of the testbed. More complex behaviors can be programmed, and
specic scenarios can be constructed to test various aspects of driving. In our test trials we used
one lane and modeled an intersec on. For the future, an expansion to mul ple lanes in order to
simulate a highway is a clear area of interest. With mul ple lanes, dierent frequencies can be
used for each wire, allowing the cars to lock onto the lane they want. Mul ple lanes also opens

16

the door for much more complex behavior and trac analysis. Hand-in-hand with this is a more
complex wireless protocol with more commands and more sophis cated pairing procedures.
Adding in sta onary transceivers is also an area of future expansion. These can act as autonomous signposts, warning of trac or weather condi ons at an exit, for instance. More importantly, they can act as gatekeepers. They can moderate heavy intersec ons, or any intersec on
at all, allowing the vehicles to pass through without crashing.

Conclusion

The overall goal was to create a test environment for exploring concepts of autonomous vehicles
and trac control. Our system involves magne c roadways created by a signal generator and
magnet wire, vehicles with sensors for the roadway and for detec ng other vehicles/obstacles,
and a wireless point-to-point ad-hoc network that allows the vehicles to communicate with each
other. Using these parts as the test bed many concepts of trac control can be carried out, and
more can be learned about how to build a completely autonomous road network. The future
of driving is in this type of technology, and designing the test bed provided insight into just how
dicult it is to drive a car amongst other cars.

Cost Breakdown

Item
Propeller Proto Board
XBee Wireless Modem
PING))) Rangender
Solderless Breadboard and electrical components
Magnet Wire for Track
Signal Generator Electrical Components

Cost Per Item


Quan ty
Total
$25
3 (one per car) $75
$23
3 (one per car) $69
$32
6 (two per car) $192
$25 (approx) 3 (one per car) $75
$27
1 (2000 )
$27
$12 (approx)
1
$12
Total $450
We got our R/C car pla orms second-hand, so they cost us nothing. Searching around eBay
would get similar pla orms for around $35 each, bringing the total to around $555.

Work Responsibility Breakdown

Michael Jones
Wire Guidance Track Design
Signal Generator Circuit design and tuning
Induc ve Sensor Circuit design and tuning
Track Feature Design (How to test features of the autonomous network)
17

Small Car (H-drive) drive train


Op mal Chassis Sensor Layout and PING sensor calibra on
XBee radio se ngs for simula on
David Trethewey
Propeller Proto Board Design
ESC-Based Drive Train
Propeller programming
XBee packet state machine design
Shared
Overall design and construc on of vehicles
Non-wireless algorithms (sensors and drive control)
Feature Tes ng

References
Lego Wire Guidance Sensor Page - Phil Hurbain
h p://www.philohome.com/sensors/loguide.htm
Induc ve Guidance - AGV Electronics
h p://www.agve.se/page/by_desc/induc ve-guidance
XBee Manual - Digi Electronics
h p:// p1.digi.com/support/documenta on/90000982_B.pdf
XBee Parallax Tutorial - Parallax, Inc.
h p://forums.parallax.com/showthread.php?124213-Wireless-index
Propeller Manual v1.1 - Parallax, Inc
h p://www.parallax.com/Portals/0/Downloads/docs/prod/prop/WebPM-v1.1.pdf
Propeller Chip forums - Parallax, Inc.
h p://forums.parallax.com/forumdisplay.php?65-Propeller-Chip
MCP3202 Datasheet - Microchip, Inc.
ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf
NE555 Datasheet - Fairchild Semi
www.fairchildsemi.com/ds/LM/LM555.pdf
Futaba MC230CR ESC Manual - Futaba, Inc.
h p://manuals.hobbico.com/fut/futm0922-manual.pdf
L293Ne - Texas Instruments
h p://focus. .com/general/docs/lit/getliterature.tsp?genericPartNumber=l293&leType=pdf&track=no
18

Appendix - Propeller Code


The objects from the object exchange used are:
XBee_Object_2 XBee Transciever AT-API Object : h p://obex.parallax.com/objects/146/
Servo32v7 Servo32v7 : h p://obex.parallax.com/objects/51/
pwmAsm [modied] Dedicated pwm generator : h p://obex.parallax.com/objects/216
ADC_INPUT_DRIVER ADC Input Driver : h p://obex.parallax.com/objects/488

Modied pwmAsm
Modied to use both counters so two pwm signals come from one cog.
{A simple pwm object based on code from AN001 propeller counters
Author: Jev Kuznetsov
date : 16 Oktober 2007
Modified by David Trethewey, 24 Apr 2011
usage
OBJ
pwm : pwmAsm
....
pwm.start( Pin)
' start pwm
pwm.SetPeriod( period )
' set pwm period in clock cycles
pwm.SetDuty( duty)
' set duty in %
pwm.Stop
}
VAR
long cogon, cog
long aDuty
' order important (the variables are read from memory in this order)
long aPinOut
long aCtrVal
long sPeriod
long bDuty
' order important (the variables are read from memory in this order)
long bPinOut
long bCtrVal
PUB Start(aPin, bPin) : okay
'start pwm on Pin @ 80 kHz
longfill(@aDuty, 0, 8)
19

aDuty := 0
' default duty
aPinOut := |< aPin
aCtrVal := %00100 << 26 + aPin
sPeriod := clkfreq / 1000
bDuty := 0
' default duty
bPinOut := |< bPin
bCtrVal := %00100 << 26 + bPin
okay := cogon := (cog := cognew(@entry,@aDuty)) > 0
PUB stop
'' Stop object - frees a cog
if cogon~
cogstop(cog)
longfill(@aDuty, 0, 8)
PUB SetPeriod(counts)
' set pwm period in clock cycles, frequency = (_clkfreq / period)
sPeriod := counts
PUB SetDuty_a(counts)
if (counts < 0)
counts := 0
if (counts > 1000)
counts := 1000
aDuty :=counts*sPeriod/1000
PUB SetDuty_b(counts)
if (counts < 0)
counts := 0
if (counts > 1000)
counts := 1000
bDuty :=counts*sPeriod/1000
DAT
'assembly cog which updates the PWM cycle on APIN
'for audio PWM, fundamental freq which must be out of auditory range (period < 50S)
org
entry
mov
t1, par
'get first parameter
rdlong valuea, t1
add
t1, #4
rdlong pinOuta, t1
or
dira, pinOuta

' set pinOut to output


20

add
t1, #4
rdlong ctraval, t1
mov ctra, ctraval
lish counter A mode and APIN
add
t1, #4
rdlong period, t1
add
t1, #4
mov
par1, t1
rdlong valueb, t1

'estab-

add
t1, #4
rdlong pinOutb, t1
or
dira, pinOutb
' set pinOut to output
add
t1, #4
rdlong ctrbval, t1
mov ctrb, ctrbval
'establish counter B mode and APIN
mov frqa, #1
'set counter to increment 1 each cycle
mov frqb, #1
'set counter to increment 1 each cycle
mov time, cnt
'record current time
add time, period
'establish next period
:loop
rdlong valuea, par
'get an up to date pulse width
rdlong valueb, par1
waitcnt time, period
'wait until next period
neg phsa, valuea
'back up phsa so that it trips "value" cycles from now
neg phsb, valueb
jmp #:loop
'loop for next cycle
period
res 1
time
res 1
valuea
res 1
t1
res 1
pinOuta res 1
ctraval res 1
par1
res 1
valueb
res 1
pinOutb res 1
ctrbval res 1
21

Watch_All_Pings
' Watch_All_Pings.spin
' David Trethewey
'
' Object to ask each PING))) rangefinder what its current reading is
' asks each one in succession, according to the pins listed in pins[].
' Code does all of its work sequentially, and runs in one cog. Does not
' support more than eight rangefinders, because each reading could take
' up to 20ms, and it was easier to code that way.
VAR
byte cog
' Keep track of started cog
long pins
' Which pins to read (address)
long ranges
' ranges of said pins (address)
long mask
' Enables/Disables pins (address)
long stack[32]
' stack space for new cog
OBJ
ping : "Ping"
PUB start(pins_ptr, ranges_ptr, mask_ptr)
stop
cog := cognew(monitor(pins_ptr, ranges_ptr, mask_ptr) , @stack)
return cog
PUB stop
if (cog > -1)
cogstop(cog)
cog := -1
PUB monitor(pins_ptr, ranges_ptr, mask_ptr)| loop
pins := pins_ptr
ranges := ranges_ptr
longfill(@ranges, 0, 8)
mask := mask_ptr
repeat
repeat loop from 0 to 7
if (byte[mask] >> loop // 2)
long[ranges][loop] := ping.millimeters(long[pins][loop])
else
long[ranges][loop] := 0

22

Wire_Follow_h
Wire following code with collision detec on for the h-bridge, no communica on. There is also
one for the esc, but the changes are iden cal to the changes found in Wire_Follow_Xbee.
' Wire_Follow_H.spin
' David Trethewey
' powers the R/C car with the H-Bridge and makes it follow the wire, with collision detection
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
'Note Clock Speed for your setup!!
max_forward = 2_000
neutral = 1_500
max_reverse = 1_000
max_left = 1_500
max_right = 1_000
center = 1_250
left_a = 0
left_b = 1
right_a = 2
right_b = 3
accel = 20
DTPin = 19
INPin = 18
ClkPin = 17
RSPin = 16
Ping_f = 24
Ping_r = 25
f_dist = 500
'Distance in mm to stay behind vehicle
Kp = 500
Ki = 50
Kd = 10
OBJ
pwm : "pwmAsm"
adc : "ADC_INPUT_DRIVER"
pings : "Watch_All_Pings"
VAR
long chanstate[2]
23

long chanval[2]
long chanmax[2]
long chanmin[2]
long
long
byte
long
long
long

ping_pins[8]
ping_ranges[8]
ping_mask
cur_pos
cur_speed
stack[32]

PUB Main
ping_pins[0] := ping_f
ping_pins[1] := ping_r
ping_mask := %0001
pings.start(@ping_pins, @ping_ranges, @ping_mask)
adc.start_pointed(DTPin, INPin, ClkPin, RSPin, 2, 2, 12, 1, @chanstate, @chanval, @chanmax, @chanmin)
dira[left_b]~~
dira[right_b]~~
outa[left_b]~
outa[right_b]~
pwm.start(left_a, right_a)
cognew(WireFollow, @stack)
PRI WireFollow | pre_cnt, error, pre_error, P, I, D
cur_pos := 0
cur_speed := 0
pre_error := 0
repeat
pre_cnt := cnt
if (ping_ranges[0] > 2 * f_dist)
cur_speed := (cur_speed + accel) <# 1000
elseif (ping_ranges[0] > f_dist)
cur_speed := (cur_speed + (ping_ranges[0] f_dist) * accel / f_dist) <# 1000
elseif (ping_ranges[0] > f_dist / 2)
cur_speed += (cur_speed - (f_dist ping_ranges[0]) * 2 * accel / f_dist) #> -1000
else
24

cur_speed := 0
error := chanval[0] - chanval[1] * chanmax[0] / chanmax[1]
'channel 0: Right, channel 1: Left
P := Kp * error
I += Ki * error
D := Kd * (error - pre_error)
cur_pos := 1000 #> (cur_pos + cur_speed * (P + I + D) / 1_000_000) <# 1000
if (cur_speed > 0)
outa[left_b]~
outa[right_b]~
pwm.SetDuty_a((cur_speed * max_forward + cur_pos * max_left) / 1000)
pwm.SetDuty_b((cur_speed * max_forward + cur_pos * max_right) / 1000)
else
outa[left_b]~~
outa[right_b]~~
pwm.SetDuty_a(1000 (cur_speed * max_forward + cur_pos * max_left) / 1000)
pwm.SetDuty_b(1000 (cur_speed * max_forward + cur_pos * max_right) / 1000)
pre_error := error
waitcnt(pre_cnt + clkfreq / 50)

Wire_Follow_Xbee
Iden cal Wire_Follow_h_Xbee with changes as noted above.
' Wire_Follow_Xbee
' David Trethewey
' Uses servo controls to drive the esc controlled cars, and uses XBee to communicate with other cars.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
'Note Clock Speed for your setup!!
max_forward = 2_000
neutral = 1_500
max_reverse = 1_000
max_left = 1_500
max_right = 1_000
center = 1_250
25

esc_pin = 7
steer_pin = 6
accel = 20
DTPin = 19
INPin = 18
ClkPin = 17
RSPin = 16
chanthresh = 50
Ping_f = 24
Ping_r = 25
clr_rng_f = 3000
clr_rng_r = 3000
f_dist = 500
'Distance in mm to stay behind vehicle
XB_Rx
= 0
' XBee DOUT (pin 2)
XB_Tx
= 1
' XBee DIN (pin 3)
XB_RS
= 2
' XBee RS
(pin 5)
XB_Baud
= 38400 ' Communication Speed
BC_addr
= $FFFF
Kp = 500
Ki = 50
Kd = 10
OBJ
servo : "Servo32v7"
adc : "ADC_INPUT_DRIVER"
pings : "Watch_All_Pings"
xb : "XBee_Object_2"
VAR
long chanstate[2]
long chanval[2]
long chanmax[2]
long chanmin[2]
long
long
byte
long
long
long
long

ping_pins[8]
ping_ranges[8]
ping_mask
cur_pos
cur_speed
stack[32]
front_addr
26

long rear_addr
long neg_addr
byte in_intersection
PUB Main
ping_pins[0] := ping_f
ping_pins[1] := ping_r
ping_mask := %0011
pings.start(@ping_pins, @ping_ranges, @ping_mask)
adc.start_pointed(DTPin, INPin, ClkPin, RSPin, 2, 2, 12, 1, @chanstate, @chanval, @chanmax, @chanmin)
xb.start(XB_Rx, XB_Tx, 0, XB_Baud)
front_addr := -1
rear_addr := -1
neg_addr := -1
in_intersection~
cognew(transceive, @stack)
servo.start
servo.set(esc_pin, neutral)
servo.set(steer_pin, center)
cognew(WireFollow, @stack)
PRI WireFollow | pre_cnt, error, pre_error, P, I, D
cur_pos := 0
cur_speed := 0
pre_error := 0
repeat
pre_cnt := cnt
if (ping_ranges[0] > 2 * f_dist)
cur_speed := (cur_speed + accel) <# 1000
elseif (ping_ranges[0] > f_dist)
cur_speed := (cur_speed + (ping_ranges[0] f_dist) * accel / f_dist) <# 1000
elseif (ping_ranges[0] > f_dist / 2)
cur_speed += (cur_speed - (f_dist ping_ranges[0]) * 2 * accel / f_dist) #> -1000
else
cur_speed := -500
servo.set(esc_pin, cur_speed * (max_forward 27

neutral) / 1_000 + center)


error := chanval[0] - chanval[1] * chanmax[0] / chanmax[1]
'channel 0: Right, channel 1: Left
P := Kp * error
I += Ki * error
D := Kd * (error - pre_error)
cur_pos := 1000 #> (cur_pos + cur_speed * (P + I + D) / 1_000_000) <# 1000
if (cur_pos > 0)
servo.set(steer_pin, cur_pos * (max_left center) / 1_000 + center)
else
servo.set(steer_pin, cur_pos * (center max_right) / 1_000 + center)
pre_error := error
waitcnt(pre_cnt + clkfreq / 50)
PRI Transceive | seeking, ctr
repeat
if ( ctr < cnt clkfreq ) ' don't try to pair more than every second
if ( ping_ranges[0] >= clr_rng_f )
front_addr := -1
if ( ping_ranges[1] >= clr_rng_f )
rear_addr := -1
if ( front_addr == -1 or rear_addr == -1 )
xb.API_Str(BC_addr, string("seeking"))
ctr := cnt
seeking := ( front_addr == -1 or rear_addr == -1 )
if ( chanval[0] < chanthresh and chanval[1] < chanthresh )
if ( in_intersection )
in_intersection~
else
in_intersection~~
xb.API_Str(BC_addr, string("intersection"))
xb.API_RxTime(20)
if ( xb.RxIdent == $81 )
case xb.RxData
string("seeking"):
28

1 and

1 and

1 and

1 and

if ( front_addr == ping_ranges[0] < clr_rng_f )


neg_addr := xb.srcAddr
xb.API_Str(neg_addr, string("front"))
elseif ( rear_addr == ping_ranges[1] < clr_rng_f )
neg_addr := xb.srcAddr
xb.API_Str(neg_addr, string("rear"))
string("front"):
if ( seeking )
if ( rear_addr == ping_ranges[1] < clr_rng_f )
rear_addr := xb.srcAddr
xb.API_Str(rear_addr, string("pair_front"))
string("rear"):
if ( seeking )
if ( front_addr == ping_ranges[0] < clr_rng_f )
front_addr := xb.srcAddr
xb.API_Str(rear_addr, string("pair_rear"))
string("pair_front"):
if ( xb.srcAddr == neg_addr )
front_addr := neg_addr~~
string("pair_rear"):
if ( xb.srcAddr == neg_addr )
rear_addr := neg_addr~~
string("intersection"):
if ( xb.srcAddr <> rear_addr and xb.srcAddr <> front_addr \
and (not in_intersection) )
cur_speed -= 100
if ( rear_addr <> -1 )
xb.API_Str(rear_addr, string("slowdown"))
string("slowdown"):
if ( xb.srcAddr == front_addr )
cur_speed -= 100
if ( rear_addr <> -1 )
xb.API_Str(rear_addr, string("slowdown"))

29

S-ar putea să vă placă și