algorithm Arduino hardware software

Basic SmartCar Bot – Control

When designing a SmartCar application, the details of the application (ie, what the robot does) will be purpose-specific. The underlying support infrastructure, however, should be more generic and aimed at simplifying management of the core hardware from the application.

In the previous parts of this series we covered the hardware and sensors. The next thing is to work out the core controls needed for an application using the hardware.

The core library, configuration, setup and application sketches referenced in this series of articles are available in my code repository as the MD_SmartCar library.

Control Strategy

The basic control blocks that need to be considered are shown in the figure below.

Basic Control Blocks

The SmartCar application controls its overall behaviour. For example, this could be following a wall, following a line, or moving towards darkness or light. The behaviour control uses sensors to detect its external environment and may display status and other information (or take commands) from a user interface.

Motion control receives directional commands from the Behaviour Control block and coordinates the two motors to achieve the required outcome, through individual motor control blocks and feedback on how the motors are running.

Clearly, while the Behaviour Control application is specific to the SmartCar’s task, the Motor Control and its downstream blocks are the common enabling core for the SmartCar. So how do we control those?

I decided to design the core SmartCar controls to enable it to reliably perform two types of autonomous motion:

  • Precisely controlled movements (eg, spin in place), where the ability to
    manoeuvre the vehicle at low speed is important. Independent control of motor directions and how far they spin are the control parameters for this type of movement. The motion is purely controlled by the motor encoder pulses or, for stepper motors, by setting the number of pulses.
  • General movements (eg, traveling at a set speed in a set direction), where the ability to move at-pace on an accurate path is important. This type of movement is commonly managed using the unicycle model for differential motor control, described below. The speed for each motor is regulated by a PID loop with feedback from the motor encoder, or, for stepper motors by setting the motor pulse rate.

The Unicycle Control Model

Working out the displacement and velocities of each wheel on a differential drive robot can be messy.

The unicycle model of an autonomous robot is a kinematic model that allows modeling the movement of the vehicle as if it was a unicycle, using a linear velocity vector (V) and a rotational velocity (ω) about a point within the vehicle. Taking this point to be midway between axis joining the 2 wheels simplifies the calculation.

Kinematic equations translate between the unicycle model and our wheel velocities. Steering requires each of the independent wheels to rotated at different speeds (VL and VR for the left and right side) to travel the equivalent unicycle path.

So specifying a movement path using this unicyle abstraction becomes much easier as we need to just specify “How fast do we want to move forward and how fast do we want to turn”, letting the mathematics work out the wheel rotations.

V and ω are transformed into independent motor speeds for the left and right motor (VL, VR) using the following formulas:

  • VL = (2V + ωB) / 2r
  • VR = (2V – ωB) / 2r

where B is the vehicle base length (ie, the distance between the wheel centerlines) and r is the radius of the wheel.

In the MD_SmartCar library the direction conventions used are shown in the diagram on the left.

  • Linear velocity V is positive for forward motion, negative backwards.
  • Angular velocity ω is positive for right rotation, negative for left.

The drive() method of the library sets and controls this type of motion.

Precise Movement

Precision moves are invoked by the move() and spin() methods of the library.

Precise movement is all about moving to position the SmartCar in a slow(er) and more controlled manner than free running. While they can be stand-alone single moves, it is more likely that precision moves are linked in a sequence. Choreographing the timing for each element of the maps to managing states in a Finite State Machine (FSM).

To simplify the implementation of simple sequential FSM-type moves the library introduces a concept of action sequences – a way of defining sequential actions that the library will execute to move the vehicle (ie, a ‘recipe’ for movement).

They save time in not having to program common combinations of actions and monitoring each completion in the application sketch. The library implementation executes the action sequence in the background freeing the application to focus other higher priority tasks.

An simple example is when a front bump switch detects a collision, triggering the sequence of evasive actions defined by:

  1. stop the vehicle
  2. pause a short while
  3. reverse away from the obstacle
  4. pause a short while
  5. spin to another direction
  6. resume normal vehicle motion

These actions can be defined in a list of actions to be performed and passed to the library for execution:

static const PROGMEM MD_SmartCar::actionItem_t seq[] =
  { MD_SmartCar::STOP },
  { MD_SmartCar::PAUSE, 300 },
  { MD_SmartCar::MOVE, -PI, -PI },
  { MD_SmartCar::PAUSE, 300 },
  { MD_SmartCar::SPIN, -25 },
  { MD_SmartCar::END }

The action sequence is defined as an array of MD_SmartCar::actionItem_t records. Each record contains the action to be performed and the parameters relevant to that action, as documented in the library. The last record in the array must always be the END action or the library will continue reading random memory beyond the end of the sequence.

Sequences may be completely predefined, allowing them to be stored in static memory (PROGMEM) to preserve dynamic RAM, or they may be built and/or modified ‘on the fly’ in RAM.

DC Motor Control and PWM Frequency

When using PWM for motor control the assumption is that motors will operate ideally as if they were connected to a pure DC power source. This is not a valid assumption.

A brushed DC motor’s internal rotor consists of two or more wire coils wound around magnetic core material. This makes the motor act like an inductor, and it takes a few milliseconds for energy in the coils to build enough to turn the shaft.

So, if we are turning power on and off quickly (as we do with PWM speed control), the rotor coil inductance becomes an important consideration. The rotor coil needs enough time to build energy to spin the motor, and this is governed by the PWM frequency. ‘Too high’ PWM frequencies change the supplied voltage too quickly to start the motor spinning.

Lower PWM frequencies allow the inductive coils to build up more energy from a PWM pulse, and the motor will start spinning sooner (at a lower equivalent voltage) and operate with higher torque at low speeds.

I recommend this very informative summary from Adafruit on the effect of frequency on PWM motor control. They have taken the time to experimentally map out motor curves for various hobby motors. Their conclusion is a PWM frequency of at most 300Hz is needed for most hobby DC motor control.

The default Arduino Uno/Nano PWM frequency is 490.2Hz for pins 3, 9, 10, 11 and 976.56Hz for pins 5 and 6. These frequencies are actually too high to properly drive DC motors at low duty cycles.

To allow me flexible control of the PWM frequency and PWM on any I/O pin (see this previous article) I wrote the MD_PWM library. MD_SmartCar uses this library to set the PWM frequency at 60Hz. This frequency considerably improved the performance of the TT motors running the SmartCar, allowing it to run satisfactorily over a wider range of speeds.

The next part in the series will cover how to configure and tune the MD_SmartCar library before implementing a random-rover bot application.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s