Autonomous Robot „rob01“

1. Introduction

This article gives a brief overview of my first robot project. Primary goal of the project was to build a small robot which is able to drive autonomous as well as remote controlled by a human. The result is rob01 which currently has the following features:

Drives autonomously, tries to avoid obstacles. To detect obstacles, two distance sensors are used. Also the average wheel turns are measured continuously to detect if the robot is stuck. The robot also provides a shell to which one could log in through a wireless connection. On the shell the internal robot parameters could be monitored or set, and the robot could be driven remote controlled.

Programming of the robot firmware was done completely by using open source tools. For compiling, the ARM gcc form the "Summon ARM Toolchain" is used, and as a kick start for accessing the hardware functionality, "libopencm3" is used.

2. Parts Used

The following table shows a more or less complete list of the parts used to build rob01:

Part Ref. Quantity
MCU STM32 Discovery 1
Chassis Arexx ARX-CH09 1
Battery 9V 2
Battery holder 9V 2
IR sensor Pololu QTR-1RC 2
Distance sensor Pololu GP2Y0D805Z0F 2
Dual motor driver Toshiba TB6612FNG 1
Wireless XBee PRO Znet 2.5 2
XBee explorer USB Make XBee conctable to PC 1
XBee adaptor for breadboard Make XBee conectable to 2,52 grid. 1
Voltage Regulator 5V LM7805 1
Voltage Regulator 3.3V LM1086 1
Capacitor C1 0.33uF 1
Capacitor C2 0.1uF 1
Capacitor C3+C4 10uF 2
LED PWR_LED1+PWR_LED2 3mm blue 2
Resistor R1 68 Ohm 1
Power Switches S1+S2 Two State Power Switches 2
Female connectors for STM32, XBee, TB6612FNG many

1. MCU

As an MCU, the STM32 Discovery, an ARM Cortex-M3 based CPU was used.The STM32 Discovery is available for a few bucks (about 12USD/Euro). It is way overkill for the purpose of building a small robot like this, but the intention was to learn more about the Cortex-M3. As a core library to access the STM32 hardware, the "libopencm3" was used.

2. Chassis

As a bsae for the chassis, the Arexx ARX-CH09 was used. It has two wheels, each with its own geared DC motor.Also a small extension plate to solder your own stuff on could be mounted. I did not use this plate since it is way to small to hold the STM32 plus the wireless XBee module puls the motor controller. Instead, I mounted a standard 160x100 plate on top of the chasis.

3. Batteries and Battery Holders

The ARX-CH09 also comes with a battery holder for four AA bateries. I decided not to use this because of two reasons: First I wanted to use separete power for MCU and motors (since the motors are likely to producre noise on the power supply). And secondly I wanted to use a regulator for the power supply to avoid the motors becoming slower when then battery voltage decreases. Thus, I replaced the Arexx battery holder with two holders for 9V battery packs, which barely fit on the lower level of the chasis.

4. Sensors for Obstacle Detection

For obstacle detected, two bumper sensors (Pololu GP2Y0D805Z0F) are used. They use IR reflection to detect an obstacle in the range of approximately 10cm. When an obstacle is detected, the data line is set to high. It is not possible to measure the distance with this sensors. The sensors are mouted on the chasis base plate on the left and right front.

5. Sesnsors for Odometrie

To be able to count the wheel turns, two more reflectance sensors are used (Pololu QTR-1RC). They use IR light to detecte how much the surface of an object in the range between 0.2cm and 3cm reflects the light. By having one of the gears black colored markers, the transitions between balck-white and white-black could be counted.

6. Dual Motor Driver

To control the two DC motors, the Toshiba TB6612FNG dual motor controller form Toshiba is used.

7. Wireless

To remotely get informations from the robot and control it, I decided to equipe the robot with a XBee module from Digi. On the PC side, an other Xbee module is needed. Basically the XBee is useed to set up an over the air USART connection, which could be achieved very easlily through XBee. Also it has to be mentioned, that XBee is again overkill for this purpose.

8. Voltage Regulators

