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.

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

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:

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})

