Balancing robot for dummies

Hi BrianBot,
Welcome and good luck for your project :slight_smile:

Looking at your shopping list, I have the following comments:
motor controller shield:
This device will definitly not cope with the amperes needed to feed your motors :-?
Look here (initial post)

motors & wheels:
The wheels are wide and soft, which is good for balancing
Wheel diameter could be higher
You need encoders (at least one). How will you afix them ??

My instructor seems to think there will be problems with different torques of each motor.
How much validity is in that?

This is addressed with the second encoder

Feel free to document you project here
This thread is now very visible from Google, this is convenient for discussion with non Arduino jerks

I was denied permission to use shields, anyway, as it was declared "too easy". I suppose that's convenient.

I got my hands on a free L298N H-Bridge: http://www.st.com/stonline/books/pdf/docs/1773.pdf
Unfortunately it only can handle spikes of 3 A (repetitive 2.5 A). Did you ever get an idea of what currents are used? I plan on making the bot relatively small...

Also, have you ever run into speed problems from the microcontroller? Is 16 MHz fast enough?

My understanding was that the sensors act as the feedback to tell the motors direction and speed, so a motor encoder is not necessary. I see you made the same statement in your initial post.

Check out the "murata girl" a unicycle robot that balances forward/back and left/right. It is at the 1:35 mark on the youtube video.

I'm trying to use the Kalman code described in this thread. When the angle of my bot changes rapidly, the filter can take 10 cycles to settle. In other words, the computed angle lags the accelerometer angle by as much as 10 cycles. This is making my bot slow to respond. Is this lag normal? Is there anything I can do to speed up the response?

Thanks!
Osman

@osmaneralp

I had something simulare but i found that I had the wrong value when setting the scaling of the gyro rate. Check in the data sheet for you sensor what the sensitivity is for mV/(deg/sec)

int getGyroRate() {                                             // ARef=3.3V, Gyro sensitivity=2mV/(deg/sec)
  return int(sensorValue[GYR_Y] * 4.583333333);                 // in quid/sec:(1024/360)/1024 * 3.3/0.002)
}

Also it's important that you have a good value on the ACC_Z and it should be equal when having it standing as up side down...

For ACC_Z, gravity correction = (0° value - 180° value)/2

2 – Zeroing sensor
Before aquiring data in the loop, sensors should be zeroed
This means that when the bot is stricly still and vertical, sensors should read “0[ch8243]
except for the vertical axis (Acc_Z), which is sensing gravity (1g)
Zero mesurement is performed in setup:

void calibrateSensors() {                                 // Set zero sensor values

long v;
 for(int n=0; n<3; n++) {
   v = 0;
   for(int i=0; i<50; i++)       v += readSensor(n);
   sensorZero[n] = v/50;
 }
 sensorZero[ACC_Z] -= 102;
}



calibrateSensors() is a one off action, so we have time to average 3 X 50 measurements
Finally, gravity value is deducted to Acc_Z
For ADXL330/335: 1g = 330 mV (+- 10%)
ACC_Z correction for 10 bit ADC and 3.3V AREF: 330/3300*1024 = 102 (to be fine tuned later on)

Hope you find the problem.. What sensor are you using?

I have now gotten my BlueSmirf and the other parts I was missing to be able to complete my bot.

I tried the wireless communication at 115200 bps and it worked right out of the box. Very happy with it and maybe now I can finalize the bot and the Balancing Bot GUI 2.0..

I have gotten the speed of the communication between the bot and GUI to a acceptable level. I have split ed the data in five groups and each group is sent each fifth loop..

I will post some images and a video on the interface and robot in the end of the week hopefully.. :-/

@Patrik

Thanks for the suggestion. I'll look more closely at the gyro. I'm using an LPY510AL connected to a MCP3302 13-bit a2d converter. I went with an external a2d to try to get more accuracy. Whether or not I need the extra resolution is not yet clear. I can explain how to connect the a2d to get all 13 bits of resolution if anyone's interested.