Two voltage regulators are needed. One for the MCU power supply, and an other one for the motor power supply. The STM32 MCU gets powerde by 5V, thus, the 7805 is used to regulate the 9V batery power. The motors need something about 3V, thus the 1086 is used to regulate an other 9V batery down to 3.3V. For decoupling, also some capacitors (C1-C4) are needed. To get feedback if the power is switched on, two blue LEDs (PWR_LED1, PWR_LED2) are used. On the 5V power supply, also a resistor (R1) needs to be set in front of the LED.

9. Female Connectors

For not directely soldering the MCU, the XBee and the motor driver, I decided to use a bunch of female connectors to make them pluggable. Also I used a connector to make the motors and sensors pluggable (since I want the top level to be detachable, and the sensors reside on the lower level of the robot).

3. Building the Robot

1. The Hardware

I tried to start step by step when building the robot (which is always a good approach). I took the single componets, an tried to get them working on a bread-board. First without the MCU (e.g. for the motor driver), then with the MCU (e.g. for the motor driver). By doing so, it is more easy to tell where the problem resides if something goes wrong. If the setup without the MCU works, the failure may be in the connection to the MCU, ore in the software. On the other hand, one needs not to star writing software, if the hardware by itself doesn't work properly.

Testing the power supply

Testing the power supply

Testing the TB1266FNG on the robots back

Testing the TB6612FNG on the robots back

Preparing the QTR-1RC wheel sensor

Preparing the QTR-1RC wheel sensor - front

Preparing the QTR-1RC wheel sensor

Preparing the QTR-1RC wheel sensor - back

Wheel sensors mounted

Wheel sensors mounted

First try of a mark for the sensor on the grear

First try of a mark for the sensor on the grear

Testing the wheel sensors

Testing the wheel sensors

After a while I ended up with the whole robot setup on two bread-boards, and a rough sketch of the firmware which let me control the basic driving functions remotely.

I managed to put the bread-boards on the robots back, and then had a bread-board which I could drive around. With this bread-board robot, I developed the basic firmware for the autonomous mode.

The bread-board intermediate allowed me to easily make fixes and changes to the hardware in an early stage. At the moment when I realized, that things seem to stabilize, I soldered the hardware stuff to a 160x100 plate.

2. Schematic

The picture below shows the whole schematic for rob01. It also includes an JTAG connector, since I prefer JTAG over ST-Link for programming the STM32 Discovery.

robo01 schematic

rob01 schematic

3. The Software

The firmware for the robot is roughly build up by the layers shown blow:

 | Shell                          |
 +-----------------------------+  |
 +---------------------------+ |  |
 | Drive Control             | |  |
 +---------------------------+ |  |
 +---------+ +----------+ +----+  |
 | Sensors | | Actors   | | Shell |
 +---------+ +----------+ +-------+
 | Devices                        |
 | Hardware / libopencm3          |

Note: doxygen generated documentation is available online her.

1) Hardware / libopencm3

To access basic functions, like GPIO, USART etc., of the STM32 hardware, libopencm3 is used.

2) Devices

Each device provided by the hardware is represented by it own device implementation. Currently the following devices are defined:

Device Description
bumpersensor The GP2Y bumersensors for obstacle detection.
button The (build in) push button to switch btw. autonomous and RC mode.
dcmotor The DC motors driven by the TB6612FNG
led The (build in) status LEDs (currently not used).
refsensor The QTR reflection sensors used to count wheel turns.
serial The USART used for the shell.

Each device defines a C structure which holds all the needed information to initialize and operate a device. E.g. the structure for the DC motor looks like this:

typedef struct {
      * Port 1 for motor control. E.g. GPIOC.
     uint32_t   port_ctrl1;

      * Pin 1 for motor control. E.g. GPIO4.
     uint16_t    pin_ctrl1;

      * Port 2 for motor control. E.g. GPIOC.
     uint32_t   port_ctrl2;

      * Pin 2 for motor control. E.g. GPIO5.
     uint16_t    pin_ctrl2;

      * Port on which PWM for speed control is connected to H-Bridge. E.g. GPIOC.
     uint32_t   port_pwm;

      * Pin on which PWM for speed control is connected to H-Bridge. E.g. GPIO6.
     uint16_t    pin_pwm;
} device_data_dcmotor;

