Robot using IMU for Control
Purpose
Use the IMU to correct steering on the Sparkfun magician robot. Moreover, IR sensors will be used to avoid and navigate around obstacles.
Components
- Sparkfun magician robot kit
- Solderless breadboard
- Mbed LPC1768 chip
- Pololu MD08A H-Bridge breakout
- Sparkfun LSM9DS0 9-axis IMU breakout
- IR distance sensor with filters (3x)
- AA battery (4x)
Background
Pictures
Sparkfun Magician Bot Front View

Sparkfun Magician Bot Top View

Pinouts
| Mbed | H-Bridge | Motor |
|---|---|---|
| p21 | PWMB | |
| p22 | BIN2 | |
| p23 | BIN1 | |
| p24 | AIN1 | |
| p25 | AIN2 | |
| p26 | PWMA | |
| VOUT | VCC, STBY | |
| VIN | VMOT | |
| GND | GND | |
| AO1 | Right Motor + | |
| AO2 | Right Motor - | |
| BO1 | Left Motor + | |
| BO2 | Left Motor - |
| Mbed | IMU |
|---|---|
| p9 | SCL |
| p10 | SDA |
| VOUT | VDD |
| GND | GND |
| Mbed | IR Filter |
|---|---|
| p18 | Left IR AIN |
| p19 | Right IR AIN |
| p20 | Center IR AIN |
| VIN | +5V |
| GND | GND |
Code
#include "mbed.h"
#include "motordriver.h"
#include "LSM9DS0.h"
/*/////// PID control gains
/ These values need to be adjusted in accordance with the individual
/ actuators (motors) either by trial and error or computation. The information
/ here should help: http://developer.mbed.org/cookbook/PID
*////////
#define RATE 0.1
#define Kp 0.0
#define Ki 0.0
#define Kd 0.0
// SDO_XM and SDO_G are pulled up, so our addresses are:
#define LSM9DS0_XM_ADDR 0x1D // Would be 0x1E if SDO_XM is LOW
#define LSM9DS0_G_ADDR 0x6B // Would be 0x6A if SDO_G is LOW
// peripheral objects
DigitalOut f(LED1); // forward
DigitalOut b(LED2); // backward
DigitalOut l(LED3); // left
DigitalOut r(LED4); // right
//See http://mbed.org/cookbook/Motor
//Connections to dual H-brdige driver for the two drive motors
Motor left(p21, p22, p23, 1); // pwm, fwd, rev, has brake feature
Motor right(p26, p25, p24, 1);
AnalogIn ir_c(p20); // center IR sensor
AnalogIn ir_l(p19); // left IR sensor
AnalogIn ir_r(p18); // right IR sensor
LSM9DS0 imu(p9, p10, LSM9DS0_G_ADDR, LSM9DS0_XM_ADDR); // imu sensor
Serial pc(USBTX, USBRX); // usb serial for debugging
// function prototypes
void setup();
void initIMU();
float getHeading();
float updatePID();
// global variables
int count; // counter for IR
float initHeading; // initial heading
float calcHeading; // calculated heading
float heading[1]; // index 0 = goal, index 1 = current
float headingAvg[100];
float sum;
float previousError; // previous error
float pidError; // error
float steadyError; // steady-state error
float P; // proportional error
float I; // integral error
float D; // derivative error
float dt; // update frequency
float kp; // proportional constant
float ki; // integral constant
float kd; // derivative constant
float output; // PID controller output
float motorSpeed1; // motor speed for left motor
float motorSpeed2; // motor speed for right motor
// function implementations
void setup()
{
// Use the begin() function to initialize the LSM9DS0 library.
// You can either call it with no parameters (the easy way):
uint16_t status = imu.begin();
//Make sure communication is working
pc.printf("LSM9DS0 WHO_AM_I's returned: 0x%X\n", status);
pc.printf("Should be 0x49D4\n\n");
}
// obtains the initial heading upon bootup
void initIMU()
{
imu.readMag();
initHeading = imu.calcHeading();
heading[0] = initHeading;
heading[1] = initHeading;
}
// obtains an updated heading
float getHeading()
{
imu.readMag(); // reads current value
calcHeading = imu.calcHeading(); // sets current value
//pc.printf("Compass Heading (in degrees): ");
//pc.printf("%2f\r\n",calcHeading);
//pc.printf("%2f\r\n",heading[0]);
return calcHeading;
}
// pid controller
float updatePID(float current, float target, float dt)
{
// calculate difference between actual and goal values
pidError = target - current;
if (pidError < -270) pidError += 360;
if (pidError > 270) pidError -= 360;
// accumulates error over time
steadyError += pidError*dt;
// integrator windup compensation (saturates to actuator's output)
if (steadyError < -10.0) steadyError = -10.0;
if (steadyError > 10.0) steadyError = 10.0;
P = kp*pidError; // proportional error
I = ki*steadyError; // integral error
D = kd*(pidError - previousError)/dt; // derivative error
// save current error
previousError = pidError;
float pidOutput = P + I + D; // calculate the PID output
pidOutput /= 100.0; // scale it down to get it within the range of the actuator - probably needs tuning
if(pidOutput < -0.1) pidOutput = -0.1;
if(pidOutput > 0.1) pidOutput = 0.1;
wait(dt);
//pc.printf("P = %f | I = %f | D = %f | Output = %f\n",P,I,D,pidOutput);
return pidOutput;
}
int main()
{
// initialization functions
setup();
initIMU();
// variables
dt = RATE;
kp = Kp;
ki = Ki;
kd = Kd;
motorSpeed1 = 0.4;
motorSpeed2 = 0.4;
while(1) {
if(ir_c > 0.25f) { // Turn around if about to hit an obstacle
f = 0;
b = 1;
l = 1;
r = 0;
while(count < 50) {
left.speed(0.5);
right.speed(-0.5);
wait(0.02);
count++;
}
count = 0;
heading[0] = getHeading(); // updates new goal heading
} else if(ir_l > 0.25f) { // Turn right if about to hit an obstacle
f = 0;
b = 0;
l = 1;
r = 0;
while(count < 20) {
left.speed(0.5);
right.speed(-0.5);
wait(0.02);
count++;
}
count = 0;
heading[0] = getHeading(); // updates new goal heading
} else if(ir_r > 0.25f) { // Turn left if about to hit an obstacle
f = 0;
b = 0;
l = 0;
r = 1;
while(count < 20) {
left.speed(-0.5);
right.speed(0.5);
wait(0.02);
count++;
}
count = 0;
heading[0] = getHeading(); // updates new goal heading
} else { // Moves forward indefinitely
f = 1;
b = 0;
l = 0;
r = 0;
// average the headings to elliminate noise (LPF)
// possibly needed to help smooth out sensor noise
/*
for(int x = 0; x < 100; x++) {
headingAvg[x] = getHeading();
sum += headingAvg[x];
}
heading[1] = sum/100;
*/
// updates new pid values
heading[1] = getHeading();
output = updatePID(heading[1],heading[0],dt);
// updates new motor speeds
motorSpeed1 += output;
motorSpeed2 -= output;
// set motor speeds
left.speed(motorSpeed1);
right.speed(motorSpeed2);
}
}
}
Name of your Video
Please log in to post comments.
