3.ROS2-Nodes

文章目录

概念

tolerance的意思是节点之间互不影响,即使一个坏掉了,另一个还能用

Nodes

注意区分:

  • 节点的文件名称
  • 节点的名称
  • 节点的可执行文件名称

python

最小单元

bash 复制代码
# ~/ros2_ws/src/my_py_pkg/my_py_pkg
touch my_first_node.py

Node节点最小的单元示例代码

my_first_node.py

python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

def main(args=None):
	rclpy.init(args=args)
	node = Node('py_test')
	rclpy.shutdown()
完整代码

my_first_node.py

python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

def main(args=None):
	rclpy.init(args=args)
	node = Node('py_test') # 节点名称
	node.get_logger().info('Hello, ROS2!')
	rclpy.shutdown()
	
if __name__ == '__main__':
	main()

让节点一直挂着,试试就知道了

python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

def main(args=None):
	rclpy.init(args=args)
	node = Node('py_test') # 节点名称
	node.get_logger().info('Hello, ROS2!')
	rclpy.spin(node) # Keeps the node alive until it is shut down
	rclpy.shutdown()
	
if __name__ == '__main__':
	main()
install node
bash 复制代码
# ~/ros2_ws/src/my_py_pkg/setup.py
py_node = my_py_pkg.my_first_node:main
重新构建package
bash 复制代码
# 在ros2工作目录下
colcon build --packages-select my_py_pkg
重新加载bash,并运行节点
bash 复制代码
cd ~
source .bashrc
ros2 run my_py_pkg py_node

py_node 是在setup.bash 里创建的可执行文件名称,

⚠️注意区分 文件名称,节点名称,可执行文件名称;有时候会取相同的名称

面向对象的Node节点

示例代码
python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

class MyNode(Node):# Inherit from the 【Node】 class
    def __init__(self):
        super().__init__('py_test') # Initialize the node with a name
        self.get_logger().info('Hello, ROS2!')

def main(args=None):
	rclpy.init(args=args)
	node = MyNode()
	rclpy.spin(node) # Keeps the node alive until it is shut down
	rclpy.shutdown()
	
if __name__ == '__main__':
	main()

重新构建package并运行

bash 复制代码
# 在ros2工作目录下
colcon build --packages-select my_py_pkg
source install/setup.bash
ros2 run my_py_pkg py_node
工具:timer

代码示例

python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

class MyNode(Node):# Inherit from the 【Node】 class
    def __init__(self):
        super().__init__('py_test') # Initialize the node with a name
        self.get_logger().info('Hello, ROS2!')
        self.create_timer(1.0, self.timer_callback) # Create a timer that calls the callback every 1 second;
        # self.timercallback() # Call the callback function immediately
        # self.timercallback #  will not execute the function but will pass the function object itself to the timer, allowing it to be called at the specified intervals.

    def timer_callback(self):
        self.get_logger().info('Timer callback called!')

def main(args=None):
	rclpy.init(args=args) # Initialize the ROS2 Python client library
	node = MyNode()
	rclpy.spin(node) # Keeps the node alive until it is shut down
	rclpy.shutdown()
	
if __name__ == '__main__':
	main()

重新构建package并运行

bash 复制代码
cd ~/ros2_wk
colcon build --packages-select my_py_pkg
source install/setup.bash
ros2 run my_py_pkg py_node
完整代码
python 复制代码
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node

class MyNode(Node):# Inherit from the 【Node】 class
    def __init__(self):
        super().__init__('py_test') # Initialize the node with a name
        self.get_logger().info('Hello, ROS2!')
        self.counter_ = 0
        self.create_timer(1.0, self.timer_callback) # Create a timer that calls the callback every 1 second;
        # self.timercallback() # Call the callback function immediately
        # self.timercallback #  will not execute the function but will pass the function object itself to the timer, allowing it to be called at the specified intervals.

    def timer_callback(self):
        self.get_logger().info('Timer callback called!' + str(self.counter_)) # Log the current time when the timer callback is called
        self.counter_ += 1

def main(args=None):
	rclpy.init(args=args) # Initialize the ROS2 Python client library
	node = MyNode()
	rclpy.spin(node) # Keeps the node alive until it is shut down
	rclpy.shutdown()
	
if __name__ == '__main__':
	main()

重新构建package并运行

bash 复制代码
cd ~/ros2_ws
colcon build --packages-select my_py_pkg
source install/setup.bash
ros2 run my_py_pkg py_node

CPP node

最小单元

