ArduRoller Balance Bot

54K14937

Intro: ArduRoller Balance Bot

Caveat Emptor: (I don't want to put you off building one but I also don't want you to be disappointed.)  This Instructable is now 2 years old.  Many of the parts it uses are out of date (there's no drop-in replacement for the now-discontinued gyro, for example) and I've had reports that the code is hard to compile.  If you're not comfortable with the principles behind the electronics, code and the maths involved in PID control then you probably shouldn't attempt to build one.  Also, it's a tricky project to debug once built so it's really not a good first project.  If you're not happy to spend 2 days watching your bot list, jiggle, zoom across the room and fall over before you get it to balance then steer clear!

This instructable shows how I built my ArduRoller balance bot. It balances quite well on the spot and responds to most knocks pretty quickly but sometimes giving it a more gentle push sends it gliding across the room at a constant speed. I'm still working on that.

There's a video of the bot in action at http://www.flickr.com/photos/fasaxc/5944650602/.

STEP 1: Parts

Here are the parts I used:

1 x Arduino Uno
1 x Sparkfun Ardumoto motor driver shield
1 x Sparkfun BlueSmirf Bluetooth modem
1 x 150 degree/s gyro
1 x 1.7g Accelerometer
2 x Arduino header kits
2 x screw terminals
2 x 24:1 gear motor
1 x set of 70mm wheels
2 x JST connectors
2 x LiPo batteries
1 x basic LiPo charger
3 x Multi-turn 10k potentiometers
1 x SPST switch (Radioshack)
1 x Laser-cut bamboo chassis via Ponoko  (link should allow you to make one from my shared design)
1 x LED
1 x Normally-open push switch
1 x packet of Sugru to make the bumber
Assorted M2-04 machine screws (6mm - 16mm) (found on Amazon)
M2-04 nuts to match machine screws above
Assorted straight and right-angle break-away headers
Assorted jumper wires
Solid core wire
Stranded core wire
Instamorph (aka Polymorph) low-melt-point thermoplastic

Notes:

Chassis: the motors didn't quite fit the mounts I made so I had to sand them down and rebuild them with instamorph.  I think the sensor bundle suffers from too much vibration, it might have been better to make it more solid rather than sticking out as it does.

Accelerometer: I originally tried building the bot with only an accelerometer for tilt sensing and no gyro.  It turns out that approach is a non-starter -- the accelerometer gets overwhelmed by the acceleration due to the motors so it can't be used to estimate tilt while the bot is accelerating.  OTOH, using only a gyro would make the bot susceptible to drift over time so you really need both.

Gyro: I used a 150 degree/s rate gyro.  From looking at the telemetry from my bot, I'm pretty sure it sometimes clip if you give the bot a knocks so if I was starting over I'd probably look for a 300 degree/s model.  

Wheels: the wheels are a little fragile, after a few knocks I noticed cracks around the axle so I strengthened them with instamorph.

Motors: I also tried sparkfun's 100:1 gear motors but they weren't fast enough.  The 24:1 versions have plenty of torque and speed.

Bluetooth: I use the bluetooth modem for telemetry right now but I'm also planning to use it for remote control from my Android phone.  If you omit it then the robot will still work but tuning it will be harder.

Pots: I added 3 10k multi-turn pots to the design to allow me to easily tweak internal values.  Using 3 might have been overkill since I tend to tweak only one thing at once.

Instamorph:  Amazing stuff.  It's a tough, white plastic (resembling solid nylon) at room temperature but if you heat it in boiling water it turns into a pliable goo that's really easy to work with your hands.  A heat gun is great for working with it too, allowing you to melt small areas. 

STEP 2: Overall Design

This step covers the overall design of the bot.  I'll try to explain what each of the parts is for:

The brain of the system is the Arduino Uno, which contains an AVR microcontroller running at 16MHz.  

1000 times per second, the microcontroller reads the current state of the gyroscope and accelerometer; updates its internal model of the bot and from that model decides how fast to run the motors to best balance the bot.  (The code is all shared on my Github repo .)

The gyro is a rate gyro, which means that its output is proportional to the current rate of rotation.  To estimate the current tilt, the microcontroller has to sum the incoming values, which it reads using an analog to digital converter. Unfortunately, no gyro or ADC is perfect, resulting in errors in the summation that tend to grow over time.  If the bot used only a gyro to try to balance then its idea of "up" would slowly drift over time and it'd eventually fall over.

To counteract the gyro's tendency to drift, the bot uses a 2D accelerometer to measure the direction of gravity.  When summing the gyro values it adds in a tiny fraction of the accelerometer estimate into the calculation.  Just enough to balance out the drift.  It can't add too much because the accelerometer is a very noisy sensor - it picks up vibration from the wheels and the acceleration of the motors.

The gyro and accelerometer are mounted on the axis of rotation of the wheels to get the best signal.

Once the microcontroller has decided how fast to run the motors it uses pulse width modulation to vary their speed and drives them through the Ardumoto shield.  The shield is necessary because the motors draw far more current than the microcontroller can supply on its own.

The BlueSMIRF module provides serial-over-Bluetooth, allowing the bot to communicate in both directions with another Bluetooth-enabled device.  I use my Android phone to relay the serial data to the console over the Android Debug Bridge.  I'm also planning to send signals the other way to use my phone as a remote control.

STEP 3: Chassis

I designed the chassis using Inkscape on Linux and used Ponoko for laser cutting.  The SVG version is in my Github repository and I've published it on Ponoko .

I was pretty pleased with how it turned out given it was my first try at using Ponoko.  The only part that didn't work so well was the motor mount, which I had to sand down and augment with Instamorph.

Using Ponoko, the wood grain runs left-to-right so some parts need to be placed sideways.  The laser cutter is incredibly precise and does a great job on screw holes.

Be sure to read Ponoko's design rules if you intend to modify the chassis design.  There are gotchas like needing to put "nodes" in push-fit slots to take account of material differences and the fact that the laser removes 0.2mm around your line.

STEP 4: [Update] Bumper

The bot can hit the ground quite hard when it falls over so I recently added a bumper made of Sugru.  I laid out some masking tape horizontally below where I wanted to put the bumper, just as a guide.  Then I rolled a sausage of Sugru and applied it in the gap.  I used my fingers and a clay cleanup tool to shape it and burnish it smooth.

STEP 5: Electronics

The schematic in this step shows how I wired up the electronics for the ArduRoller.  I used the prototyping area on the ArduMotor to hold the components.  It was a tight fit but I managed to get it all in there even though I added components pretty much at random.

STEP 6: Code

I've shared the code for the project on Github .  I used Eclipse rather than the Arduino IDE because it is much more robust and has a better editor.  However, I did use all the Arduino libraries so the code would probably run as a sketch with a little tweaking.

The code essentially implements a PID controller  for the tilt.  It has quite a high integral term, which causes the bot to overshoot when it's correcting for a nudge.  That tends to null out the bot's tendency to drift along, balancing upright but not stationary.  This is the key line that sums the PID terms:

    speed = tilt_rads_estimate * TILT_FACT +
                    tilt_int_rads * TILT_INT_FACT +
                    gyro_rads_per_sec * D_TILT_FACT;

There are several places that you might need to tweak in the code to make it work with your bot:

The GYRO_V_PER_DEG_PER_SEC constant sets how much the output of your particular gyro changes for a change of one degree per second in rotation rate.  I found that mine was at the end of the bell curve in the datasheet.

Likewise, the ACCEL_V_PER_G constant sets the value for the accelerometer.

You may also want to use the inputs form the pots to tweak other values like the X offset, which tilts the bot backwards or forwards.  This needs to be set correctly else the bot will not balance.

The push button causes the bot to enter calibration mode for 10 seconds.  To use it, lay the bot on its back and push the button then leave it for 10 seconds.  It will automatically calibrate the gyro rate null point.

STEP 7: Next Steps

There are lots of areas for improvement in the code:

Right now, the gyro and accelerometer values are summed using a simple complementary filter. There are much better techniques out there such as a Kalman filter but I haven't got around to getting my head around it!

The bot doesn't currently have an estimate of its linear position or velocity so it can't correct itself if it ends up drifting across the room, well balanced but with non-zero speed. I've experimented with tracking the acceleration and integrating to get velocity but with little success.

There are better control methods for this type of problem (such as state-space control) that can better deal with multiple-input-multiple-output problems.

It could use a good tidy up.

I'm currently working on better tuning the code to make it more robust, experimenting with other control mechanisms and an Android-based remote control.

Hope you enjoyed this tour of my bot!

STEP 8: Rebuilds

http://www.logos-electro.com/blog/2011/9/25/zigroller-project-hardware.html

37 Comments

Hi. Why did you choose this vertical design over a stacked design?

hi i want make my balacing robot with atmega16
hi who help me in my balacing robo projects with adxl335 moduel thanks
my email :hamedmosavi125@gmail.com
Hi. Very nice project which I am trying to replicate using IDE, your main.cpp sketch, an Arduino ProMini , L298H bridge, ADXL335 and LPR550AL which are similar to your gyro and accelerometer. I also blanked out the first six include statements.
This is what I get:
wheels turn in opposite directions and when pressing the calibrate switch on pin8 nothing happens. Can you help me to get to the calibration part working along with the serial monitor? thanks
PS I can monitor the Xacc and Yacc and the gyro using another program.
This is how I tested my gyro and accelerator so far:
hardware arduino promini, ADXL335 5V gyro 500degr-sec 2mv,
LPR550AL 5V accel 3g 10Kpots set at about 50%
LED on pins 8 and 13

sketch:

const int button = 8; // added button pin
const int led = 13;
const int led2 = 7;
void setup()
{
Serial.begin(115200);
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(button, INPUT);
}
void loop()
{
digitalWrite(led, HIGH);
digitalWrite(led2, HIGH);
delay(1000);
Serial.print(" gyro "); Serial.print(analogRead (A0));
Serial.print(" Xacc "); Serial.print( analogRead (A1) );
Serial.print(" Yacc "); Serial.print( analogRead (A2) );
Serial.print(" pota "); Serial.print(analogRead (A3));
Serial.print(" potb "); Serial.print( analogRead (A4) );
Serial.print(" potc "); Serial.println( analogRead (A5) );
ditalWrite(led, LOW);
digitalWrite(led2, LOW);
delay(1000);
}

result:
both LEDs blink on and off at 1 second intervals and the captured serial input from the analog lines are:
(bot is lying horizontal)
gyro 486 Xacc 401 Yacc 303 pota 515 potb 532 potc 506
gyro 485 Xacc 400 Yacc 303 pota 515 potb 532 potc 506
gyro 482 Xacc 398 Yacc 301 pota 514 potb 532 potc 506
(bot moved to vertical in about 5 secs)
gyro 489 Xacc 369 Yacc 319 pota 470 potb 485 potc 462
gyro 437 Xacc 325 Yacc 301 pota 452 potb 467 potc 445
gyro 397 Xacc 311 Yacc 296 pota 453 potb 468 potc 445
gyro 402 Xacc 316 Yacc 300 pota 452 potb 467 potc 445
gyro 423 Xacc 332 Yacc 318 pota 461 potb 477 potc 454
gyro 410 Xacc 322 Yacc 309 pota 464 potb 479 potc 456
gyro 399 Xacc 313 Yacc 299 pota 465 potb 481 potc 458
gyro 391 Xacc 308 Yacc 293 pota 466 potb 481 potc 458
One other thing that's worth trying: you can change the PID controller expression to exclude the integral or proportional term while you debug the other. ie. comment out one of the terms in this expression:

speed = tilt_rads_estimate * TILT_FACT +
tilt_int_rads * TILT_INT_FACT +
gyro_rads_per_sec * D_TILT_FACT;

It won't balance but it'll show you how that term behaves.
If the motors are moving in opposite directions then you need to either swap the wires on one motor or flip the direction bit in the code.

I don't think the current version of the code uses a calibration button but you can leave the USB cable connected and write a single letter g to the console. That will make it spit out diagnostics (although it won't balance with USB cable and diagnostics running).

