In this project, we had build a differential drive robot using EPS32 microcontroller. ROS2 humble is used for controlling the robot. Micro-ROS is uses for connecting ESP32 to ROS2 running computer.
- Ubuntu 22.04
- ROS2 humble (Install ros2 humble by following this link)
- Arduino IDE
- Basic knowledge on ROS2 concepts (Follow this youtube playlist, if you are beginner in ROS2)
- Setup micro-ROS libraries on arduino IDE for ESP32 and install micro- ROS agent on your computer by following this tutorial.(IMPORTANT)
- ESP32 subscribes
/cmd_vel
topic published from computer usingteleop_twist_keyboard
/cmd_vel
topic contains required linear and angular velocities of the robot.- Linear and angular velocities are converted to right wheel and left wheel velocities using differential drive kinematics equations.
- Motor speed is controlled using PID controller. PID controller is implemented on ESP32 by taking feedback from wheel encoders.
- Wheel Odometry is published from ESP32.
PID controller is used for closed loop speed control. Watch the given video for more info.
For more understanding on PID controller, watch the playlist provided by matlab
Getting startedRobot chassis
I had build the chassis using Form board, Which can be easily cut and joined by glue.
Two N20 motor and one caster wheel is used.
- Setup micro-ROS libraries on arduino IDE for ESP32 and install micro- ROS agent on your computer by following this tutorial.(IMPORTANT)
After setup of the library upload the code provided at the end. I'm not explaining the code line by line, but I had added some comments on code for understanding. If you wish to have detailed code explanation please comment below.
Before uploading the code some parameters has to set based on our robot, they are
int8_t L_FORW = 26;
int8_t L_BACK = 27;
int8_t L_enablePin = 25;
int8_t L_encoderPin1 = 18; //Encoder Output of pin1 must connected with intreput pin of Esp32.
int8_t L_encoderPin2 = 21;
//right wheel
int8_t R_FORW = 33;
int8_t R_BACK = 32;
int8_t R_enablePin = 5;
int8_t R_encoderPin1 = 23; //Encoder Output of pin1 must connected with intreput pin of Esp32.
int8_t R_encoderPin2 = 15;
//parameters of the robot
float wheels_y_distance_ = 0.1;
float wheel_circumference_ = 0.1256;
float wheel_radius=0.02;
//encoder value per revolution of left wheel and right wheel
int tickPerRevolution_LW = 1050;
int tickPerRevolution_RW = 1055;
int threshold = 150;
//pid constants of left wheel
float kp_l = 1.8;
float ki_l = 5;
float kd_l = 0.01;
//pid constants of right wheel
float kp_r = 2.25;
float ki_r = 5;
float kd_r = 0.01;
Let's explain it step by step
Initialization of the pins
int8_t L_FORW = 26;
int8_t L_BACK = 27;
int8_t L_enablePin = 25;
int8_t L_encoderPin1 = 18; //Encoder Output of pin1 must connected with intreput pin of Esp32.
int8_t L_encoderPin2 = 21;
//right wheel
int8_t R_FORW = 33;
int8_t R_BACK = 32;
int8_t R_enablePin = 5;
int8_t R_encoderPin1 = 23; //Encoder Output of pin1 must connected with intreput pin of Esp32.
int8_t R_encoderPin2 = 15;
Initializing the distance between two wheels.
float wheels_y_distance_ = 0.1;
Wheel radius and wheel circmference is initialized
float wheel_radius=0.02;
float wheel_circumference_ = 2*3.14*wheel_radius;
Tick per revolution of a wheel is the encoder value per revolution of the motor. You can find it on the datasheet of the motor or by yourself. For finding encoder value per revolution (Tick per revolution) follow this tutorial Reading the encoder value of N20 motor using ESP32
int tickPerRevolution_LW = 1055;
int tickPerRevolution_RW = 1055;
Threshold is a value we found while implementing this project. I found that motor is not moving when pwm value is in between 0 to 165. When pwm value greater than 165 the motor start moving. So I decided to set a threshold value of 150. This threshold is added with the pwm signal generated by the PID controller. Experiment it by yourself.
int threshold = 0;
Initializing the PID controller constants for both wheels. I found this values better for my motors. PID controller constants has to be tuned according to the motor. If you find one motor starts rotating before another change the PID value ofthat motor. Experiment with different values by yourself (It not recommended to increase kd values very larger, experiment by yourself. ).For more details watch the video given on top about PID controller.
//pid constants of left wheel
float kp_l = 1.8;
float ki_l = 5;
float kd_l = 0.01;
//pid constants of right wheel
float kp_r = 2.25;
float ki_r = 5;
float kd_r = 0.01;
Now lets setup the odometry library for publishing odometry messages.
Go to this github release. Download the zip file
Unzip the file and place Odometry folder inside the unziped file in the location ~/Arduino/libraries
Compile the code given at end of this document and upload it.
Create a ros2 workspace. Open terminal, make sure you are at home directory, make a ros2 workspace directory. I'm naming it as Diffdrive_ws you can name it as you wish.
mkdir Diffdrive_ws
Open the work space
cd Diffdrive_ws/
Create a source folder
mkdir src
Build using colcon build
colcon build
Now we have created a ros2 workspace for our robot. Now we want to install required packages.
Get into our workspace
cd Diffdrive_ws/
Clone the github repo which contain necessary packages
git clone https://github.com/amalshaji4540/diffdrive_ws.git src/
Build the packages.
colcon build
When first time you build it will take some time.
Source the Diffdrive_ws
workspace
source ~/Diffdrive_ws/install/setup.bash
For running the files inside Diffdrive_ws you need to source it every time when you open a new terminal, for avoiding it place the source command inside .bashrc
file. The following commands opens .bashrc
file
gedit ~/.bashrc
Source command is pasted inside.bashrc folder
Connect ESP32 to the computer using usb cable.
First find your serial device name
ls /dev/serial/by-id/*
Run micro ros agent by adding serial device name as parameter
Make sure that you had sourced ros2, microros and Diffdrive installations. Otherwise you will get error
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/serial/by-id/usb-1a86_USB_Single_Serial_5553003870-if00
Press the reset button (EN)
Open a new terminal and check the topics list
ros2 run topic list
For visualizing nodes and topics run,
rqt_graph
Here we can see that /micro_ros_esp32_node
is subscribed /cmd_vel
topic and publishing to the topic /odom/unfiltered
Let's publish messages to cmd_vel
topic, for that we are using teleop_twist_keyboard.
ros2 run teleop_twist_keyboard teleop_twist_keyboard
Use of different keys for moving the robot
I - used for moving forward
k- used to stop
j- used for turning left
l- used for turning right
, -used to moving backward
Just one press on the key is enough. While continuously pressing a key, control of the robot is lost. we are studying this issue.
Make sure that you are pressing small letters. Because capital letters send linear velocity in y direction. We are only using linear velocity in x direction and angular velocity in z direction for our differential drive robot. Capital keys work for forward and backward keys but not for sidewise keys.
Let's learn more about teleop_twist_keyboard, run the below command for getting more info about a node.
ros2 node info /teleop_twist_keyboard
Here we can see that teleop_twist_keyboard
publishes to the topic /cmd_vel
and message type is geometry msgs/msg/Twist.
For printing what messages are published to /cmd_vel
topic, open another terminal and run
ros2 topic echo /cmd_vel
Now select the teleop_twist_keyboard and press any one of the keys shown on the terminal.
Here we can see that linear and angular velocities along x, y, z axis is published by teleop_twist_keyboard.
We are working with a differential drive robot which can be controlled using linear velocity in x axis and angular velocity in z axis.
For visualizing the robot transform run the following command
ros2 launch diffdrive_description display.launch.xml
display.launch.xml
file contains the following code.
<launch>
<let name="urdf_path" value="$(find-pkg-share diffdrive_description)/urdf/my_robot.urdf.xacro"/>
<let name="rviz_path" value="$(find-pkg-share diffdrive_description)/rviz/diffdrive_config.rviz"/>
<node pkg="robot_state_publisher" exec="robot_state_publisher">
<param name="robot_description" value="$(command 'xacro $(var urdf_path)')"/>
</node>
<node pkg="joint_state_publisher_gui" exec="joint_state_publisher_gui"/>
<include file="$(find-pkg-share robot_localization)/launch/ekf_new.launch.py">
</include>
<node pkg="rviz2" exec="rviz2" output="screen" args="-d $(var rviz_path)"/>
</launch>
Launch files are used for running multiple nodes and launch files using a single command line. By running this display.launch.xml
launch file robot_state_publisher, joint_state_publisher and rviz2 nodes are executed and ekf_new.launch.py
launch file is also executed
For localization we are using robot_localization package. Which subscribes odometry message published by ESP32 and publishes filtered odometry and transforms. We are using wheel odometry for localization. We can add more sensors like IMU for localization, it increases the accuracy.
Output
Robot localization package ros2 github
Robotics Back-End youtube channel
Synchronizing Motor Position with Encoders, PID Control and Arduino
Comments