Introduction: How to Make a Robot Car Drive Straight and Turn Exact Right Angles With MPU6050 Gyroscope Sensor

About: I use Instrustables to document my past projects and development of skills. I am an electrical engineering student from the University of Queensland.

The video above shows the effectiveness of my solution by comparing before and after. Remote control robotic cars typically use two or four motors for moving around. Despite being from the exact same model and manufacturer, no two motors are exactly the same. Due to this slight difference, robotic cars rarely drive straight when both motors are supplied with the same power. This problem has plagued many remote control enthusiasts, but there are surprisingly few solutions on the internet. These few solutions are mostly designed for Lego robots or use encoders, which assume the robot is symmetrical and the terrain is flat:

https://www.youtube.com/watch?v=XEuAdImqUcI&ab_channel=OutoftheBOTS (uses encoders)

https://www.youtube.com/watch?v=SMw_HGJBLQ0&ab_channel=ZainKhan (Lego)

My solution uses the MPU6050 gyroscope, which tolerates asymmetry and uneven terrain, and is more adaptable than a Lego kit. This article mostly focuses on the software/code (which I attached below as "driving_straight_MPU6050_v1.ino"), since the software can be modified to suit robots with different hardware, if you understand the key concepts of my code. Hardware information is under the heading Supplies. The effectiveness of my solution is shown in the video above.

Line specific explanation of the code is in the comments of the attached Arduino .ino file below. The broader concepts behind the code is on this Instructable page, with four Steps explaining:

  1. Overview
  2. MPU6050 gyroscope sensor and its calibration
  3. Proportional control technique
  4. USART serial communication

Further step:

5) Debugging common problems (skip, if everything works)

Supplies

You do NOT need the exact same hardware, if you can understand and adapt my code. The information in the brackets are the exact hardware I used. I have attached datasheets below for further information.

  1. Laptop with Arduino IDE installed
  2. Arduino board (Arduino UNO)
  3. MPU6050 Triple Axis Accelerometer and Gyro module
  4. 7 - 12V DC power supply (6X AA rechargeable batteries)
  5. motor control module (L293D)
  6. wireless communication e.g. radio, Bluetooth, infrared (HC-05*)
  7. two motors and wheels (TT motors)
  8. Structural element for the chassis the car
  9. A 100 to 500uF capacitor to stabilize the 5V output of Arduino
  10. Breadboard + jumper wires OR soldering + PCB

*If you are also using HC-05, you can see how I used it at: https://www.instructables.com/How-to-Connect-HC-05-to-Windows-1011-Mac-Apple-Com/

Step 1: Overview

The circuit diagram above visualizes the pin connections in my code. Into the serial monitor while running the code, enter characters, a, w, d to move the car; i for print out of information; p for pause. Remote control commands are entered into the serial monitor. When using the serial monitor, the wireless HC-05 replaces a wired USB cable, so the command is sent from serial monitor on computer, to Bluetooth connection of HC-05, then to TX and RX pin on the Arduino using USART serial connection.

My solution uses software and some control engineering concepts to correct the hardware imperfection of the motor that prevents the robot car from driving straight. The software corrects it by giving less power to the stronger motor. For me, the stronger motor is the left one. If your right one is stronger, you need to modify the driving () and controlSpeed () functions in the Arduino code (attached below Introduction section).

When gyroscope detects the car is veering too much to the left, it increases power to the left motor (since now left motor outpaces right motor to make the car turn right). When veering too much to the right, it reduces the power to the left motor. The MPU6050 gyroscope detects if the car is veering left/right and by how much. Based on this detection, Proportional Control Technique adjusts the power to the left motor.

The Arduino controls the power to motor via the motor control module (L293D) and its PWM (pulse width modulation) pins. These are pins are represented by leftSpeed and rightSpeed constants in the code. Note: not all digital pins on Arduino are PWM. PWM is basically a square wave turning on and off at high frequency, this causes the average voltage at the PWM to pin to be lower than if it is constantly on. The motor control module interprets this lower voltage and reduces power to motor accordingly.

Step 2: MPU6050 Gyroscope Sensor

MPU6050 communicates with the Arduino with I2C (aka TWI) serial communication. The preinstalled Wire.h library allows you to use I2C and read values stored on MPU6050.

MPU6050 does not directly measure angle/orientation, but measures angular velocity (represented by GyroX, GyroY, GyroZ in the code). To get, the angle/orientation we integrate angular velocity over time (shown in line 76-8 in code). We can also calculate angle/orientation from linear accelerations (line 63-4). We 'average' the two estimates of angle, while giving the angular velocity estimate a much higher weighting, because both estimates have their own merits (which I do not fully understand either).

The code uses summation to approximate an integration. The summation method's (line 76-8) accuracy depends on on the frequency of readings per seconds. We want to take as many readings per seconds as possible, which means reducing the cycle time of void loop() and reducing the lags in the code (see Step 4).