bash 复制代码
cd ~/ros2_ws/
touch my_first_node.cpp
c 复制代码
#include "rclcpp/rclcpp.hpp"

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv); # Initialize the ROS2 C++ client library/comunication framework
    //
    rclcpp::shutdown();
    return 0;
}
完整代码
c 复制代码
#include "rclcpp/rclcpp.hpp"

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv); //Initialize the ROS2 C++ client library/comunication framework
    auto node = std::make_shared<rclcpp::Node>("cpp_test"); // Create a node with a name
    RCLCPP_INFO(node->get_logger(), "Hello, ROS2!"); // Log a message to indicate that the node has started

		rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
  • std::make_shared:C++11 引入的模板函数,用于在动态内存中构造一个对象,并返回一个 std::shared_ptr 智能指针。相比直接使用 new,它更安全、高效(一次内存分配)。
  • rclcpp::Node:ROS2 的 C++ 客户端库(rclcpp)中代表 节点 的类。节点是 ROS2 图中负责通信的基本单元,可以发布/订阅话题、提供服务/客户端、获取参数等。
  • "cpp_test":传递给 rclcpp::Node 构造函数的参数,即 节点名称。在 ROS2 网络中,节点名必须唯一(同一进程内不能重名,不同进程可以同名但通常建议全局唯一)。
    因此,std::make_shared<rclcpp::Node>("cpp_test") 创建了一个名为 "cpp_test" 的 ROS2 节点对象,并返回指向它的 std::shared_ptr
  • RCLCPP_INFO
    来源: ROS2rclcpp 库中定义的一个日志宏(#include <rclcpp/rclcpp.hpp>)。
    功能: 输出 INFO 级别 的日志。ROS2 日志级别依次为:DEBUG、INFO、WARN、ERROR、FATAL。INFO 表示一般性提示信息。
    参数: 通常需要两个参数------logger 和格式化字符串(类似 printf),后面可以跟可变参数。
    RCLCPP_INFO(logger, "format string", args...);
    特点:宏会根据当前节点的日志配置(如日志级别、输出目标)决定是否实际输出。默认情况下,INFO 及以上级别会输出到控制台和 ROS2 日志系统。
  • node->get_logger()
    node 是之前创建的 std::shared_ptr<rclcpp::Node> 智能指针。
    get_logger() rclcpp::Node 类的成员函数,返回该节点专属的 日志记录器对象(类型为 rclcpp::Logger)。
    每个节点有自己的日志器,日志会带上节点的名称(如 cpp_test)以便区分不同节点的输出。
C++需要构建代码
bash 复制代码
vim CMakeLists.txt

打开来这样的

  • if(BUILD_TESTING) 部分不需要可以直接删除
    在ament_package()前加入,后面有代码解释
c 复制代码
// <cpp_node> 是可执行文件名称,<src/my_first_node.cpp>是源文件路径
add_executable(cpp_node src/my_first_node.cpp) 
ament_target_dependencies(cpp_node rclcpp)

install(TARGETS
	cpp_node
	DESTINATION lib/${PROJECT_NAME}
)

最终效果

代码解释

  • add_executable(cpp_node src/my_first_node.cpp)
    CMake 内置命令:用于从源代码生成一个可执行文件。
    参数:
    cpp_node:指定生成的可执行文件的目标名称,自己指定
    src/my_first_node.cpp:源文件的路径(相对于 CMakeLists.txt 所在目录)。
    作用: 告诉 CMake 需要编译 my_first_node.cpp,并将编译后的目标文件链接成名为 cpp_node 的可执行程序。
  • ament_target_dependencies(cpp_node rclcpp)
    ament 宏命令:由 ROS2 的构建系统 ament_cmake 提供,用于为指定的目标 自动处理依赖项的包含目录、链接库和编译定义。
    参数:
    cpp_node:前面 add_executable 定义的目标名称,指明要为哪个目标添加依赖。
    rclcpp:依赖的 ROS2 包名称(即 rclcpp 库及其相关的依赖,如 rcl, rmw, rcutils 等)。
    作用: 自动查找 rclcpp 包及其所有传递依赖(如 ROS2 中间件、日志系统等)
  • install(TARGETS ...) 命令
    CMake 内置命令:定义构建完成后,如何将生成的目标(可执行文件、库等)安装到指定位置。
    TARGETS:关键字,后面列出需要安装的目标名称,这里cpp_node(由前面的 add_executable 定义)。可以继续换行往后加
    • DESTINATION:指定安装的目标路径(相对于 - CMAKE_INSTALL_PREFIX)。
    • DESTINATION lib/${PROJECT_NAME}
      lib:安装到 lib 目录下。在 ROS2 工作空间中,可执行文件通常安装在 install/<package_name>/lib/<package_name>/ 路径。
      ${PROJECT_NAME}:CMake 变量,值为当前项目的名称(由 project() 命令定义)。例如如果 CMakeLists.txt 中有 project(my_cpp_pkg)
      组合后:目标被安装到 lib/<项目名>/ 目录下。

代码整体含义

  1. 编译 src/my_first_node.cpp 生成可执行文件 cpp_node。
  2. 确保该可执行文件能够正确使用 rclcpp 库,从而可以创建 ROS2 节点、发布/订阅话题、记录日志等。
  3. 最终在 install 目录下的 lib/<package_name>/ 中生成可执行文件,可通过 ros2 run <package_name> cpp_node 运行。
构建package并运行
bash 复制代码
cd ~/ros2_ws
colcon build --packages-select my_cpp_pkg
source install/setup.bash
ros2 run my_cpp_pkg cpp_node

面向对象的Node

c++ 创建类
c 复制代码
class MyNode : public rclcpp::Node //继承创建类
{
public:
    MyNode() : Node("cpp_test") //调用父类函数Initialize the node with a name
    {           

    }   

private:
}
示例代码
c 复制代码
#include "rclcpp/rclcpp.hpp"

class MyNode : public rclcpp::Node
{
public:
    MyNode() : Node("cpp_test") //Initialize the node with a name
    {           
        RCLCPP_INFO(this->get_logger(), "Hello, ROS2!"); //Log a message to indicate that the node has started
    }   

private:
}

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv); //Initialize the ROS2 C++ client library/comunication framework
    auto node = std::make_shared<MyNode>(); //Create an instance of the MyNode class

    rclcpp::spin(node); //Keeps the node alive until it is shut down
    rclcpp::shutdown();
    return 0;
}
加定时器
c 复制代码
#include "rclcpp/rclcpp.hpp"

