High speed balancing robot: introduction

By | November 10, 2018

For quite some time, I’ve been developing a two wheeled balancing robot. You know, like a Segway. But in this case it is somewhat smaller, as to minimise damage when hitting something at high speed. That happens rather often, due to the very high maximum speed of this robot.

This post will cover the architecture of the robot, explaining all ins and outs. Many balancing robots have been made before, but this one has some nice features that make it very well behaved. Also, everything has been designed to make the robot easy to replicate. As one of the goals of this project is to get other people to work on the robot, all source files will be made open source. Also, in a future post I hope to document how to build one.

Just to be clear: the purpose of me making this project open source, is not for people to get such a cute, fun robot, that they’ll play with and then never use anymore. The purpose is to educate other people, to share knowledge, and to create and extremely good performing robot together. Just because. Because developing technological things without purpose is fun.

Basics

In order to balance an unstable system, negative feedback is required. The diagram below illustrates the approach. Besides a feedback loop, a sensor to measure an error (or rather the sytem response), and an actuator to minimise the error, are required.

Sensing and actuation

Getting a sensor with sufficient accuracy and bandwidth is relatively easy; in this case the well-known (and mostly cheap) MPU6050 IMU is used. The accelerometer provides a long term stable, but noisy signal, especially if you accelerate fast. The gyroscope on the other hand gives an accurate, high speed signal, with drift however. Combining the two using a simple complementary filter gives a stable signal, representing the angle of the robot.

Next, an actuator needs to be selected. A common choise is a DC motor, for example for its low cost or simple control. However, a DC motor has in general a too low torque to directly drive the wheels of the robot. A gearbox is used to increase torque, but several disadvantages are introduced as well. Cheap gearboxes have severe backlash and friction, which make the control loop non-linear, and therebey making everything a lot more complicated.

This is where the stepper motor comes in. This idea isn’t new, serveral other examples of balancing robots with stepper motors are available. For some examples, see herehere or here. Steppers are quite suitable for the job: direct drive, relatively high torque, low inertia (which isn’t true for a DC motor with high gear ratio), and no backlash. Stepper motors are commonly used in for example 3D printers, and can hence be found for a low price (about €10 each). Driving the steppers is also done with 3D printer related parts. A so called step stick, containing a DRV8825 IC, arranges all the power electronics, accepting a logic step and direction input.

Software

Then comes the software. In my opinion, this is the part where the most difference can be made. The above description holds for a lot of balancing robots. With suitable software / control implementation, a lot of differentation can be made. The software of this robot runs on an ESP32, a dual core microcontroller with integrated WiFi and Bluetooth. Using an ESP32 allows for several nice tricks, such as control with a smartphone via WiFi / Bluetooh (not implemented yet), OTA, wireless data logging, etc.

Currently, the software runs two control loops at 200 Hz. See below for a schematic view of the two (cascased) controllers. The first control loop tries to minimise angle error, i.e. keeping the robot upright (in case of a zero reference). The second control loop either controls position or speed, depending on user input. If no input is present, position is regulated to be zero. Otherwise, the outer controller manipulates robot angle to obtain a speed equal to the speed setpoint. To give an example: if the robot is standing still, and a positive (forward) speed setpoint is given, the robot tilts forward until it reaches the desired velocity. When standing upright, the inner control loop moves the wheels backwards, such that the robot will tilt forward.

Electronics

For powering the robot, I currently use a 6S1P (24 V) 2650 mAh LiPo battery. That’s quite a big one: it allows for up to 5 hours of driving time. So, such a big battery is not strictly required. Besides this battery being relatively cheap (in Wh/€), a big mass on top of the robot makes the robot fall “slower”. This makes stabilising the control loop easier, and, it also looks more natural in my opinion. A sturdy case has been 3D printed to fit tightly around the battery, to allow for protection. The high voltage of the battery allows the steppers to be run at extremely high speeds. Note that 6S is over the top, and 3S will give sufficiently high speeds.

The last part to add is a PCB. For the first robot versions, I developed some experimental PCBs. For the current version, I’ve drawn a simple PCB, based on commonly available modules (IMU / step-stick / ESP32 dev board), which allows for easy assembly. The PCB is drawn in KiCad, which is open source software. I have some PCBs in stock, so feel free to ask for one. In case you want to make something yourself (on an experimental PCB), see the PDF of the schematic in the Git repo.

