Skip to content

Part 1: Getting Started with ROS2

Introduction

Exercises: 8
Estimated Completion Time: 2 hours

Aims

In the first part of this lab course you will learn the basics of ROS and become familiar with some key tools and principles of the framework which will allow you to program robots and work with ROS applications effectively. For the most part, you will interact with ROS using the Linux command line and so you will also become familiar with some key Linux command line tools that will help you. Finally, you will learn how to create some basic ROS Nodes using Python and get a taste of how ROS topics and messages work.

Intended Learning Outcomes

By the end of this session you will be able to:

  1. Control a TurtleBot3 Robot, in simulation, using ROS.
  2. Launch ROS applications using ros2 launch and ros2 run.
  3. Interrogate running ROS applications using key ROS command line tools.
  4. Create a ROS package comprised of multiple nodes and program these nodes (in Python) to communicate with one another using ROS Communication Methods.
  5. Create a custom ROS message interface and create Python Nodes to use this.
  6. Navigate a Linux filesystem and learn how to do various filesystem operations from within a Linux Terminal.

Exercises

Additional Resources

First Steps

Step 1: Accessing a ROS2 Environment for this Course

If you haven't done so already, see here for all the details on how to install or access a ROS environment for this course (TODO).

Step 2: Launch ROS

Launch your ROS environment.

  1. OPTION 1
  2. OPTION 2
  3. etc...

Either way, you should now have access to ROS via a Linux terminal instance, and we'll refer to this terminal instance as TERMINAL 1.

Step 3: Download The Course Repo

We've put together a few ROS packages specifically for this course. These all live within this GitHub repo, and you'll need to download and install this into your ROS environment now, before going any further.

[TODO: Create a ROS2 Workspace first??]

  1. In TERMINAL 1, Navigate into the "ROS2 Workspace" using the cd command1:

    cd ~/ros2_ws/src/
    
  2. Then, run the following command to clone the Course Repo from GitHub:


    TERMINAL 1:

    git clone https://github.com/tom-howard/tuos_ros.git -b humble
    


  3. Once this is done, you'll need to build this using a tool called "Colcon"2:


    TERMINAL 1:

    cd ~/ros2_ws/ && colcon build --packages-up-to tuos_ros && source ~/.bashrc
    


Don't worry too much about what you just did, for now. We'll cover this in more detail throughout the course. That's it for now though, we'll start using some of the packages that we've just installed a bit later on.

Exercise 1: Launching a simulation and making a robot move

Now that you're all up and running, let's launch ROS and fire up a simulation of our TurtleBot3 Waffle robot...

  1. In the terminal enter the following command to launch a simulation of a TurtleBot3 Waffle in an empty world:


    TERMINAL 1:

    ros2 launch turtlebot3_gazebo empty_world.launch.py
    


  2. A Gazebo simulation window should open and within this you should see a basic representation of the robot's that you'll work with in the lab (TODO):

[FIGURE]

  1. With the Gazebo simulation up and running, return to your terminal and open up a second terminal instance (TERMINAL 2)

    [TODO: use tmux??]

  2. In this new terminal instance enter the following command:


    TERMINAL 2:

    ros2 run turtlebot3_teleop teleop_keyboard
    


  3. Follow the instructions provided in the terminal to drive the robot around using specific buttons on your keyboard:

Summary

You have just launched a number of different applications on a ROS Network using two different ROS commands - ros2 launch and ros2 run:

  1. ros2 launch turtlebot3_gazebo empty_world.launch.py
  2. ros2 run turtlebot3_teleop teleop_keyboard

These two commands have a similar structure, but work slightly differently.

The first command you used was a launch command, which has the following two parts to it (after the launch bit):

ros2 launch {[1] Package name} {[2] Launch file}

Part [1] specifies the name of the ROS package containing the functionality that we want to execute. Part [2] is a file within that package that tells ROS exactly what scripts ('nodes') that we want to launch. We can launch multiple nodes at the same time from a single launch file.

The second command was a run command, which has a structure similar to launch:

ros2 run {[1] Package name} {[2] Node name}

