Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

In the previous section, we shared how to configure the environment on the board. In this section, we will gradually discuss some key points in ROS2.

Reference Materials

ROS2 21 Lecture Video: 【GuYueJu】GuYue·Introduction to ROS2 21 Lectures | Introducing a Brand New Robot Operating System_哔哩哔哩_bilibili

(https://www.bilibili.com/video/BV16B4y1Q7jQ/?spm_id_from=333.337.search-card.all.click)

ROS2 21 Lecture Document: ROS2 Introduction Tutorial (guyuehome.com)

(https://book.guyuehome.com/)

Environment Description

RDK X3/OriginBot, Image: OriginBot v2.0.2

PC is Windows 11

Package

Regarding the creation and description of packages, it has been clearly explained in the ROS2 21 lectures, so I won’t elaborate here. Please refer to the “Core Concepts/Package” lecture https://book.guyuehome.com/ROS2/2.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/2.2_%E5%8A%9F%E8%83%BD%E5%8C%85/

Node

In the documentation of (Node – ROS2 Introduction Tutorial (guyuehome.com))(https://book.guyuehome.com/ROS2/2.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/2.3_%E8%8A%82%E7%82%B9/#hello-world), many examples have been introduced. Here, I would like to supplement the implementation of the C++ code and include the code from the document for comparison and explanation of usage differences.

Example 1: Hello World Node (Procedural)

This example demonstrates how to implement printing “Hello World” every 0.5 seconds in a procedural manner using Python.

Python Version

The code is as follows:

#!/usr/bin/env python3 # -*- coding: utf-8 -*-
"""@Author: GuYueJu(www.guyuehome.com)@Description: ROS2 Node Example - Publish "Hello World" log information, using procedural implementation"""
import rclpy                                     # ROS2 Python interface library
from rclpy.node import Node                      # ROS2 Node class
import time

def main(args=None):                             # ROS2 node main entry function    rclpy.init(args=args)                        # ROS2 Python interface initialization    node = Node("node_helloworld")               # Create ROS2 node object and initialize
    while rclpy.ok():                            # Check if ROS2 system is running        node.get_logger().info("Hello World")    # ROS2 log output        time.sleep(0.5)                          # Sleep to control loop timing
    node.destroy_node()                          # Destroy node object        rclpy.shutdown()                             # Shutdown ROS2 Python interface

You can run it on RDK X3, the effect is as shown in the figure below.

Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Here you might encounter a problem when pressing ctrl+C to exit, resulting in a KeyboardInterrupt error. This is a minor issue, but is there a way to solve it? Certainly, you just need to add a try/except mechanism.

#!/usr/bin/env python3 # -*- coding: utf-8 -*-
"""@Author: GuYueJu(www.guyuehome.com)@Description: ROS2 Node Example - Publish "Hello World" log information, using procedural implementation"""
import rclpy                                     # ROS2 Python interface library
from rclpy.node import Node                      # ROS2 Node class
import time

def main(args=None):                             # ROS2 node main entry function    rclpy.init(args=args)                        # ROS2 Python interface initialization    node = Node("node_helloworld")               # Create ROS2 node object and initialize    try:        while rclpy.ok():                            # Check if ROS2 system is running            node.get_logger().info("Hello World")    # ROS2 log output            time.sleep(0.5)                          # Sleep to control loop timing    except:        pass
    node.destroy_node()                          # Destroy node object        rclpy.shutdown()                             # Shutdown ROS2 Python interface

Add three lines of code, then compile and execute in the terminal.

Compile using the separate compilation method we used in the last section.

colcon build --packages-select learning_node

So how does ROS2 determine how to use this node name? You can see in the setup.py file.

 entry_points={        'console_scripts': [         'node_helloworld       = learning_node.node_helloworld:main',         'node_helloworld_class = learning_node.node_helloworld_class:main',         'node_object            = learning_node.node_object:main',         'node_object_webcam     = learning_node.node_object_webcam:main',        ],    },

In which ‘node_helloworld = learning_node.node_helloworld:main‘ indicates the node name, and learning_node.node_helloworld:main indicates the execution path.

Just declare the node name and path in this file.

C++ Version

First, you need to create a new package named learning_node_c or learning_node_cpp; we will be compatible with the package name and name it learning_node_c.

# Do not add rclcpp dependency
ros2 pkg create --build-type ament_cmake learning_node_c
# Add rclcpp dependency
ros2 pkg create --build-type ament_cmake learning_node_c --dependencies rclcpp
Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Create a node_helloworld.cpp file in learningd_node_c/src and fill in the following content.

#include "rclcpp/rclcpp.hpp"

int main(int argc, char **argv){    rclcpp::init(argc, argv);    auto helloWorldNode = std::make_shared<rclcpp::Node>("node_helloworld");    while (rclcpp::ok()) {        RCLCPP_INFO(rclcpp::get_logger("node_helloworld"), "Hello World");        std::this_thread::sleep_for(std::chrono::milliseconds(500));    }    // spin represents the loop execution of the helloWorldNode    // rclcpp::spin(helloWorldNode);    rclcpp::shutdown();    return 0;}

How to name and run the node?

1. Configure rclcpp dependency, which needs to be filled in the CMakelist.txt and package.xml files.

2. Specify the node name in the CMakelist.txt file.

Details are as follows, refer to the Chinese description.

cmake_minimum_required(VERSION 3.5)
project(learning_node_c)
# Default to C99
if(NOT CMAKE_C_STANDARD)  set(CMAKE_C_STANDARD 99)endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)  set(CMAKE_CXX_STANDARD 14)endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")  add_compile_options(-Wall -Wextra -Wpedantic)endif()
# find dependencies
find_package(ament_cmake REQUIRED)
# rclcpp dependency description
find_package(rclcpp REQUIRED)
# Add the executable
# Add node description, node_helloworld_c is an alias
add_executable(node_helloworld_c src/node_helloworld.cpp)
# Give dependency to the node
ament_target_dependencies(node_helloworld_c rclcpp)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
if(BUILD_TESTING)  find_package(ament_lint_auto REQUIRED)  # the following line skips the linter which checks for copyrights  # uncomment the line when a copyright and license is not present in all source files  #set(ament_cmake_copyright_FOUND TRUE)  # the following line skips cpplint (only works in a git repo)  # uncomment the line when this package is not in a git repo  #set(ament_cmake_cpplint_FOUND TRUE)  ament_lint_auto_find_test_dependencies()endif()
# Install the executable
# Actually compile to install, becoming an executable
install(TARGETS  node_helloworld_c  DESTINATION lib/${PROJECT_NAME})
ament_package()</dependency>

The running result is as follows:

Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Example 2: Hello World Node (Object-Oriented)

Object-oriented programming is indeed a more common coding style and more aligned with the design structure of C++/Python.

Python Version

The implementation code is as follows:

#!/usr/bin/env python3# -*- coding: utf-8 -*-
"""@Author: GuYueJu(www.guyuehome.com)@Description: ROS2 Node Example - Publish "Hello World" log information, using object-oriented implementation"""
import rclpy                                     # ROS2 Python interface library
from rclpy.node import Node                      # ROS2 Node class
import time

"""Create a HelloWorld node, output "hello world" log during initialization"""
class HelloWorldNode(Node):    def __init__(self, name):        super().__init__(name)                       # Initialize parent class of ROS2 node        try:            while rclpy.ok():                            # Check if ROS2 system is running                self.get_logger().info("Hello World")    # ROS2 log output                time.sleep(0.5)                          # Sleep to control loop timing        except:            pass

def main(args=None):                                 # ROS2 node main entry function    rclpy.init(args=args)                            # ROS2 Python interface initialization    node = HelloWorldNode("node_helloworld_class")   # Create ROS2 node object and initialize    node.destroy_node()                              # Destroy node object    rclpy.shutdown()                                 # Shutdown ROS2 Python interface

The setup.py also sets ‘node_helloworld_class = learning_node.node_helloworld_class:main’, I won’t elaborate on this.

C++ Version

The implementation code is as follows:

#include "rclcpp/rclcpp.hpp"
class HelloWorldClassNode : public rclcpp::Node{public:    HelloWorldClassNode() : Node("node_helloworld_class") {        timer_ = create_wall_timer(            std::chrono::milliseconds(500),            [this]() {                RCLCPP_INFO(get_logger(), "Hello World");            }        );    }
private:    rclcpp::TimerBase::SharedPtr timer_;};
int main(int argc, char **argv){    rclcpp::init(argc, argv);
    auto helloWorldClassNode = std::make_shared<HelloWorldClassNode>();
    rclcpp::spin(helloWorldClassNode);
    rclcpp::shutdown();    return 0;}

Add the following content in the CMakelist.txt:

add_executable(node_helloworld_class_c src/node_helloworld_class.cpp)
ament_target_dependencies(node_helloworld_class_c rclcpp)
install(TARGETS  node_helloworld_class_c  DESTINATION lib/${PROJECT_NAME})
Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)
Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Learning ROS2 Through Development Boards: 21 Lectures (C++ Node Example)

Leave a Comment