I'm using the 4X out instead of the 1X out from the gyro. Anyone know if this is a good idea? The 4X out gives 10mv/deg/sec. The a2d has 8096 units in a 2.48v range. That's about 0.3mv/unit. So each a2d unit represents 0.03 deg/s or 0.085 quid/s. This is quite different from the 4.58 quid/s used in this thread. Does this mean I will have to change the kalman matrix parameters? :-/

The accelerometer is built into the Chumby that also contains the ARM processor which is the brains of my bot. I'm getting good symetrical readings from the acc, so I don't think that's my problem.

Here's a picture of my bot:

--Osman

So cute! And it's blue! I love it!

Guys, I have a question, since I won $40 loyalty from SF, I want to get a new IMU board. Question is: should I get the recommended 5DOF IMU ($44.95) OR should I get the new 6DOF IMU that uses I2C ($64.95)?
The advantage of the last one being freeing the analog pins so I can use pots to fine tune the PID and perhaps a better accuracy. But is I2C fast enough in this case? Also the acc-meter has 2 interrupt output pins that you can program to trigger when falling too fast or when new measurement is ready, etc.

@osmaneralp

I'm using a similar sensor I think and it's running in 4X mode. I got the following gyro rate as below but then I use a Arduino analog port directly.

I'm using a nother GYR called LPR510AL and that one has a sensitivity of 10 mV/(°/s) at ±100°/s. ( and it's running in 4X mode)

It looks like I have made a miss calculation where it should have been:

// ARef=3.3V, Gyro sensitivity=10mV/(deg/sec)
// in quid/sec:(1024/360)/1024 * 3.3/0.010)
return int((sensorValue[GYR_Y] * 0,9166679)*-1);

Measurement range: ±100°/s and ±400°/s
Sensitivity: 10 mV/(°/s) and 2.5 mV/(°/s)

@Ro-Bot-X

I know to little about the SPI buss but I love the idea to getting the analog ports free. If you buy it and it works good I will defiantly go for one later on to..

@kas
@osmaneralp
@Ro-Bot-X
@GuzZzt

I would like to have a page on the projects homepage with some example robots that has used this project to be able to build a balancing robot or something else...

If you feel that this project had helped you with your bot could you send me a link to a homepage or a build blog there you describe your bots and what components you have used. With some images and videos etc.

Otherwise send some text and the images and links to videos and I can make a page on my web server for the bot..

I got my hands on a free L298N H-Bridge: http://www.st.com/stonline/books/pdf/docs/1773.pdf
Unfortunately it only can handle spikes of 3 A (repetitive 2.5 A). Did you ever get an idea of what currents are used? I plan on making the bot relatively small...

I would take the motor stall current as the H-bridge amp requirement

Also, have you ever run into speed problems from the microcontroller? Is 16 MHz fast enough?

16 MHz is fast enough

My understanding was that the sensors act as the feedback to tell the motors direction and speed, so a motor encoder is not necessary. I see you made the same statement in your initial post.

You will get "basic" balance w/o encoders, but the bot will drift forward/backward

I have now gotten my BlueSmirf and the other parts I was missing to be able to complete my bot.
...
I will post some images and a video on the interface and robot in the end of the week hopefully..

Congratulation Patrik
I will also post my XBee version this week end :wink: :wink:

Ok, I ordered the 6DOF IMU. It has I2C interface, not SPI. Standard speed is 100k/s, faster speed is 400k/s. To poll the sensor we need the Wire library, I have experience working with I2C sensors and I have 4 Arduinos on a robot linked through I2C interface. So, I hope it will work fine. The only thing I have a problem with is the 3.3V interface. I'll see what I can do. I think I need to use 4.7k pull up resistors tied to 3.3V instead of 5V. Also, the sensor needs 3.3V that I will get from the Arduino board directly (I still need to check the current requirements).

I will create a blog page with my attempt, although it did not work because of the design I tried just to see if it works. New version coming as soon as I will get the new IMU.