Here, Part [1] is the same as the launch command, but Part [2] is slightly different: {[2] Node name}. Here we are directly specifying a single script that we want to execute. We therefore use ros2 run if we only want to launch a single node on the ROS network: the teleop_keyboard node (a Python script), in this case.

ROS Packages & Nodes

Packages

ROS applications are organised into packages. Packages are basically folders containing scripts, configurations and launch files (ways to launch those scripts and configurations), all of which relate to some common robot functionality. ROS uses packages as a way to organise all the programs running on a robot.

Info

The package system is a fundamental concept in ROS and all ROS programs are organised in this way.

You will create a number of packages throughout this course, each containing different nodes, launch files and other things too. We'll start to explore this later on in this part of the course.

Nodes

ROS Nodes are executables that perform specific robot tasks and operations. Earlier on we used ros2 run to execute a node called teleop_keyboard, which allowed us to remotely control (or "teleoperate") the robot, for example.

Question

What was the name of the ROS package that contained the teleop_keyboard node? (Remember: ros2 run {[1] Package name} {[2] Node name})

A ROS robot might have hundreds of individual nodes running simultaneously to carry out all its necessary operations and actions. Each node runs independently, but uses ROS communication methods to share data with the other nodes on the ROS Network.

The ROS Network

We can use the ros2 node command to view all the nodes that are currently active on a ROS Network.

Exercise 2: Visualising the ROS Network

