Robotics - ROS2 Basic Pub Sub Node

ROS2 Installation, Packaging & Build System

Posted by Rico's Nerd Cluster on November 5, 2024

Install ROS2

This is only to install ROS2-iron-desktop for quick desktop experiments. In general I’d like to use docker containers though.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Remove any corrupted ROS2 keyrings.
sudo rm /etc/apt/keyrings/ros-archive-keyring.gpg
sudo mkdir -p /etc/apt/keyrings

# Download ROS2 GPG key
curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo gpg --dearmor -o /etc/apt/keyrings/ros-archive-keyring.gpg

# Verify the GPG keys are added correctly
# Should see "ros-archive-keyring.gpg"
ls /etc/apt/keyrings/

sudo rm /etc/apt/sources.list.d/ros2.list
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt update

# install comprehensive ROS2, like rviz, rqt, and demos
sudo apt install -y ros-iron-desktop
sudo apt install -y python3-colcon-common-extensions
sudo apt install ros-iron-joint-state-publisher
sudo apt install ros-iron-xacro

echo "source /opt/ros/iron/setup.bash" >> ~/.bashrc
source ~/.bashrc

# Run a talker and listener.
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp listener

Create a Python Package For Publisher & Subscriber

  • mkdir -p ~/ros2_ws/src && cd ~/ros2_ws/src
  • Create a package ros2 pkg create --build-type ament_python --dependencies rclpy std_msgs
    • --build-type specifies that this is a python pkg.
    • ros2 pkg create my_mixed_pkg --build-type ament_cmake --dependencies rclcpp rclpy std_msgs is the command for creating cmake and python package.
    • Package naming: in ros2, please use _ instead of -, like ` py_package_1 instead of py-package-1`
  • File Directory
1
2
3
4
5
6
7
├── dummy_test
│   ├── demo.py
│   ├── __init__.py
├── package.xml
├── resource
│   └── dummy_test
├── setup.py
1
2
- `resource`: holds files that do not need executable permissions like config, launch files, data files etc.
- python executables **NEED executable permissions**, so they need to placed in `dummy_test`
  • setup.py: so we can create exportable modules (along with ament_python_install_package(${PROJECT_NAME})). It’s NOT used for creating python nodes
  • See this page for creating python nodes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from setuptools import setup, find_packages

package_name = 'dummy_test'

setup(
    name=package_name,
    version='0.0.1',
    packages=find_packages(),
    install_requires=['setuptools', 'rclpy'],
    zip_safe=True,
    maintainer='Your Name',
    maintainer_email='you@example.com',
    description='A minimal ROS 2 publisher node',
    license='Apache License 2.0',
    entry_points={
        'console_scripts': [
            'demo = dummy_test.demo:main',
        ],
    },
)
  • dummy_test/demo.py: this is the console script that could be found by the Python environment’s bin
    • Note: main() function must be defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import rclpy
from rclpy.node import Node

from std_msgs.msg import String


class MinimalPublisher(Node):

    def __init__(self):
        super().__init__('minimal_publisher')
        self.publisher_ = self.create_publisher(String, 'topic', 10)
        timer_period = 0.5  # seconds
        self.timer = self.create_timer(timer_period, self.timer_callback)
        self.i = 0

    def timer_callback(self):
        msg = String()
        msg.data = 'Hello World: %d' % self.i
        self.publisher_.publish(msg)
        self.get_logger().info('Publishing: "%s"' % msg.data)
        self.i += 1


def main(args=None):
    rclpy.init(args=args)

    minimal_publisher = MinimalPublisher()

    rclpy.spin(minimal_publisher)

    # Destroy the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    minimal_publisher.destroy_node()
    rclpy.shutdown()
  • Build and run
1
2
3
4
chmod +x dummy_test/resource/dummy_test
colcon build --packages-select dummy_test --symlink-install
source install/setup.bash
ros2 run dummy_test demo
1
- `--symlink-install` allows in-place modifications in `colcon build --packages-select dummy_test --symlink-install`

Packaging & Build System

  • In ROS2, cpp files require CMakeLists.txt, python files require setup.cfg and setup.py:
1
2
3
4
5
6
7
8
9
10
11
12
cpp_package_1/
    CMakeLists.txt
    include/cpp_package_1/
    package.xml
    src/

py_package_1/
    package.xml
    resource/py_package_1
    setup.cfg
    setup.py
    py_package_1/
  • With Colcon, I like:
1
colcon build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=1 --packages-select dummy_test --cmake-force-configure
  • -DCMAKE_BUILD_TYPE=RelWithDebInfo: This sets the CMake variable CMAKE_BUILD_TYPE to RelWithDebInfo, meaning “Release with Debug Info.”. Despite the existence of the debugging symbols, below can still happen with optimization:
    • Lines can be merged or removed
    • Variables can vanish
    • Stepping can feel “jumpy”
    • use a pure Debug build (no optimization) or something like -Og (for GCC) or -O1 -g (for Clang) for real debug build
  • -DCMAKE_EXPORT_COMPILE_COMMANDS=1: Tells CMake to generate a compile_commands.json file in your build directory. This JSON file lists all compiler invocations for your project, which is extremely useful for tools like clangd, code analyzers, and IDEs that need to know your include paths and compiler flags.
  • --cmake-force-configure This is not a standard CMake flag; it’s a colcon (ROS 2 build tool) argument. It forces CMake to re-run its configuration step for all packages, even if CMake thinks nothing has changed.