class MyNode : public rclcpp::Node
{
public:
    MyNode() : Node("cpp_test"), counter_(0) //构造函数,初始化节点名称为"cpp_test"并将计数器初始化为0
    {           
        RCLCPP_INFO(this->get_logger(), "Hello, ROS2!"); //Log a message to indicate that the node has started
        timer_ = this->create_wall_timer( //Create a timer that calls the callback every 1 second
            std::chrono::seconds(1),
            std::bind(&MyNode::timer_callback, this) //Bind the timer callback function to the timer 注册回调函数
        );
    }   

private:
    void timer_callback() // Define a callback function for the timer
    {
        RCLCPP_INFO(this->get_logger(), "Timer callback called! %d", counter_); //Log a message when the timer callback is called
        counter_++;
    }
    rclcpp::TimerBase::SharedPtr timer_; // Declare a timer object
    int counter_ = 0; 
}

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv); //Initialize the ROS2 C++ client library/comunication framework
    auto node = std::make_shared<MyNode>(); //创建MyNode类的实例,并将其作为共享指针返回

    rclcpp::spin(node); //Keeps the node alive until it is shut down
    rclcpp::shutdown();
    return 0;
}
  • create_wall_timerrclcpp::Node 提供的成员函数,用于创建一个基于系统时间(壁钟,wall clock)的定时器。

    • 第一个参数:时间间隔 std::chrono::seconds(1),表示每 1 秒 触发一次。
    • 第二个参数:回调函数,类型为 std::function<void()>。这里使用 std::bind 将成员函数 timer_callback 绑定到当前对象 this 上。
    • &MyNode::timer_callback:成员函数指针。
    • this:指定调用该成员函数时使用的对象实例。
    • 返回值:rclcpp::TimerBase::SharedPtr 类型的智能指针,被赋值给成员变量 timer_,用于管理定时器的生命周期(只要 timer_ 存在,定时器就会运行)。
  • 声明一个 rclcpp::TimerBase::SharedPtr 类型的成员变量,用于存储 create_wall_timer 返回的智能指针。

    保持对定时器的引用,防止定时器被提前销毁(智能指针的特性:只要 timer_ 在作用域内,定时器就保持有效)。

相关推荐
dragen_light3 小时前
4.ROS2-Language Libraries
ros
kobesdu3 天前
开源3D激光SLAM算法的异同点、优劣势与适配场景总结
算法·3d·机器人·ros
佳木逢钺3 天前
Kalibr 完全指南:从原理推导到ROS实战,掌握相机-IMU高精度标定
人工智能·计算机视觉·ros·无人机
kobesdu6 天前
【ROS2实战笔记-6】RobotPerf:机器人计算系统的基准测试方法论
笔记·机器人·ros
MIXLLRED7 天前
随笔——ROS Ubuntu版本变化详解
linux·ubuntu·机器人·ros
sanzk7 天前
astra pro稠密建图
ubuntu·ros·3d相机
kobesdu8 天前
【ROS2实战笔记-4】Gazebo:从通信桥接到性能瓶颈相关技术梳理
笔记·机器人·ros·gazebo
LN花开富贵9 天前
【ROS】鱼香ROS2学习笔记一
linux·笔记·python·学习·嵌入式·ros·agv
kobesdu9 天前
【ROS2实战笔记-3】RViz2图形底层与调试暗坑
笔记·机器人·ros·rviz