Welcome to Level 2! In this level, you'll create a ROS package to control a virtual rover using a simple publisher-subscriber communication pattern. This will help you understand the core communication mechanisms in ROS, which are essential for building modular and scalable robotic applications.
- Create a package inside the Docker image
- Create a simple publisher-subscriber system
Imagine you are tasked with developing the command system for our rover. Your job is to create a control interface that sends movement commands to the rover, and a system that interprets these commands to move the rover accordingly.
You will create a publisher that sends Twist messages (a translation and rotation vector, see doc) based on user input, and a subscriber that receives these messages and interprets them as movement commands.
The publisher will send a Twist message, which the subscriber will interpret as a trajectory command. The user will input letters to indicate the trajectory to send:
w: Move Forward (+x)a: Slide Left (-z)s: Move Backward (-x)d: Slide Right (+z)t: Rotate Left (+ry)y: Rotate Right (-ry)
The subscriber will receive the data and transform it into a coherent string, filtering out impossible commands:
Tx axis: “Go [Forward / Backward]”Tx axisandRy axis: “Go [Left / Right]”Tz axis: “Slide [Left / Right]”Ry axis: “Rotating on itself to the [Left / Right]”Tz axisandRy axis,Tx axisandTz axis: “Forbidden move”
It will also print the position: “New Position: [x, z, orientation (Ry)]”, where translation is updated after rotation if both are present.
-
Open the Docker container:
If the Docker container is not already running, start it using the
run.shscript (for Mac or Linux) orrun.bat(for Windows):./run.sh (Mac or Linux) run.bat (Windows)
-
Create a new ROS package:
Inside the Docker container, create a new workspace and a package:
cd src ros2 pkg create --build-type ament_python rover_commandsThis command creates a new ROS package named
rover_commandsusing Python. The--build-type ament_pythonspecifies that we are using Python for this package. -
Navigate to the package directory:
cd rover_commands
-
Open a Text Editor/IDE:
If it is not the case, open the assignement in your favorite text editor or IDE. We recommend VS Code for its ease of use and installation.
Once this is done, you can edit the files directly on your computer, they will also be updated in real-time in Docker!
You will see that in the assignement a new folder appeared, it is your package. It should have the following content:
| rover_commands | -- | resource | | -- | rover_commands | -- | rover_commands | | -- | __init__.py | -- | test | | -- | some files | -- | package.xml | -- | setup.cfg | -- | setup.py -
Create the publisher script:
Inside the folder rover_commands of the package, we will place all the source code of our package. Create a file named
publisher.pyin your editor.Check that it also appears in Docker using:
cd ~/dev_ws/src/rover_commands/rover_commands ls
-
Edit the publisher script:
Open
publisher.pywith a text editor and add the following code:import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class TrajectoryPublisher(Node): def __init__(self): super().__init__('trajectory_publisher') # TODO: Create a publisher of type Twist # Your code here self.get_logger().info('Publisher node has been started.') # TODO: Create a loop here to ask users a prompt and send messages accordingly # Function that prompts user for a direction input, and sends the command def cmd_acquisition(self): command = input("Enter command (w/a/s/d/t/y - max 2 characters): ") # TODO: Complete the function to transform the input into the right command. # Your code here pass def main(args=None): rclpy.init(args=args) # Init ROS python node = TrajectoryPublisher() # Create a Node instance rclpy.spin(node) # Run the node in a Thread node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
This script defines a
TrajectoryPublishernode that waits for user input and publishes aTwistmessage based on the command. Thecmd_acquisitionfunction is called indefinitely to prompt for user input.Now your turn to complete it! Use online resources such as ROS doc.
-
Update the
setup.py:In the
rover_commandsdirectory, open thesetup.pyfile and add the following entry to theconsole_scriptslist:entry_points={ 'console_scripts': [ 'publisher = rover_commands.publisher:main', ], },
This tells ROS to register the
publisherscript as an executable node.
-
Create the subscriber script:
Again, inside the folder rover_commands of the package, create a file named
subscriber.pyin your editor.Check that it also appears in Docker using:
cd ~/dev_ws/src/rover_commands/rover_commands ls
-
Edit the subscriber script:
Open
subscriber.pywith a text editor and add the following code:import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class TrajectorySubscriber(Node): def __init__(self): super().__init__('trajectory_subscriber') # TODO: Create a subscriber of type Twist, that calls listener_callback # Your code here self.get_logger().info('Subscriber node has been started.') self.position = {'x': 0.0, 'z': 0.0, 'ry': 0.0} def listener_callback(self, msg): # TODO: Interpret the received commands and log the result using self.get_logger().info() # Your code here self.get_logger().info(f'New Position: {self.position}') def main(args=None): rclpy.init(args=args) node = TrajectorySubscriber() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
This script defines a
TrajectorySubscribernode that listens forTwistmessages on thetrajectorytopic. It interprets the commands and prints the corresponding action and updated position.Now your turn to complete it! Use online resources such as ROS doc.
-
Update the
setup.py:In the
rover_commandsdirectory, open thesetup.pyfile and add the following entry to theconsole_scriptslist:entry_points={ 'console_scripts': [ 'publisher = rover_commands.publisher:main', 'subscriber = rover_commands.subscriber:main', ], },
This tells ROS to register the
subscriberscript as an executable node.
-
Build the package:
In the
dev_wsdirectory, build the package:cd ~/dev_ws/ colcon build
This command compiles the package and sets up the necessary environment.
-
Source the setup file:
After building, source the setup file to overlay the workspace on your environment:
. install/setup.bashThis command sets up the environment variables needed to run the nodes. Now, your terminal is aware of the existing nodes. You will have to execute this command on every terminal you open to help it find your nodes.
-
Run the publisher node:
In one terminal inside the Docker container, run the publisher node:
ros2 run rover_commands publisher
This starts the
TrajectoryPublishernode, which waits for user input and publishes the correspondingTwistmessage. -
Run the subscriber node:
Open a new terminal inside the Docker container and run the subscriber node:
docker exec -it base_humble_desktop bash . install/setup.bash ros2 run rover_commands subscriber
This starts the
TrajectorySubscribernode, which listens forTwistmessages on thetrajectorytopic and prints the interpreted commands and updated position.
-
Start the Publisher Node:
In the first terminal, you will be prompted to enter commands (e.g.,
w,a,s,d,t,y). -
Monitor the Subscriber Node:
In the second terminal, you will see the subscriber node interpreting the commands and printing the corresponding actions and updated position.
Example Output:
Enter command (w/a/s/d/t/y): w [INFO] [publisher]: Published: linear: x: 1.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- [INFO] [subscriber]: Go Forward [INFO] [subscriber]: New Position: {'x': 1.0, 'z': 0.0, 'ry': 0.0}
By completing these steps, you have created a ROS package with a simple publisher-subscriber system to control the rover. The publisher sends trajectory commands, and the subscriber interprets and prints the trajectory.
Congratulations on completing Level 2! You are now ready to move on to more complex interactions in Level 3.
