Turtlebot3 - Motor Control

Subscribe Send me a message home page tags


#ros  #turtlebot3  #noetic  #roserial 

In this post, we report the experience and lesson learned when commanding motors on TurtleBot3.

System Configuration

Task Description

The task is to use ROS to send commands to control the motors on Turtlebot3.

Recall the basic setup: Two motors of the robot are controlled by the OpenCR board, which is connected to Raspberry Pi via USB.

basic_setup.jpg

To control the motors, we need to

Motor Driver

To install the driver, we can follow the official instruction Turtlebot3 - OpenCR Setup. Note that there is a sanity check at the end of the tutorial. We should be able to push the buttons mentioned in the tutorial to trigger motor movement. If the sanity check fails, please follow the instruction in the section "Click here to expand more details about the firmware upload using Arduino IDE".

motor_issue_solution.jpg
Summary - At this point, we should be able to trigger motor movement by pushing the buttons on the OpenCR board. This indicates that the OpenCR is set up correctly.

Launch roscore on Raspberry Pi

Here we deviate from the official TurtleBot3 tutorial. In the official tutorial, the roscore is launched on the remote PC and then we "bring up" TurtleBot3 on Raspberry Pi. See more details at TurtleBot3 - Quick Start Guid - Bringup.

Because we only want to test the motor control part and we don't want to launch other things prepared by the TurtleBot3 automatically, we should not use the turtlebot3_bringup package. Instead, we can build the turtlebot packages manually and then launch roscore on Raspberry Pi directly.

First, we need to checkout turtlebot packages. The turtlebot3 packages can be found at ROBOTIS-GIT/turtlebot3. This repository is the ros workspace we need. Here is the project structure of my ros workspace:

ros_workspace
└── src
    ├── CMakeLists.txt -> /opt/ros/noetic/share/catkin/cmake/toplevel.cmake
    ├── ISSUE_TEMPLATE.md
    ├── LICENSE
    ├── README.md
    ├── turtlebot3
    ├── turtlebot3_bringup
    ├── turtlebot3_description
    ├── turtlebot3_example
    ├── turtlebot3_navigation
    ├── turtlebot3_slam
    └── turtlebot3_teleop

If we build the workspace, it will fail due to missing dependencies. The problem is that the turtlebot3_bringup package depends on turtlebot3_msgs and we don't have turtlebot3_msgs in our workspace. This dependency requirement can be found in the turtlebot3_bringup/package.xml file.

The fix is simple, we just need to check out the turtlebo3_msgs package at ROBOTIS-GIT/turtlebot3_msgs.

Note: Most turtlebot packages list other related resources. For example, see Open Source related to TurtleBot3.

Now we can build the workspace by executing the following commands:

cd ~/catkin_ws
catkin_make
source devel/setup.bash

If the catkin_make command does not exist, make sure the following command is executed

source /opt/ros/noetic/setup.bash

After the workspace is built, we can now launch roscore by executing the command on Raspberry Pi:

roscore
Summary - At this point, we have compiled the turtlebot packages manually on Raspberry Pi and the ros core is running on Raspberry Pi too.

Communication between Serial Node and Ros Core

Now we need to "bridge" the OpenCR and Raspberry Pi. We've uploaded code to OpenCR when we set up the OpenCR board. The code enables OpenCR to behave like a ROS node. Therefore, this "bridge" step is similar to a node joining a cluster.

The "bridge" operation is handled by rosserial and we need to install it on Raspberry Pi.

We can install the rosserial package by using the command

WARNING: The command below may not be accurate. Also, please make sure the ROS version is correct. In our example, the ROS version is noetic.
sudo apt-get install ros-noetic-rosserial-arduino

Because OpenCR is connected to Raspberry Pi via USB, we need to tell ros the serial port to listen to.

Here is the interesting part, I still cannot find any formal way to detect the serial devices. But I found one solution online:

# Save the list of devices to the file
ls -1 /dev > /home/ubuntu/tmp/before.txt

# Plug the usb
ls -1 /dev > /home/ubuntu/tmp/after.txt

# Compare the differnece
diff -u /home/ubuntu/tmp/before.txt /home/ubuntu/tmp/after.txt

The output will look like the following:

--- /home/ubuntu/tmp/before.txt	2022-06-11 00:10:32.897681353 +0000
+++ /home/ubuntu/tmp/after.txt	2022-06-11 00:11:10.681737940 +0000
@@ -307,6 +307,7 @@
 ptyzf
 random
 rfkill
+serial
 shm
 snapshot
 snd
@@ -380,6 +381,7 @@
 tty7
 tty8
 tty9
+ttyACM0
 ttyAMA0
 ttyS0
 ttya0

In our case, we know the port is ttyACM0.

It's time to establish the communication between the serial node and ros core on Raspberry Pi. We can execute the commands below:

cd ~/ros_workspace
source devel/setup.bash
rosrun rosserial_python serial_node.py _port:=/dev/ttyACM0 _baud:=115200

Note: Each time we launch a new terminal, we need to execute the source ~/ros_workspace/devel/setup.sh command. It's better to add it to the ~/.bashrc file.

Summary - At this point, we know the serial port of the OpenCR connection. We've also run the rosserial tool so that ros core knows about the serial node.

Publish Motor Control Commands

The goal is to trigger motor movement. However, we don't need to go to the low level and communicate with the motor directly because ultimately what we want is to control the robot movement. The robot movement can be controlled by setting the linear and angular velocities. The API provided in ROS for this purpose is geometry_msgs/Twist message. The unit in the message is m/s and rad/s.

Note:

To publish a command, we can use the following command:

rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}'
WARNING: The empty spaces matter in the above message.

How does the /cmd_vel topic work? We notice that if we stop publishing to the /cmd_vel topic, the wheels stop moving. It suggests that the system requires a constant stream of commands to keep moving. This behavior is also mentioned in Understanding ROS using Turtlesim. It seems that the system is modeled as a state machine and we need to provide a stream of ticks. This is a reasonable approach because in this way we can no longer tell the difference between a simulated system and a real physical system. The simulation is usually trigged by ticks.

But what is the rate of the tick? There must be something in the code we upload to the OpenCR because the code defines the behavior of the serial node. The code we upload can be found at ROBOTIS-GIT/OpenCR/tree/master/arduino/opencr_arduino/opencr/libraries/turtlebot3/examples/turtlebot3_burger/turtlebot3_core.

If we check the turtlebot3_core_config.h file, we can see the line below:

1
2
#define CONTROL_MOTOR_SPEED_FREQUENCY          30   //hz
#define CONTROL_MOTOR_TIMEOUT                  500  //ms

One of the usages of these two variables is in the turtlebot3_core.ino file: (See code)

1
2
3
4
5
6
7
8
9
10
11
12
if ((t-tTime[0]) >= (1000 / CONTROL_MOTOR_SPEED_FREQUENCY))
{
updateGoalVelocity();
if ((t-tTime[6]) > CONTROL_MOTOR_TIMEOUT)
{
  motor_driver.controlMotor(WHEEL_RADIUS, WHEEL_SEPARATION, zero_velocity);
}
else {
  motor_driver.controlMotor(WHEEL_RADIUS, WHEEL_SEPARATION, goal_velocity);
}
tTime[0] = t;
}

As we can see in the code, the velocy is set up zero when there is a timeout event.

Summary - At this point, we should see wheels moving when we publish messages to /cmd_vel topic.

----- END -----