Also the operations to perform on a device are defined. Again for the DC motor it looks like so:

 * Make the DC motor turn clock-wise.
 * @param[in]   *dev    device representing the DC motor
void dcmotor_cw(device *dev);

 * Make the DC motor turn counter-clock-wise.
 * @param[in]   *dev    device representing the DC motor
void dcmotor_ccw(device *dev);

 * Make the DC motor break (by shortening the moters power supply).
 * @param[in]   *dev    device representing the DC motor
void dcmotor_break(device *dev);

 * Make the DC motor float.
 * @param[in]   *dev    device representing the DC motor
void dcmotor_float(device *dev);

Then within a board definition those structs are filled with concrete values, and passed to the board init function. This function calls for each device the corresponding init function. Thus it is possible to easily support different ports where the devices may be connected to different PINs. For the DC motors, connected to the STM32 board, this looks like shown below:

// Define the DC motors
device_data_dcmotor dcmotor_left_data = {
     .port_ctrl1        = GPIOB,        // AIN1 @ PA5
     .pin_ctrl1         = GPIO5,
     .port_ctrl2        = GPIOC,        // AIN2 @ PC12
     .pin_ctrl2         = GPIO12,
     .port_pwm          = GPIOC,        // PWMA @ PC11
     .pin_pwm           = GPIO11,

device_data_dcmotor dcmotor_right_data = {
     .port_ctrl1        = GPIOB,        // PIN1 @ PB7
     .pin_ctrl1         = GPIO7,
     .port_ctrl2        = GPIOB,        // PIN2 @ PB8
     .pin_ctrl2         = GPIO8,
     .port_pwm          = GPIOB,        // PWMB @ PB9
     .pin_pwm           = GPIO9,

device dcmotor_left = {
     .id        = "DC_MOT_LEFT",
     .rcc_reg   = &RCC_APB2ENR,
     .data      = (device_data *)&dcmotor_left_data,
     .init      = dcmotor_init,

device dcmotor_right = {
     .id        = "DC_MOT_RIGHT",
     .rcc_reg   = &RCC_APB2ENR,
     .rcc_en    = RCC_APB2ENR_IOPBEN,
     .data      = (device_data *)&dcmotor_right_data,
     .init      = dcmotor_init,

All devices are then "collected" in the board device struct, so they could be passed to the board init function later on:

// All my board devices
board_devices my_devices = {
     .size = 10,
     .device_array = {

It is intended to later on expand that concept, and use the firmware to not only support one specific robot, but many (yes, work for rob02 is already in progress)

3) Sensors

Above the device layer, the sensors layer is located. This layer further more abstracts form the underlaying hardware to a "business" function. The following sensors are currently known by the firmware:

Sensor Description
bumper The front left/right bumper sensors. The bumper devices are used here.
odometer The odometric sensors for counting wheel turns. The refsensor devices are used here.

The values for the sensors are aquired within the systick interrupt, and then written into a global sensor values struct. They are used later on by the drive control layer. For the odometric sensors also the PPM is calculeted. PPM stands for "Pulses-Per-Minute". Each transition form the balck to the white area and vice versa on the gear gives a "pulse". By calculating the PPM, the drive control later on could determine, if a wheel is getting slugish or stuck. Since aquiring of the sensor values is done within the systick interrupt, the main application needs not to care about driving the sensors.

4) Actors

Beside the sensors, there is one actor: the vehicle. This actor is made up by the two DC motor devices. The vehicle actor has operations to move into its various directions, turn around or break. By introducing a vehicle actor, which takes directions, the drive control layer later on has not to care if the vehicle uses two DC motors, four DC motors, servos etc.

5) Drive Control

The drive control layer brings together the sensors and actors, and implements the autonomous mode for the robot. It consists of two main routines: "find path" and "follow path". The first one decides based on the aggregsted sensor values which direction to follow next (and for how many wheel turns). The second one then tries to execute that path. Follwing a path could be aborted at any time when a new obstacle is detected within the path currently followed. If path executing is aborted, "find path" is called again to determine a new direction to follow. The very simple path finding algorithim looks like this:

void drive_control_find_path(path_segment *ps)
     ps->count = 2;

     int stat = drive_control_agregate_sensors(1);

     switch(stat) {
          ps->direction = VEHICLE_DIR_NONE;
          ps->direction = VEHICLE_DIR_BACKWARD;
          ps->count = 3;
          ps->direction = VEHICLE_DIR_FORWARD;
          ps->count = 0;
          ps->direction = VEHICLE_DIR_TURN_RIGHT;
          ps->direction = VEHICLE_DIR_TURN_LEFT;
          ps->direction = VEHICLE_DIR_TURN_RIGHT;
          ps->direction = VEHICLE_DIR_TURN_LEFT;
          ps->direction = VEHICLE_DIR_FORWARD;
          ps->count = 0;

Note: "ps" is the path segment structure which defines the next path segment to follow. It consists of a direction, and a wheel count value. A count of zero means: follow this directin until an obstacle is detected.

The sesnsor aggregatino takes the current values of the bumper sensors, and the PPM of the wheels and tries to fill an integer with flags for each direction an obstacle is detected. E.g. if the right bumper sensor sees an obstacle, and the left wheels PPM is below the minimum PPM (it is stuck), the flags for "obstacle right" and "obstacle left" are set.

By using the PPM, it is possible to detect, if the vehicle crashed on an obstacle not seen by the bumper sensors (e.g. while driving backwards).

6) Shell

The topmost layer is the shell. The shell could be reached through a serial line. In the case of rob01 the serial line is "arifiyed" by a XBee module. Thus, one could log into the robot at any time by using a simple serial terminal. The shell offers some commands to drive the robot manually, to query the sensor values or to switch from/to autonomous mode. Since the USART receiving is done interrupt driven, the main application loop needs not to care about handling shell events. I also started to build a counter part for the shell, the ncurses based "RobotControlRC", which allowed one to e.g. use the arrow keys to move the robot, but the firmware advanced, while the "RobotControlRC" did not (eventually I will update RC one day).

ncurses based remote control

ncurses based remote control

Beside the "normal" firmware I build a special test firmware which includes a test shell. This tesht shell includes commands to check the functionality of all the build in hardware, and software. It comes in handy, when plugging together the hartware, and one wants to make sure, the left bumper is really the left bumper, and clock-wise really turns the motor clock-wise.

4. Conclusions

Buling a robot for sure is one of the most fun ways to learn more about microcontrollers and electronics. A fairly functional autonomous robot could be build up from widely avilable and cheap parts. There is no need to by a complete (build up) kid for more (beside it would take out most of the fun).

The only really expensive part on rob01 are the XBee modules. They are easy to use, but overkill (no mesh networking, no device profiles etc. are needed). I definitely will replace XBee with somethings cheaper (e.g. one of this Nordic 2.4 GHz receivers) next time.

Also the Arexx platform is a great starter, one should not expect to much from it. The motors and gears are not very accurate, the vehicle barely manages to follow a straight line. The bumper sensors used are very easy to handle, but with a range of only 10 cms they really do not "see" much. Thus using a ultrasonic range finder on a pan-tilt unit would be definitely the better choice.

Trying to detect if the wheels are stuck turned out to be not that easy and releayable. I got it working for a certion floor surface, but I thing on an other surface it may fail. This concept really has to be thought over.

Working with libopencm3 turned out to be a very usable open source alternative to the STM standard library. It is fairly easy to use, and lightweight. The library comes with a bunch of more or less good examples. The only down-side about the libopencm3 is, that there is not much documentaiton available on the net.

So, on to rob02 🙂

5. More Images


Note: both archives include the schematic plus the doxygen generated docs.

5 Comments to Autonomous Robot „rob01“

  1. isra's Gravatar isra
    17. Januar 2012 at 16:51 | Permalink

    I tray to download the project source and I fond there only the firmware
    can you PLZ send me your proj sourc file?
    Your proj look very instructing and interesting!!
    thank you in advance.

  2. eric's Gravatar eric
    18. Januar 2012 at 20:13 | Permalink

    That’s aweseome!
    I know whats going to be my next project.

    PS: You should give your robot an outfilt like this:

Leave a Reply

You must be logged in to post a comment.