Some PCB history

The total cost of the robot is quite limited due to use of standard components. Electronics are about €10, steppers €10 a piece, battery between €5 and €30. The frame can be printed cheaply (see Git repo for source files), or simply zip tie everything to a piece of wood. Currently I use an RC transmitter / receiver, which drives the cost up, but as soon as WiFi/Bluetooth control is implemented, it isn’t required anymore.

To end this post, some more pictures of the complete robot(s). Yes, it has a small brother :).

More info

Source files can be found in the Git repo. It is highly appreciated if you contribute to the project!

The subolders in the repo also contain readme files, explaining how to assemble the electronics, and how to setup the software environment.

23 thoughts on “High speed balancing robot: introduction

  1. Hank, LEE

    Hello.

    I saw your high speed balancing robot in youtube.
    it is interesting for me and I want to do this project if you let me. ^^

    Could you send me a PCB ( current version )?

    Now i am buying resource motor and etc.
    Thanks for your video.

    Reply
  2. ROBERTO CARLOS DA SILVA ALVARENGA

    Hi ! Congratulations!!!!
    I really like your project! And I want to do the same.
    What is the model of the motor stepper that you’re using ?

    Thank you.

    Reply
    1. Wouter Post author

      Thanks for your reponse! I use Nema17 stepper motors (the ones typically found in a 3D printer). I’ve used motors with a length of 34, 40 and 48mm, all work ok. I think 40mm length is a nice balance between weight and torque required for this application.

      Reply
    1. Leonardo

      Friend excellent project. I’m mont one together for my son. I installed Atom but in the compilation it presents errors. Did you need to install any external libraries?

      Reply
      1. Wouter Post author

        Hi Leonardo, thanks for your response! I think your error is caused by me not having included the Streaming library; I installed this library globally (which is bad practice). See the latest commit in the dev branch: https://gitlab.com/kloppertje/balancingrobot/tree/devel

        If it still doesn’t work, please post (or better, mail) the specific errors you get. Also, I can recommend VSCode, it works a bit better compared to Atom with PlatformIO, in my opinion.

        Reply
        1. Leonardo

          lib\AsyncTCP\src\AsyncTCP.cpp:259:27: error: field ‘call’ has incomplete type ‘tcpip_api_call’

          Reply
          1. Leonardo

            I solved the error above. I upgraded the AsyncTCP library to 1.0.3 now I’m having this error. lib\I2Cdev\I2Cdev.cpp:276:75: error: no matching function for call to ‘min(uint8_t&, int)’

  3. Robert Schossleitner

    Hello Wouter,

    thanks for the PCB. Now i am fighting a bit with the Stepper Code in the Loop i found this lines.

    motLeft.update();
    motRight.update();
    // updateStepper(&motLeft);
    // updateStepper(&motRight);

    In my case it seems that ust the motLeft produces an appropriate stepper clock on the gpio pin the motRight gpio is quiet. However the direction pin of the motRight is working. If i flip the update-Statements (motRight first), in all cases the first is working. It seems to me that something with the timer/ISR is not working. Do you have any hints regarding that problem?

    BR Robert

    Reply
    1. Wouter Post author

      Hi Robert,

      I haven’t experienced these issues before. Are you using the devel branch (which I recommend)? And, have you changed any of the pins used for step generation? Maybe something goes wrong in this function (lib/fastStepper.cpp):
      void IRAM_ATTR fastStepper::timerFunction() {
      // portENTER_CRITICAL_ISR(&timerMux);
      if (!pinState) {
      _step += dir; // Step is made on rising edge
      GPIO.out_w1ts = 1<<_stepPin; pinState = 1; } else { GPIO.out_w1tc = 1<<_stepPin; pinState = 0; } // portEXIT_CRITICAL_ISR(&timerMux); } You could for example try using digitalWrite (which is probably a bit slower). KR Wouter

      Reply
    2. Atilla BALCI

      What type fuse should i use instead of F1 polyswitch? i can’t find it in my country. Ampere or volt?

      Reply
      1. Wouter Post author

        Depends on the supply voltage. Typically, two stepper motors consume 10-15W. So, at 12V that’s roughly 1A. So, a polyfuse with a rated current a bit above that, say 2A, should suffice. And the rated voltage should then be higher than 12V. But again, it all depends on the choice of input voltage.

        Reply
        1. Atilla BALCI

          I am trying to do the same as yours. Will work with 6s lipo. How many volts and amps do you recommend? There are thermal and glass fuses with legs but I don’t know how many degrees Celsius or amperes (volts) i will use.

          Reply
        2. Atilla BALCI

          Finally, I am confused about two points.
          First, in the pcb readme file, you said “You can de-solder the current adjustment potentiometer on the stepper driver breakout boards. Add a wire to the wiper terminal, and connect the (two) wires to J4”. As far as I understand, J4 and J7 for the stepper motors output connections. Will the cables from the stepper driver breakout boards be connected to J4 or J1?
          The second is JP1. I tied the decay mode pin of the DRV8825 to VCC. Will JP1 tips be soldered?
          Thank you for your support.

          Reply
  4. Robert Schossleitner

    Hello Wouter,
    yes i am on the devel branch. I have written an alternative loop-function with just a simple test code in it. And i am counting how often the IRAM_ATTR motXXXTimerFunction() are called.

    https://github.com/SrR0/BalancingRobotESP32/blob/master/src/main.cpp

    The counter of the left motor interrupt is counting up the other stays 0. If i flip the first

    motLeft.update();
    motRight.update();

    to

    motRight.update();
    motLeft.update();

    the right motor output is working. The Timer and portMUX stuff is a bit suspect to me. It seems the first timer functioncall is locking something with the timer of the other motor

    BR Robert

    Reply
  5. Robert Schossleitner

    Hi!

    in the meantime i found out that if I disable the timer before enable it, it seems to work. So the second timerAlarmEnable without timerAlarmDisable before does not work. I have no idea why.
    fastStepper::update(){

    timerAlarmDisable(_timer); //just for testing todo: cleanup
    timerEnable = 0;
    if (!timerEnable) {
    timerAlarmEnable(_timer); // Re-enable timer
    timerEnable = 1;
    }
    } else {
    timerAlarmWrite(_timer, 100000, true);
    timerAlarmDisable(_timer);
    timerEnable = 0;
    }

    BR Robert

    Reply
  6. Robert Schossleitner

    Hello,

    I used the PID controller class in a different projekt and I noticed that the behaviour when using the integral term of the PID controller was a bit weired. The lower I set it, the more powerful was the effect. I didnt understand the calculation oft the integral term:

    void PID::updateParameters()
    {
    _if = _dT/Ti;
    _df1 = Td/(Td+N*_dT);
    _df2 = Td*N/(Td+N*_dT);
    }

    the higher the Ti ist the lower is the effect when being multiplied with the error of the comparation between setpoint and feedback-input.

    I changed it into
    void PID::updateParameters()
    {
    _if = _dT*Ti; //higher value of the Ti and longer sampleperiode should increase the effect of an error

    _df1 = Td/(Td+N*_dT);
    _df2 = Td*N/(Td+N*_dT);
    }

    Seems to work for me. Maybe I did not understand the whole therory 🙂

    BR Robert

    Reply
    1. Wouter Post author

      Hi Robert,

      This is just a matter of definition. Sometimes people use “gains”. I like to look at the PID controller in the frequency domain, and in that case using time constants (Ti and Td) makes more sense. A lower Ti means it will work up to a higher frequency, i.e. it will be more aggressive. The same goes for Td, a lower time constant means a higher frequency at which the damping action is active. So, in this case, a higher Td gives the feeling of “more damping”, which probably is a bit more intuitive than the behaviour of Ti.

      KR,
      Wouter

      Reply
  7. Atilla BALCI

    Hi Wouter,
    I have built one working with 24v and its working excellent. But i can not activate the servo. Could you help me?

    Reply
  8. Baur

    Hello! Your robot does not lose balance if it is tilted left or right?

    Reply
    1. Wouter Post author

      Depends on the orientation you’d be pushing in. And if you push hard enough, it’ll fall. It can handle some slight pushes though.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.