Background
This is part of a series of posts where I document my progress in building an autonomous mobile bot from scratch. The ultimate goal of this project is to build a robot that can accomplish the following tasks:
Autonomously map an indoor space using SLAM and save the generated map
Load the generated map file and localize the robot inside the map
Move the robot around by sending move commands from a GUI that shows the map
During the first few months of the project, I developed an algorithm for autonomous SLAM in a simulated environment and I documented the process in part 1.
With the simulation stage complete, it was time to move onto the real world. I built a two-wheeled robot with LiDAR, IMU, and wheel encoders (same as the robot in the simulation).
Figure 1: Picture of my differential drive robot.
Figure 2: Top-down view of figure 1.
I bought an AlphaBot2-Pi and heavily modified it to turn it into the robot shown above. I swapped the motors that came with the original Alphabot2-Pi with motors that had hall effect encoders. I also added a small breadboard with an Arduino Nano and an IMU sensor. The LiDAR sensor is not shown in the images, but is attached right next to the IMU sensor.
Upon completing the build and testing the robot, I encountered the first real-world problem. When I ran the two DC motors at the same PWM duty cycle, I was getting a noticeable difference in the angular velocities of the wheels. The right wheel was spinning slower than the left wheel, which caused the robot to make a slight turn to the right instead of going straight.
This difference was later discovered to be a fault in the hardware. Even though I set the duty cycle to be 50% in the software, the output duty cycle going to the motors were different. The right wheel was receiving lower average voltage than the left wheel, which explains its slower speed.
To fix this issue, I decided to implement a PID controller. PID controllers are almost always used when controlling the speed of a motor. This is mainly because the speed of the motor and the PWM duty cycle is not a linear relationship. You cannot expect a motor to spin at 50% its maximum speed simply because you set the duty cycle to 50%.
Figure 3: Graph of PWM duty cycle vs motor speed.
Figure 4: Graph of PWN duty cycle vs motor speed under different load.
As shown above, PWM duty cycle does not translate linearly to motor speed. Even if the motor is subjected to the same voltage, it will run at differing speeds based on factors, such as load, temperature, friction, etc.
The essence of the PID controller is to take the difference between the current wheel velocity and the desired wheel velocity (this is often termed error) and use that difference to adjust the value of the duty cycle.
Figure 5: Block diagram of a PID controller.
Block diagram is often used to illustrate how the PID controller operates. In the case for DC motor control, the diagram is interpreted as follows:
r(t) - desired wheel velocity
u(t) - PWM duty cycle
y(t) - actual wheel velocity
e(t) - error
Plant Process - DC motor
The actual wheel velocity y(t) is subtracted from the desired wheel velocity r(t) to yield the error e(t). The error is then used to compute the PWM duty cycle y(t), which is fed into the DC motor to produce a new wheel velocity.
Implementation
Implementation was done in C++ with ROS2 Humble.
Firstly, I made a listener that listens on the /wheel_vels topic to constantly update the global variables wheel_vel_r and wheel_vel_l, which are the actual wheel velocities of the left and right DC motors.
This data is transferred from the Arduino Nano to the Raspberry Pi over serial communication and there is a ROS node dedicated to reading from serial and publishing the data to on the topic /wheel_vels.
While the wheel velocity variables are getting updated, the PID controller algorithm executes whenever a message is received over the /cmd_vel topic
The first step in the algorithm is to compute the duration of time since the last iteration of the PID controller.
This value is later used to approximate the derivative and integral.
Then you convert the desired robot velocity received from the /cmd_vel topic to individual wheel velocities.
These equations were obtained from an online resource.
Although this equation is for obtaining the robot velocity from the wheel velocities, we can manipulate the variables to do the opposite.
Then, the error is calculated and all the terms are inputted into the computeGain() function to obtain the corresponding increase/decrease in the PWM duty cycle.
The computeGain() function is where the Proportion, Integral, and Derivative gains are calculated.
In the discrete world of computers, derivative and integrals must be approximated. Integrals are approximated using the Riemann sum and the derivative is approximated by the average slope between current and the previous time point. These approximations become more accurate the smaller the value of dt is.
The gain is then added to the current PWM duty cycle for each wheel. The actual value of the duty cycle is capped from -100 to 100. -100 means the motor should spin backwards with 100% duty cycle.
Result
Figure 6: 50% duty cycle applied to left and right motors. The right motor has a slower speed, which results in the robot turning to the right.
Figure 7: PID controller used to ensure that both left and right motors turn at the same angular velocity.
Without the PID controller, the robot makes a right turn because the right wheel is turning slower than the left wheel. With the PID controller, the appropriate duty cycle is computed and the robot moves in a straight line.
Comentarios