08:16 14/01/2011
@Osmaneram
Nice bot :slight_smile:
I played with a MPC3208 ADC which is the 8 input version
As far as I remember, those are 12 bit ADC's with 4096 units (not 8096) :o
Please clarify

Your IMU board may have a different noise pattern that may require specific Kalman filter tuning.
The ADC should not inpact Kalman tuning (it didn't on my setup)

@Ro-Bot-X
I am also tempted to go digital for several reasons

  • analogic is noisy by nature
  • additional features
  • this is the future...

I2C frequency is in the 100KHz range, don't be affraid
Please try it and let us know the outcome, we all need guinea pigs :wink: :wink:

I played with a MPC3208 ADC which is the 8 input version
As far as I remember, those are 12 bit ADC's with 4096 units (not 8096)
Please clarify

@kas Good catch. I'm actually using a 3302, which is a four channel 13-bit differential input a2d. The extra bit compared to the 32xx parts is a sign bit. To take advantage of the sign bit, the a2d input voltage must swing above and below the a2d vref. If the vref of your arduino is tied to 3.3v, you the input voltage from the gyro will never go above vref, and so you only get 12 bits.

To get the full 13 bits, take the vref from the gyro, which on my gyro is 1.241v, and connect it to the vref on the a2d and channel 1 on the a2d. Take the vout from the gyro and connect it to channel 0 on the a2d. Configure the a2d to run in differential mode. This means that the a2d will compare channel 0 to channel 1. If the voltage on channel 0 is greater than the voltage on channel 1, the output of the a2d is positive and the sign bit is 0, otherwise the sign bit is 1 which indicates a negative value. In addition to the sign bit, there are 12 more bits of resolution. This means that you get 12 bits from 0 to vref, and another 12 bits from vref to 2 x vref. That's 13 bits effective resolution.

Whether or not all 13 bits will be useful is not yet clear! :-/

--Osman

Hi Kas,

compliments for your great job and info.

I have terminated a self balancing robot very similar to your (same IMU), except for motors.

I used 6V HITEC HS-325HB hacked to run continuosly, but this motors are not enough for he job they have to do.

I tried to make the robot less high but it became too instable..

anyway I order the same Your Pololu equipment...waiting for shipment.

Two questions:

(1) in the setup, with the function calibrateSensors(); you have to put the robot (each time you switch it on) exactly in the static equilibrium position in order to evaluate zero values (exept for z).

is this a little bit boring and could produce errors (if it is not perfectly perpendicular)?

If during the calibrateSensors() i'll give a tilt, the Robot will work to keep the tilt!

maybe there is something that I cannot understand?

(2) what do You use to recharge the 12V batteries?

thanks,

bye,

Prof

Hi prof_jazz, welcome :slight_smile:
please let us have a photo of your new pet

I used 6V HITEC HS-325HB hacked to run continuosly, but this motors are not enough for he job they have to do

I also waste one year following this track :wink:

(1) in the setup, with the function calibrateSensors(); you have to put the robot (each time you switch it on) exactly in the static equilibrium position in order to evaluate zero values (exept for z).
is this a little bit boring and could produce errors (if it is not perfectly perpendicular)?
If during the calibrateSensors() i'll give a tilt, the Robot will work to keep the tilt!

No No...
If you tilt the bot forward before calibrateSensors(), it will move forward and find a vertical equilibrium
This will happen only with encoder(s) implementation, look here

(2) what do You use to recharge the 12V batteries?

I ordered the battery charger shown here

** Balancing robot for dummies **

Part seven: Xbee wireless control, KasBot V 3.0

The objective is to obtain basic motion control (Forward/Backward, Stop Right/Left)
Xbee modules act as a serial line replacement, a USB cable without the cable :slight_smile:
See post #82 for more info

1) controller side:

Shopping list at post #77
Transmitter with Nunchuck joystick controller is shown on post #107
The additional micro joystick and the 3 LED's will be used for advanced features wich are beyong the scope of this basic KasControl V1.0

Nunchuk_kas.h does take care of the I2C communication with the joystick