calculateError() function: defines what is considered as car's zero angular velocity and zero acceleration. In physics (at least classical physics), motion is relative. For example when you are standing still on a plane in the air, you are still moving relative to the earth, since the reference point changes from the plane to the earth. calculateError() defines this reference point, so when running this function, the car needs to be stationary.

Step 3: Proportional Control Technique

If the orientation/angle is significantly different to the desired angle, the car rotates fast to correct this quickly. However, if the difference is slight, the car rotates slowly to avoid over-correction. This is what Proportional Control does: the rate of change is proportional to the discrepancy between the actual and desired value. In our project, the rate of change is the angular velocity (GyroX in the code). The graphs above shows the targets angular velocity for a given angle difference. I capped the angular velocity at 60 degree/second, which is very fast. In the rotating mode, we take the absolute value of the angular velocity, since the direction of rotation is determined by direction of rotation of wheels, instead of the speed difference between left and right motors. In the code, Proportional Control is implemented under rotate() and controlSpeed() functions.

You can modify my proportional control parameters and equation, but ensure yours is:

  • a continuous function
  • has a value of zero, when angle difference = 0
  • Proportional Control is not the only method, others: Bang-bang control (very simple, but often over-corrects), Proportional Integral Differential (PID, significantly more complex)

Step 4: Code Cyle Time and USART Serial Communication

In Step 2, we discussed the importance of reducing lag to maintain gyroscope's accuracy. When modifying the Arduino code, avoid using delay() and Serial.print(). The serial monitor and its printing functions use USART serial communication. Arduino sends data to the serial monitor on the computer via the HC-05 wireless communication. USART is slow and causes lag. The code uses a baud rate of 9600 for USART. Each character you print needs 1 start bit, 8 data bits and 1 stop bit (default settings on Arduino does not use parity bit). Thus, a character takes up 1 + 8 + 1 = 10 bauds, or 960 characters per seconds, which is very slow compared to Arduino's internal processing speed. A character can be a letter, digit, space or anything else in the ASCII table. If you really need to use print, increase the baud rate on both serial monitor and HC-05 would reduce the lag.

Step 5: Debugging Common Problems

If everything works, skip this step.

  1. Add "forward(); analogWrite(rightSpeed, maxSpeed); analogWrite(leftSpeed, maxSpeed); paused = true;" to the end of void setup(), do not use serial monitor. Both car wheels should spin in the forward position. This tests if you connected you motor control module (L293D) correctly.
  2. If not spinning, lift car up to see if the wheels spin without the friction of the ground
  3. If motor spins in the opposite direction, switch the wires of the motor (spinning in the opposite direction) connection to the motor control module
  4. While running the program, enter character 'i' into the serial monitor for a printout of key variables
  5. Gyrox should be approximately zero (<0.3), and angle should stay relatively unchanged when car is stationary
  6. Entering a and d into the monitor should change target angle
  7. The code assumes turning left increases angle value and right decreases angle. If yours is opposite, try 'angle = -roll; //with a negative sign' on line 82
  8. If angle value changes significantly when pitching the nose of the car up, down or car spins in the axis of its forward travel. angle might equal pitch or yaw (or their negative values).
  9. Change count < 6 to count != 0 on line 129, run the program, then enter 'i' into the serial monitor. If elapsedTime > 5ms:
  10. read Step 4
  11. or keep != 0 on line 129, if you are just are just concern about having a smooth turn, but not the accuracy of the gyro sensor over time
  12. When turning the car left/right, it takes a long time for the car to reach its final position / it vibrates for too long
  13. read Step 4 to reduce lags in the program
  14. change count < 6 to count != 0 on line 129
  15. use leftSpeedVal = changeSpeed(leftSpeedVal, +1); to change leftSpeedVal, rather than leftSpeedVal++; to prevent the variable from exceeding 255 (analogWrite() cannot correctly take values >255)
  16. Wireless module is not communicating with Arduino or receiver
  17. if Arduino is powered with batteries via its Vin pin. While connected to Arduino, the voltage across the batteries should be strictly between 7 to 12V, otherwise Arduino is unstable
  18. If using HC-05, reset its connection to computer, by removing it from the list of Bluetooth devices on the computer, then reconnect
  19. see: https://www.instructables.com/How-to-Connect-HC-05-to-Windows-1011-Mac-Apple-Com/
  20. Cannot upload code from computer to Arduino board
  21. close all serial devices on computer (which includes serial monitor, python file running pySerial library or PUTTY)
  22. If using USART/UART wireless communication (includes HC-05), cannot upload code to Arduino board
  23. I uploaded my code via wired USB connection, not via Bluetooth/wireless. After the upload, I unplug the USB and reconnected the HC-05
  24. USART communication typically takes up the TX and RX pin on the Arduino board, but uploading code also uses these two pins. When uploading code, USART device's TX and RX need to be unplugged
  25. see: https://www.instructables.com/How-to-Connect-HC-05-to-Windows-1011-Mac-Apple-Com/

Feel free to ask questions in the comments section below.

Remote Control Contest

Participated in the
Remote Control Contest