You should currently have two terminal instances active: the first in which you launched the Gazebo simulation (TERMINAL 1) and the second with your teleop_keyboard node active (TERMINAL 2).

  1. Open up a new terminal instance now (TERMINAL 3).
  2. Use the following command to have a look at which nodes are currently active on the network:


    TERMINAL 3:

    ros2 node list
    


    Only a handful of nodes should be listed:

    /camera_driver
    /gazebo
    /teleop_keyboard
    /turtlebot3_diff_drive
    /turtlebot3_imu
    /turtlebot3_joint_state
    /turtlebot3_laserscan
    
  3. We can visualise the connections between the active nodes by using an application called RQT. RQT is a collection of graphical tools that allow us to interact with and interrogate the ROS network. Launch the main RQT application by entering rqt in TERMINAL 3 (you might see some warnings in the terminal when you do this, but don't worry about them):


    TERMINAL 3:

    rqt
    


    A window should then open:

  4. From here, we then want to load the Node Graph plugin. From the top menu select Plugins > Introspection > Node Graph.

  5. Select Nodes/Topics (all) from the top-left most dropdown, and in the Hide section uncheck everything except Debug and Params (you may then need to hit the refresh button):

    Here, nodes are represented by rectangles and topics by ellipses (hover over a region of the graph to enable colour highlighting).

    This tool shows us that (amongst other things) the /teleop_keyboard and /turtlebot3_diff_drive nodes are communicating with one another. The direction of the arrow tells us that /teleop_keyboard is a Publisher and /turtlebot3_diff_drive is a Subscriber. The two nodes communicate via a ROS Topic called /cmd_vel.

Publishers and Subscribers: A ROS Communication Method

ROS Topics are key to making things happen on a robot. Nodes can publish (write) and/or subscribe to (read) ROS Topics in order to share data around the ROS network. Data is published to topics using ROS Messages. As we've just learnt, the teleop_keyboard node was publishing messages to a topic (/cmd_vel) to make the robot move earlier.

Let's have a look at this in a bit more detail...

Exercise 3: Exploring ROS Topics and Messages

We can find out more about the /cmd_vel topic by using the ros2 topic command.

  1. Open up yet another new terminal instance (TERMINAL 4) and type the following:


    TERMINAL 4:

    ros2 topic list
    


    This shows us all the topics that are currently available on the ROS network (a lot of which we saw in the RQT Node Graph above):

    /camera/camera_info
    /camera/image_raw
    /clock
    /cmd_vel
    /imu
    /joint_states
    /odom
    /parameter_events
    /performance_metrics
    /robot_description
    /rosout
    /scan
    /tf
    /tf_static
    

    Let's find out a bit more about /cmd_vel...

  2. Use the topic info command now:


    TERMINAL 4:

    ros2 topic info /cmd_vel
    


    This should provide the following output:

    Type: geometry_msgs/msg/Twist
    Publisher count: 1
    Subscription count: 1
    

    We've now established the following information about /cmd_vel:

    1. The topic has 1 publisher writing data to it
    2. The topic also has 1 subscriber reading this data
    3. From RQT Node Graph we know that the /teleop_keyboard node is the publisher (i.e. the node writing data to the topic)
    4. The /turtlebot3_diff_drive node is receiving this data (and acting upon it). This node therefore monitors (i.e. subscribes to) the /cmd_vel topic and makes the robot move in the simulator whenever a velocity command is published.
    5. Data is transmitted on the /cmd_vel topic using an Interface. This particular interface type is: geometry_msgs/msg/Twist.

      The type field has three parts to it:

      1. geometry_msgs: the name of the ROS package that this interface belongs to.
      2. msg: that this is a topic message rather than another type of interface (there are three types of interface, and we'll learn about the other two later in this course).
      3. Twist: the actual message type (i.e., the way the data is structured)

      In summary then, we've established that if we want to make the robot move we need to publish Twist messages to the /cmd_vel topic.

  3. We can use the ros2 interface command to provide further information about the message structure:


    TERMINAL 4:

    ros2 interface show geometry_msgs/msg/Twist
    


    From this, we obtain the following:

    # This expresses velocity in free space broken into its linear and angular parts.
    
    Vector3  linear
            float64 x
            float64 y
            float64 z
    Vector3  angular
            float64 x
            float64 y
            float64 z
    

    We'll learn more about what this means in Part 2.

  4. To finish, enter Ctrl+C in each of the three terminals that should currently have ROS processes running (Terminals 1, 2 and 3). The associated Gazebo and RQT Node Graph windows should close as a result of this too.

Tip

Whenever you need to stop any ROS process use Ctrl+C in the terminal it's running in.

Creating Your First ROS Applications

Shortly you will create some simple publisher and subscriber nodes in Python and send simple ROS messages between them. As we learnt earlier though, ROS applications must be contained within packages, and so we need to create a package first in order to start creating our own ROS nodes.

It's important to work in a specific filesystem location when we create and work on our own ROS packages. These are called "Workspaces" and you should already have one ready to go within your local ROS environment3:

~/ros2_ws/src/

Note

~ is an alias for your home directory. So cd ~/ros2_ws/src/ is the same as typing cd /home/{your username}/ros2_ws/src/.

Important

All new packages must be located in the src folder of the workspace!!

Exercise 4: Creating your own ROS Package

The ros2 Command Line Interface (CLI) includes a tool to create a new ROS packages: ros2 pkg create. This tool supports two different "build types:"

  1. CMake (for packages containing nodes written in C++):

    ros2 pkg create --build-type ament_cmake

  2. Python (for packages containing nodes written in well, er, Python!):

    ros2 pkg create --build-type ament_python

    Packages are structured slightly differently in each case.

You can learn more about all this from the Official ROS2 Tutorials (if you're interested).

We'll be using Python throughout this course, but we'll actually take a slightly different approach to package creation that will provide us with a little more flexibility and ease of use (particularly for things we'll do later on in the Assignment #1 course and in Assignment #2). We've therefore created a helper script (inside the tuos_ros Course Repo) to help you create packages without using either of the above two commands. The approach we'll take is based on this tutorial (courtesy of the Robotics Backend), so feel free to look at this if you'd like to find out more. Then, simply follow the steps below to create your first ROS package for this course, using the create_pkg.sh helper tool.

  1. Navigate into the tuos_ros Course Repo that you downloaded earlier by using the Linux cd command (change directory). In TERMINAL 1 enter the following:


    TERMINAL 1:

    cd ~/ros2_ws/src/tuos_ros/
    


  2. Here you'll find the create_pkg.sh helper script. Run this now using the following command to create a new package called part1_pubsub:


    TERMINAL 1:

    ./create_pkg.sh part1_pubsub
    


  3. Navigate into this new package directory (using cd):


    TERMINAL 1:

    cd ../part1_pubsub/
    


    Info

    .. means "go back one directory," so that command above is telling cd to navigate out of the tuos_ros directory (and therefore back to ~/ros2_ws/src/), and then go into the part1_pubsub directory from there.

  4. tree is a Linux command which shows us the content of the current directory in a nice tree-like format. Use tree now to show the current content of the part1_pubsub directory:

    ~/ros2_ws/src/part1_pubsub$ tree
    .
    ├── CMakeLists.txt
    ├── include
    │   └── part1_pubsub
    │       └── minimal_header.hpp
    ├── package.xml
    ├── part1_pubsub
    │   ├── __init__.py
    │   └── tb3_tools.py
    ├── scripts
    │   └── minimal_node.py
    └── src
        └── minimal_node.cpp
    
    5 directories, 7 files
    
    • scripts: is a directory that will contain all the Python Nodes that we'll create (you'll notice a minimal_node.py already exists in there).
    • part1_pubsub: is a directory that we can use to store Python modules, that we can then import into our main Python nodes

      (from part1_pubsub.tb3_tools import ..., for example)

    • package.xml and CMakeLists.txt: are both files that define our package, and how it must be built (using colcon build). We'll explore these more shortly...

Exercise 5: Creating a publisher node

  1. From the root of your part1_pubsub package, navigate to the scripts folder using the cd command.
  2. touch is a Linux command that we can use to create an empty file. Use this to create an empty file called publisher.py, which we will add content to shortly:


    TERMINAL 1:

    touch publisher.py
    


  3. Use ls to verify that the file has been created, but use the -l option with this, so that the command provides its output in "a long listing format":


    TERMINAL 1:

    ls -l
    


    This should output something similar to the following:

    ~/ros2_ws/src/part1_pubsub/scripts$ ls -l
    total 4
    -rwxr-xr-x 1 student student 339 MMM DD HH:MM minimal_node.py
    -rw-r--r-- 1 student student   0 MMM DD HH:MM publisher.py
    

    This confirms that the file exists, and the 0 in the middle of the bottom line there indicates that the file is empty (i.e. its current size is 0 bytes), which is what we'd expect.

  4. We therefore now need to open the file and add content to it. We'd recommend using Visual Studio Code (VS Code) as an IDE for this course, which can be launched with the following command in TERMINAL 1:


    TERMINAL 1:

    code ~
    


    [TODO: does this work for Docker??]

  5. Using the VS Code File Explorer, locate the publisher.py file that you have just created (ros2_ws/src/part1_pubsub/scripts/) and click on the file to open it in the main editor.

  6. Once opened, copy the code provided here into the empty file and save it.

    Note

    It's important that you understand how this code works, so make sure that you read the annotations!

  7. Next, we need to add our publisher.py file as an executable to our package's CMakeLists.txt. This will ensure that it then gets built when we run colcon build (in the next step).

    In VS Code, open the CMakeLists.txt file that is at the root of your part1_pubsub package directory (ros2_ws/src/part1_pubsub/CMakeLists.txt). Locate the lines (near the bottom of the file) that read:

    # Install Python executables
    install(PROGRAMS
      scripts/minimal_node.py
      DESTINATION lib/${PROJECT_NAME}
    )
    

    Replace minimal_node.py with publisher.py to define this as a Python executable in your package:

    # Install Python executables
    install(PROGRAMS
      scripts/publisher.py
      DESTINATION lib/${PROJECT_NAME}
    )
    
  8. Now, use colcon to build your package.

    1. You MUST run this from the root of your Colcon Workspace (i.e.: ~/ros2_ws/), NOT the src directory (~/ros2_ws/src/), so navigate there now using cd:

      cd ~/ros2_ws/
      
    2. Then, use the following colcon command to build your package:

      colcon build --packages-select part1_pubsub --symlink-install
      

      What do the additional arguments above do?

      • --packages-select: Build only the part1_pubsub package, nothing else (without this colcon would attempt to build every package in the workspace).
      • --symlink-install: Ensures that you don't have to re-run colcon build every time you make a change to your package's executables (i.e. your Python files in the scripts directory).
    3. Finally, "re-source" your bashrc4:

      source ~/.bashrc
      
  9. We should now be able to run this node using the ros2 run command.

    Remember: ros2 run {package name} {script name}, so:


    TERMINAL 1:

    ros2 run part1_pubsub publisher.py
    


    ... Hmm, something not quite right? If you typed the command exactly as above and then tried to run it, you probably just received the following error:

    $ ros2 run part1_pubsub publisher.py
    No executable found
    

    When we create a file using touch it is given certain permissions by default. Run ls -l again (making sure that your terminal is in the right location: ~/ros2_ws/src/part1_pubsub/scripts/).

    The first bit tells us about the permissions that are currently set: -rw-r--r--. This tells us who has permission to do what with this file and (currently) the first bit: -rw-, tells us that we have permission to read or write to it. There is a third option we can set too though, which is the execute permission, and we can set this using the chmod Linux command...

  10. Run the chmod command as follows:


    TERMINAL 1:

    chmod +x publisher.py
    


  11. Now, run ls -l again to see what has changed:


    TERMINAL 1:

    ls -l
    


    We have now granted permission for the file to be executed too:

    -rwxr-xr-x 1 student student 1125 MMM DD HH:MM publisher.py
    
  12. OK, now use ros2 run again to (hopefully!) run the publisher.py node (remember: ros2 run {package name} {script name}).

    If you see a message in the terminal similar to the following then the node has been launched successfully:

    [INFO] [#####] [simple_publisher]: The 'simple_publisher' node is inisialised.
    

    Phew!

  13. We can further verify that our publisher node is running using a number of different tools. Try running the following commands in TERMINAL 2:

    1. ros2 node list: This will provide a list of all the nodes that are currently active on the system. Verify that the name of our publisher node is visible in this list (it's probably the only item in the list at the moment!)
    2. ros2 topic list: This will provide a list of the topics that are currently being used by nodes on the system. Verify that the name of the topic that our publisher is publishing messages to (/my_topic) is present within this list.

Interrogating ROS Topics

So far we have used the ros2 topic ROS command with two additional arguments: [TODO: check this!]

  • list: to provide us with a list of all the topics that are active on our ROS system, and
  • info: to provide us with information on a particular topic of interest.

We can use the autocomplete functionality of the Linux terminal to provide us with a list of all the available options that we can use with the ros2 topic command. To do this type ros2 topic followed by a Space and then press the Tab key twice:

rostopic[SPACE][TAB][TAB]

You should then be presented with a list of all options:

[TODO: a gif]

  • ros2 topic hz {topic name} provides information on the frequency (in Hz) at which messages are being published to a topic:

    ros2 topic hz /my_topic
    

    This should tell us that our publisher node is publishing messages to the /my_topic topic at (or close to) 1 Hz, which is exactly what we ask for in the publisher.py file (in the __init__ part of our Publisher class). Enter Ctrl+C to stop this command.

  • ros2 topic echo {topic name} shows the messages being published to a topic:

    ros2 topic echo /my_topic
    

    This will provide a live stream of the messages that our publisher.py node is publishing to the /my_topic topic. Enter Ctrl+C to stop this.

  • We can see some additional options for the echo command by viewing the help documentation for this too:

    ros2 topic echo --help
    

    From here, for instance, we can learn that if we just wanted to print the first message that was received we could use the -once option, for example:

    ros2 topic echo /my_topic --once
    

Exercise 6: Creating a subscriber node

To illustrate how information can be passed from one node to another (via topics and messages) we'll now create another node to subscribe to the topic that our publisher node is broadcasting messages to.

  1. In TERMINAL 2 use the filesystem commands that were introduced earlier (cd, ls, etc.) to navigate to the scripts folder of your part1_pubsub package.

  2. Use the same procedure as before to create a new empty Python file called subscriber.py and remember to make it executable!

  3. Then, open the newly created subscriber.py file in VS Code, paste in the code here and save it.

    Once again, it's important that you understand how this code works, so make sure you read the code annotations!

  4. Next, we need to add this as an additional executable for our package.

    Open up the CMakeLists.txt file at the root of your part1_pubsub package directory again, head back to the # Install Python executables section and add the subscriber.py file:

    # Install Python executables
    install(PROGRAMS
      scripts/publisher.py
      scripts/subscriber.py
      DESTINATION lib/${PROJECT_NAME}
    )
    
  5. Now we need to colcon build again.

    1. Make sure you're at the root of the Colcon Workspace:

      cd ~/ros2_ws/
      
    2. Run colcon build on only the part1_pubsub package:

      colcon build --packages-select part1_pubsub --symlink-install
      
    3. And then re-source the bashrc:

      source ~/.bashrc
      
  6. Use ros2 run to execute your newly created subscriber.py node (remember: ros2 run {package name} {script name}). If your publisher and subscriber nodes are working correctly you should see an output like this:

    [TODO: another gif]

  7. Interrogate your ROS network:

    1. As before, we can find out what nodes are running on our system by using the ros2 node list command. Run this in TERMINAL 3, you should see both your publisher and subscriber nodes listed there.

    2. Use the ros2 topic command to list all the topics that are available on the network. You should see /my_topic listed there.

    3. Use the ros2 topic command again to find more info on my_topic.

    4. Use the ros2 interface command to show you what type of data is being sent between the two nodes.

  8. Finally, close down your publisher and subscriber nodes by entering Ctrl+C in the terminals where they are running (should be 1 & 2).

Exercise 7: Defining our own message

We've just created a publisher and subscriber that were able to communicate with one another via a topic. The data that the publisher was sending to the topic was very simple: a example_interfaces/msg/String type message.

This message just has one field called data of the type string:

$ ros2 interface show example_interfaces/msg/String

string data

ROS messages will generally be more complex than this, typically containing several fields in a single message. We'll define our own custom message now, this time with two fields, so you can see how things work with slightly more complex data types.

  1. Message interfaces must be defined within a msg folder at the root of our package directory, so let's create this folder now in TERMINAL 1:

    1. First, navigate into your package:

      cd ~/ros2_ws/src/part1_pubsub
      
    2. Then use mkdir to make a new directory:

      mkdir msg
      
  2. We'll create a message called Example, and to do this we'll need to create a new file called Example.msg inside the msg folder:

    touch msg/Example.msg
    
  3. To define the data structure of this message, we now need to open up the file and add the following content:

    Example.msg
    string info
    int32 time
    

    The message will therefore have two fields:

    # Field Name Data Type
    1 info string
    2 time int32

    We can give our fields any name that we want, but the data types must be either built-in-types or other pre-existing ROS interfaces.

  4. We now need to declare this message in our package's CMakeLists.txt file, so that the necessary Python code can be created (by colcon build) to allow us to import this message into our own Python files.

    Add the following lines to your part1_pubsub/CMakeLists.txt file, above the ament_package() line:

    CMakeLists.txt
    find_package(rosidl_default_generators REQUIRED)
    rosidl_generate_interfaces(${PROJECT_NAME}
      "msg/Example.msg" 
    )
    
  5. We also need to modify our package.xml file. Add the following lines to this one, just above the <export> line:

    package.xml
    <buildtool_depend>rosidl_default_generators</buildtool_depend>
    <exec_depend>rosidl_default_runtime</exec_depend>
    <member_of_group>rosidl_interface_packages</member_of_group>
    
  6. We can now use Colcon to generate the necessary source code for the message:

    1. First, make sure you're in the root of the ROS2 Workspace:

      cd ~/ros2_ws/
      
    2. Then run colcon build:

      colcon build --packages-select part1_pubsub --symlink-install 
      
    3. And finally re-source the .bashrc:

      source ~/.bashrc
      
  7. We can now verify that this worked with some more ros2 command line tools:

    1. First, list all the ROS messages that are available to us on our system:

      ros2 interface list -m
      

      Scroll through this list and see if you can find our message in there (it'll be listed as part1_pubsub/msg/Example)

    2. Next, show the data structure of the interface:

      ros2 interface show part1_pubsub/msg/Example
      

      This should match with how we defined it in our part1_pubsub/msg/Example.msg file.

Exercise 8: Using a custom ROS Message

  1. Create a copy of the publisher.py file from Exercise 5. Let's do this from the command line too:

    1. Navigate into your package's scripts folder:

      cd ~/ros2_ws/src/part1_pubsub/scripts
      
    2. And use the cp command to make a copy of the publisher.py file and call this new file custom_msg_publisher.py:

      cp publisher.py custom_msg_publisher.py
      
    3. Let's create a copy of the subscriber.py file too, while we're here:

      cp subscriber.py custom_msg_subscriber.py
      
  2. Declare these two new files as additional executables in our CMakeLists.txt:

    CMakeLists.txt
    # Install Python executables
    install(PROGRAMS
      scripts/publisher.py
      scripts/subscriber.py
      scripts/custom_msg_publisher.py   # ADD THIS 
      scripts/custom_msg_subscriber.py  # AND THIS
    DESTINATION lib/${PROJECT_NAME}
    )
    
  3. Run Colcon again (last time now!):

    1. First:
      cd ~/ros2_ws
      
    2. Then:
      colcon build --packages-select part1_pubsub --symlink-install
      
    3. And finally:
      source ~/.bashrc
      
  4. Now modify your custom_msg_publisher.py file as follows:

    custom_msg_publisher.py
    #!/usr/bin/env python3
    
    import rclpy
    from rclpy.node import Node
    
    from part1_pubsub.msg import Example # (1)!
    
    class SimplePublisher(Node):
    
        def __init__(self):
            super().__init__("simple_publisher")
    
            self.my_publisher = self.create_publisher(
                msg_type=Example, # (2)!
                topic="my_topic",
                qos_profile=10,
            )
    
            publish_rate = 1 # Hz
            self.timer = self.create_timer(
                timer_period_sec=1/publish_rate,
                callback=self.timer_callback
            )
    
            self.get_logger().info(
                f"The '{self.get_name()}' node is initialised."
            )
    
        def timer_callback(self):
            ros_time = self.get_clock().now().seconds_nanoseconds()
    
            topic_msg = Example() # (3)!
            topic_msg.info = "The ROS time is..."
            topic_msg.time = ros_time[0]
            self.my_publisher.publish(topic_msg)
            self.get_logger().info(
                f"Publishing: '{topic_msg.info} {topic_msg.time:.0f}'"
            )
    
    def main(args=None):
        rclpy.init(args=args)
        my_simple_publisher = SimplePublisher()
        rclpy.spin(my_simple_publisher)
        my_simple_publisher.destroy_node()
        rclpy.shutdown()
    
    if __name__ == '__main__':
        main()
    
    1. We're now importing the Example message from our own part1_pubsub package.

    2. We're also now declaring that "my_topic" will use the Example message data structure to send messages.

    3. We need to deal with the topic messages differently now, to account for the more complex structure.

      We now populate our messages with two fields: info (a string) and time (an int). Identify what has changed here...

  5. Final Task:

    Modify the custom_msg_subscriber.py node now to accommodate the new message type that is being published to /my_topic.

Wrapping Up

In this session we've covered the basics of ROS, and learnt about some key concepts such as Packages; Nodes; and how to send data across a ROS Network using Topics, Messages, and the Publisher-Subscriber Communication Method.

We've learnt how to use some key ros2 commands:

  • launch: to launch multiple ROS Nodes via launch files.
  • run: to run executables within a ROS package.
  • node: to display information about active ROS Nodes.
  • topic: to display information about active ROS topics.
  • interface: to display information about all ROS Interfaces that are available to use in a ROS application.

We have also learnt how to work in the Linux Terminal and navigate a Linux filesystem using key commands such as:

  • ls: lists the files in the current directory.
  • cd: change directory to move around the file system.
  • mkdir: make a new directory (mkdir {new_folder}).
  • chmod: modify file permissions (i.e. to add execute permissions to a file for all users: chmod +x {file}).
  • touch: create a file without any content.

In addition to this we've also learnt how to create a ROS2 package, and how to create simple Python nodes that can publish and subscribe to topics on a ROS network.

We've worked with pre-made ROS messages to do this and also created our own custom message interface to offer more advanced functionality.


  1. What is a ROS2 Workspace? You can find out more here

  2. What is Colcon? Find out more here

  3. You can learn more about ROS2 Workspaces here

  4. What does source ~/.bashrc do? See here for an explanation