// Nunchuck_kas.h        joystick functions ----------------------------------------------------------------

#include "Wprogram.h"
#include <Wire.h>

static uint8_t nunchuck_buf[6];                             // array to store nunchuck data,

void nunchuck_init()  { 
  Wire.begin();                                                  // join i2c bus as master
  Wire.beginTransmission(0x52);                                  // transmit to device 0x52
  Wire.send(0x40);                                        // sends memory address
  Wire.send(0x00);                                        // sends a zero.  
  Wire.endTransmission();                                  // stop transmitting
}

void nunchuck_send_request()  {                             // Send a request for data to nunchuck
  Wire.beginTransmission(0x52);                                  // transmit to device 0x52
  Wire.send(0x00);                                        // sends one byte
  Wire.endTransmission();                                  // stop transmitting
}
 
char nunchuk_decode_byte (char x)  {                         // Encode data to format that most wiimote drivers 
  x = (x ^ 0x17) + 0x17;                                     // except only needed if you use one of the regular
  return x;                                                  // wiimote drivers
}

int nunchuck_get_data()  {                                   // Receive data back from the nunchuck
  int cnt=0;
  Wire.requestFrom (0x52, 6);                                  // request data from nunchuck
  while (Wire.available ()) {                               // receive byte as an integer
    nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive());
    cnt++;
  }
  nunchuck_send_request();                                  // send request for next data payload
  if (cnt >= 5)     return 1;                               // If we recieved the 6 bytes, then go print them
  return 0;                                                 // failure
}                                                           // returns 1 on success read, returns 0 on failure

int nunchuck_joyx()  {                                     // returns value of x-axis joystick
  return nunchuck_buf[0]; 
}

int nunchuck_joyy()  {                                     // returns value of y-axis joystick
  return nunchuck_buf[1];
}

This is a strip down version of the code that can be found in the Arduino Playground section.
We only use the joystick data, (we have enough accelerometers in this project ;))
More info on wiring and code here

The main code uses a communication protocol based on 3 Bytes:
controlByte = 'd' (just says "hey, I am coming with new joystick data !!")
dataByte_1 = value of y-axis joystick
dataByte_2 = value of x-axis joystick

To reduce traffic, data is only sent when joystick is moved

// ** Kas Control V10     Balancing bot RC with nunchuk **  

// Yellow: Clock SCL (AI5)  Red: 3.3V  Green: Data SDA(AI4)  Write: Ground
// serial data format:   <controlByte> <dataByte_1> <dataByte_2> <CR>
// V1.0 basic control

#include <Wire.h>
#include "Nunchuck_kas.h"

int refreshTime = 100;                                    // time in millisecs between Nunchuck poolings
long lastPulse = 0;
char controlByte = 0;
char dataByte_1 = 0;                                      // joystick data
char dataByte_2 = 0;

void setup()  {
  Serial.begin(19200);
  nunchuck_init();                                         // send the initilization handshake
  delay(100);
  nunchuck_joyx();
  nunchuck_joyy();
}

void loop()  {
  if (millis() - lastPulse >= refreshTime) {                // x ms have passed
    checkNunchuck();
    lastPulse = millis();                                   // save the time of last pulse
  }
} 

void checkNunchuck()  {
  static int Byte1_ant, Byte2_ant;
  nunchuck_get_data();
  controlByte = 'd';
  dataByte_1 = map(nunchuck_joyy(), 27, 218, 0, 40);        // nunchuck joystick North/South                    
  dataByte_2 = map(nunchuck_joyx(), 27, 218, 0, 40);        // nunchuck joystick East/West
  dataByte_1 = constrain(dataByte_1, 0, 40);                                 
  dataByte_2 = constrain(dataByte_2, 0, 40);
  if((dataByte_1!=Byte1_ant)||(dataByte_2!=Byte2_ant))  {
    updateBot();
    Byte1_ant = dataByte_1;
    Byte2_ant = dataByte_2;
  }
}