To calibrate, connect the USB cable and send a g to get the output flowing. Then, the bot will write out the key state values. Then you need to work back from the outputs. When you're not moving the bot, you want the gyro to be reading 0 after subtracting the gyro offset. You also want the X and Y acceleration to be normalized so that their sum square is 1. The error in the gyro offset is absolutely critical, you need it accurate to 0.01 or the error in gyro position will sum up quickly and overwhelm the signal.

Looks like your gyro might have a different mV/rad/s value from mine, you'll need to enter that in at the top. I found the best way to set that was to run the calibration output and then: quickly rotate the bot by 45 degrees and then slowly rotate it back. If all your values are correct, then the gyro angle guess should end up back where it started.

Since my code is pretty complex (and somewhat grungy), you might want to start from scratch with your own code, implementing a basic complementary filter to combine gyro and accelerometer and then a PID controller to balance the bot.
Hi, Can you explain please why the batteries are not plugged into Vin? And how do you get the motors to run in reverse if the battery it plugged in this way
Hi, the batteries are plugged into Vin, I think that's pretty clear on the diagram. Maybe you're wondering why the Vins aren't all connected? In fact they are all connected, it's just implicit in the diagram. Drawing all the lines to connect Vin would make it messy.

I'm using an Ardumoto to control the motors, which uses a H-bridge internally. Effectively, it can connect the motors either way round; with Vin on one pin and 0v on the other thy spin one way; with Vin on the other pin and 0V on the fist they spin the other.
Thanks for the quick reply (sorry it's another post. The reply isn't working for me for some reason...)! I'll see if I can figure out the filter at the website, and I'll try to find a carpet.
I guess I'll just continue working on experimenting for the PID constants (maybe I'll try the methods on wikipedia). If it works, I'll upload a video.
Thanks for all the help!
Hi! I constructed your balancing robot and I am stuck on what seems to be one final step. The robot I made is relatively much simpler (no trimpots, no push button, no LED status), but everything necessary for balance is there. The problem I have is that the balance seems underdamped. I modified the code a little bit (which I can post somewhere else), but most notably the ISR and PID controller.
I changed the ISR to a loop (since the interrupt kicked in but the code within the ISR never began) and changed the constants for the PID (initially to slow the speed of the wheels, but now I'm trying to modify to stabilize it).
http://www.youtube.com/watch?v=zPcYQKwf4rQ
http://www.youtube.com/watch?v=lDwBbGUEXaw
Above are two videos of the robot.
Any suggestions on how to fix this (especially for calculating filters and PID controller constants) would be great.
Thanks!
Your bot is looking pretty close to balancing. I just experimented a lot. I think I used the filter designer at http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html to get the filter constants. I just experimented a lot to find some that worked.

Adjusting the PID controller requires a lot of trial and error; there are a few different
"sweet spots" that give different behaviour. For example, increasing one of the parameters might make the bot respond well to a particular type of nudge but then it'll make it worse at standing still.

One thing I notice is that your're trying to balance your bot on a smooth surface. I recommend starting with carpet because the carpet helps to dampen the bot. My bot doesn't balance well on a smooth surface either.

If you find that it tends to drift in one direction more than the other then you'll need to adjust the balance point.
Ok. But what about d_tilt_pot? it is not used anywhere, why do we read its analog value? In the code there is the line: speed = 7 * sqrt(abs(speed)). Is 7 related somehow to the diameter of the wheel?
Any fix on the constant speed across the room issue? I think that could be solved with a line that would slowly increase the amount of correct if the initial amount does work. SO instead at going across the room, it would increase until it had enough to right it self
I already have an integral term in my controller but due to symmetry it doesn't work to prevent horizontal drift. Increasing it tend to make the bot oscillate back and forth but it still drifts across the floor as it does so. It's probably theoretically possible to derive the drift rate from the sensors but they're so noisy that it's very difficult. It'd be must easier to just add shaft encoders so that my bot can tell exactly how far it has travelled and how fast.
Hmm, that is an unfortunate downside to reasonably priced electronic components. Shaft encoders might do it, but Im not sure how that would solve the oscillation due to sensor noise. Now that got me thinking. What about another line that would dictate how much power the integral line would be allowed to add to correct offset? So the closer the bot is to up right the lower the limit, and as the angle increases the higher the limit would be set if it was needed. Less chance of over correcting. But if it were too low you'd might get a slow side to side sway.
Ah, but what if the bot is upright and moving, then the integral term would do nothing and we're back to square one! In short: control theory is hard and there are a lot of non-obvious consequences of any change. I've experimented quite a lot with all the PID parameters but without a rewrite I think the code is as good as it's going to get. (A rewrite would let me make the code more performant so it might shorten the control loop and result in better control.)

Using the shaft encoders would help oscillation because I could reduce the integral term and rely instead more on the shaft encoders. Unfortunately, amalgamating multiple inputs like that requires more complex control algorithms that I'm not familiar with.
I apologize for my lack of knowledge on the subject. I've been just getting into controllers, and was looking into balance bots as a project. Been a bit tough, dropping out of calculous sure doesn't help matters, especially in terms of PID controllers....
Thanks for your reply) Now I'm trying to understand the code, but some parts of the program are unclear. Why GYRO_OFFSET = 3.982, and X_OFFSET = 8 ? And why do we need potentıometers? Have you used complementary filter or other one? Thank you in advance:)
The potentiometers allow adjustment of the PID controller without recompilation. I can adjust the code so that the potentiometers change any value that I'm interested in and then change the potentiometers gradually to experiment. It's much faster than recompiling the code every time.

The other values are the calibration values for my particular sensors. You'll need to work those out for your own sensors too or experiment until you find the right values.
More Comments