void updateBot()    {                                       // send data stream
    Serial.print(controlByte);        
    Serial.print(dataByte_1);    
    Serial.print(dataByte_2);
    Serial.print("\n");
}

...........

.......... continued

** Balancing robot for dummies **

Part seven: Xbee wireless control, KasBot V 3.0

2) Robot side:

On the bot side, the Xbee module is set as per post #107

The datastream is decoded within getMotion()

void getMotion() {
  if(!Serial.available())      return;
  delay(5);                  
  int controlByte = Serial.read();              // get control byte
  if(!Serial.available())      return;
  int dataByte_1 = Serial.read();               // get data byte 1
  if(!Serial.available())      return;
  int dataByte_2 = Serial.read();               // get data byte 2
  Serial.flush();                                 
 
  if(controlByte == 'd')   {                    // Nunchuk joystick data
    setPoint = -(dataByte_1 - 20)/2;          // get forward/backward motion byte
    if(abs(setPoint) > 2)  {
      count = 0;
      last_count = 0;
    }
    turn = (dataByte_2 - 20);                   // get right/left motion byte
  }  else    bip(bipPin, 10, 1);                  // wrong header
}

setPoint is used to bias updatePid() and slithly move the bot from vertical, thus initiating the move

void loop() {
// ********************* Get Xbee data *******************************
  getMotion();

// ********************* Sensor aquisition & filtering ***************
.....

// *********************** Angle Control and motor drive *************
  drive = updatePid(setPoint, actAngle);                                // PID algorithm

  if(abs(actAngle) < 100)    {
    Drive_Motor(drive); 
  }  else {  
    Drive_Motor(0);                                                    // stop motors if situation is hopeless
    setZeroIntegal();                                                  // reset PID Integral accumulaor
  }
// ********************* print Debug info ****************************
.....

// *********************** loop timing control ***********************
.....

}

turn becomes a new parameter in Drive_Motor(), to allow right and left motor to run at their own speed

int Drive_Motor(int torque)  {
  if (torque + turn >= 0)  {                                          // drive motors forward
    digitalWrite(InA_R, LOW);                        
    digitalWrite(InB_R, HIGH);
    torque_R = torque + turn;
    if (torque - turn >= 0)  {                                        // drive motors forward 
      digitalWrite(InA_L, LOW);                     
      digitalWrite(InB_L, HIGH);
      torque_L = torque - turn;
    }  else  {
      digitalWrite(InA_L, HIGH);                      
      digitalWrite(InB_L, LOW);
      torque_L = abs(torque - turn);
    }
  }  else {                                                           // drive motors backward
    digitalWrite(InA_R, HIGH);                       
    digitalWrite(InB_R, LOW);
    torque_R = abs(torque + turn);
     if (torque - turn >= 0)  {                                       // drive motors forward
      digitalWrite(InA_L, LOW);                     
      digitalWrite(InB_L, HIGH);
      torque_L = torque - turn;
    }  else  {
      digitalWrite(InA_L, HIGH);                      
      digitalWrite(InB_L, LOW);
      torque_L = abs(torque - turn);
    }
 }
  torque_R = map(torque_R,0,255,K_map,255);
  torque_L = map(torque_L,0,255,K_map,255);
  torque_R = constrain(torque_R, 0, 255);
  torque_L = constrain(torque_L * motor_Offset, 0, 255);
  analogWrite(PWM_R,torque_R);
  analogWrite(PWM_L,torque_L * motor_Offset);                      // motors are not built equal...
}

OK guys, this is probably my last significant contribution to this thread :-/
I suddently realize that I have a real life and... some other projects to explore ::slight_smile: ::slight_smile:

I am sure this thread will keep on, with new bright ideas coming on
What about non linear control ??
What about one wheeled bot as shown by Big Oil ??

"...Two wheeled balancing robots are an area of research that may well provide the future locomotion for everyday robots..."

I really enjoyed all these discussions we had on this common project
Thanks again to everybody for reading and contributing :slight_smile: :slight_smile: :slight_smile:

wow kas!

any video of this new child?

bye