当前位置: 首页 > news >正文

【ROS 学习笔记】ROS1(Noetic) ROS2(Humble)话题创建全流程梳理

文章目录

  • 一、ROS1创建话题流程
      • **1. 准备工作**
      • 2. 创建功能包(Package)
      • 3. 编写发布者(Publisher)节点
        • 3.1 C++版本发布者
        • 3.2 Python版本发布者
      • 4. 编写订阅者(Subscriber)节点
        • 4.1 C++版本订阅者
        • 4.2 Python版本订阅者
      • 5. 配置编译规则(仅C++需要)
      • 6. 编译工作空间
      • 7. 测试话题通信
      • 8. 验证话题状态
  • 二、ROS2创建话题流程
      • 1. 准备工作
      • 2. 创建功能包(Package)
      • 3. 编写发布者(Publisher)节点
        • 3.1 C++版本发布者
        • 3.2 Python版本发布者
      • 4. 编写订阅者(Subscriber)节点
        • 4.1 C++版本订阅者
        • 4.2 Python版本订阅者
      • 5. 配置编译规则
        • 5.1 C++功能包配置(`CMakeLists.txt`)
        • 5.2 Python功能包配置(`setup.py`)
      • 6. 编译工作空间
      • 7. 测试话题通信
      • 8. 验证话题状态
  • 总结 ROS 2与ROS 1的核心差异

一、ROS1创建话题流程

在ROS(Robot Operating System)中,话题(Topic)是节点(Node)之间基于发布-订阅(Publish-Subscribe)模式进行通信的核心机制。创建一个话题的完整步骤如下:

1. 准备工作

  • 确保已安装ROS(以ROS Noetic为例,其他版本步骤类似)。
  • 创建并初始化ROS工作空间(若已存在可跳过):
    # 创建工作空间目录
    mkdir -p ~/catkin_ws/src
    cd ~/catkin_ws
    # 初始化工作空间
    catkin_make
    # 加载工作空间环境变量(每次新终端需执行,或添加到~/.bashrc)
    source devel/setup.bash
    

2. 创建功能包(Package)

ROS的代码需组织在功能包中,功能包需依赖至少一个ROS核心库(如roscpprospy)和消息类型库(如std_msgs)。

cd ~/catkin_ws/src
# 创建功能包(格式:catkin_create_pkg 包名 依赖1 依赖2 ...)
catkin_create_pkg my_topic_pkg roscpp rospy std_msgs
  • my_topic_pkg:自定义功能包名称。
  • roscpp/rospy:分别为C++/Python的ROS库依赖。
  • std_msgs:标准消息类型库(提供String、Int32等基础消息)。

3. 编写发布者(Publisher)节点

发布者节点负责向话题发送消息。以下分别提供C++Python版本示例(以发送std_msgs/String类型消息为例)。

3.1 C++版本发布者

my_topic_pkg/src/目录下创建talker.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"int main(int argc, char **argv) {// 初始化节点,节点名称为"talker_node"(需唯一)ros::init(argc, argv, "talker_node");// 创建节点句柄(管理节点资源)ros::NodeHandle n;// 创建发布者对象:发布到话题"chatter",消息类型为std_msgs::String,队列长度10ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 10);// 设置循环频率(10Hz)ros::Rate loop_rate(10);int count = 0;while (ros::ok()) {  // 当节点正常运行时循环std_msgs::String msg;std::stringstream ss;ss << "hello world " << count;msg.data = ss.str();// 打印日志(可选)ROS_INFO("发布消息: %s", msg.data.c_str());// 发布消息到话题chatter_pub.publish(msg);// 处理回调(若有)ros::spinOnce();// 按照循环频率休眠loop_rate.sleep();++count;}return 0;
}

核心代码讲解

这段代码是 ROS(机器人操作系统)中典型的消息发布逻辑,用于在节点运行时持续发送消息。以下是逐行解析:

  1. while (ros::ok()) {

    • ros::ok() 检查 ROS 节点是否正常运行(未被关闭或中断)。
    • 当节点正常时返回 true,循环持续执行;收到终止信号(如 Ctrl+C)时返回 false,循环退出。
    • 作用:确保节点在活跃状态下持续工作。
  2. std_msgs::String msg;

    • 声明一个 ROS 标准消息类型 std_msgs::String 的对象 msg
    • 该消息类型包含一个字符串字段 data,用于存储要发送的内容。
  3. std::stringstream ss;

    • 创建字符串流对象 ss(C++ 标准库组件)。
    • 用于高效拼接多个字符串或变量(避免频繁内存分配)。
  4. ss << "hello world " << count;

    • 向字符串流写入内容:
      • 固定字符串 "hello world "
      • 变量 count(通常是循环计数器,需在外部定义,如 int count = 0;
    • 示例输出:若 count=1,则流内容为 "hello world 1"
  5. msg.data = ss.str();

    • ss.str() 将字符串流内容转为 std::string
    • 赋值给消息的 data 字段,完成消息组装。
3.2 Python版本发布者

my_topic_pkg/scripts/目录下创建talker.py,并添加可执行权限:

#!/usr/bin/env python3
import rospy
from std_msgs.msg import Stringdef talker():# 初始化节点,节点名称为"talker_node"(需唯一)rospy.init_node('talker_node', anonymous=True)  # anonymous=True确保节点名唯一# 创建发布者对象:发布到话题"chatter",消息类型为String,队列长度10pub = rospy.Publisher('chatter', String, queue_size=10)# 设置循环频率(10Hz)rate = rospy.Rate(10)count = 0while not rospy.is_shutdown():  # 当节点未关闭时循环hello_str = "hello world %d" % count# 打印日志(可选)rospy.loginfo("发布消息: %s", hello_str)# 发布消息到话题pub.publish(hello_str)# 按照循环频率休眠rate.sleep()count += 1if __name__ == '__main__':try:talker()except rospy.ROSInterruptException:pass

添加可执行权限:

chmod +x ~/catkin_ws/src/my_topic_pkg/scripts/talker.py

4. 编写订阅者(Subscriber)节点

订阅者节点负责从话题接收消息,并定义消息处理逻辑(回调函数)。

4.1 C++版本订阅者

my_topic_pkg/src/目录下创建listener.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"// 回调函数:收到消息时自动调用
void chatterCallback(const std_msgs::String::ConstPtr& msg) {ROS_INFO("收到消息: %s", msg->data.c_str());
}int main(int argc, char **argv) {// 初始化节点,节点名称为"listener_node"ros::init(argc, argv, "listener_node");// 创建节点句柄ros::NodeHandle n;// 创建订阅者对象:订阅话题"chatter",队列长度10,收到消息时调用chatterCallbackros::Subscriber sub = n.subscribe("chatter", 10, chatterCallback);// 进入循环,等待回调函数触发(阻塞当前线程)ros::spin();return 0;
}
4.2 Python版本订阅者

my_topic_pkg/scripts/目录下创建listener.py,并添加可执行权限:

#!/usr/bin/env python3
import rospy
from std_msgs.msg import String# 回调函数:收到消息时自动调用
def callback(data):rospy.loginfo("收到消息: %s", data.data)def listener():# 初始化节点,节点名称为"listener_node"rospy.init_node('listener_node', anonymous=True)# 创建订阅者对象:订阅话题"chatter",队列长度10,收到消息时调用callbackrospy.Subscriber("chatter", String, callback)# 进入循环,等待回调函数触发(阻塞当前线程)rospy.spin()if __name__ == '__main__':listener()

添加可执行权限:

chmod +x ~/catkin_ws/src/my_topic_pkg/scripts/listener.py

5. 配置编译规则(仅C++需要)

Python脚本无需编译,但C++代码需通过CMakeLists.txt配置编译规则。

编辑my_topic_pkg/CMakeLists.txt,在末尾添加:

# 找到依赖包
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgs
)# 声明catkin依赖
catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs
)# 包含头文件目录
include_directories(${catkin_INCLUDE_DIRS}
)# 编译发布者节点
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
# 安装发布者节点到devel/lib/my_topic_pkg
install(TARGETS talkerRUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)# 编译订阅者节点
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
install(TARGETS listenerRUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

6. 编译工作空间

cd ~/catkin_ws
catkin_make  # 编译所有功能包
source devel/setup.bash  # 刷新环境变量

7. 测试话题通信

打开3个终端,分别执行以下命令:

  1. 启动ROS核心服务(roscore)

    roscore
    
  2. 运行发布者节点

    • C++版本:
      rosrun my_topic_pkg talker
      
    • Python版本:
      rosrun my_topic_pkg talker.py
      
  3. 运行订阅者节点

    • C++版本:
      rosrun my_topic_pkg listener
      
    • Python版本:
      rosrun my_topic_pkg listener.py
      

8. 验证话题状态

使用ROS工具查看话题信息:

rostopic list  # 列出所有活跃话题(应包含"/chatter")
rostopic echo /chatter  # 实时打印话题"/chatter"的消息
rostopic info /chatter  # 查看话题的消息类型和发布/订阅者信息

创建ROS1话题的核心流程是:创建工作空间与功能包 → 编写发布者/订阅者节点 → 配置编译(C++)→ 编译并测试。关键是确保发布者和订阅者使用相同的话题名称和消息类型,否则无法通信。

二、ROS2创建话题流程

在ROS 2(以Humble版本为例)中,话题(Topic)的创建逻辑与ROS 1类似,但工具链和API有较大变化。以下是ROS 2中创建话题通信的完整步骤:

1. 准备工作

  • 确保已安装ROS 2 Humble(参考官方安装指南)。
  • 创建并初始化ROS 2工作空间(若已存在可跳过):
    # 创建工作空间目录
    mkdir -p ~/ros2_ws/src
    cd ~/ros2_ws
    

2. 创建功能包(Package)

ROS 2功能包通过ros2 pkg create创建,需指定构建类型(C++用ament_cmake,Python用ament_python)和依赖。

cd ~/ros2_ws/src
# 创建C++功能包(支持C++节点)
ros2 pkg create my_topic_pkg --build-type ament_cmake --dependencies rclcpp std_msgs# 或创建Python功能包(支持Python节点)
ros2 pkg create my_topic_pkg --build-type ament_python --dependencies rclpy std_msgs
  • my_topic_pkg:自定义功能包名称。
  • rclcpp/rclpy:ROS 2的C++/Python客户端库。
  • std_msgs:标准消息类型库(提供String、Int32等基础消息)。

3. 编写发布者(Publisher)节点

发布者节点向话题发送消息,以下分别提供C++Python版本示例(消息类型为std_msgs/String)。

3.1 C++版本发布者

my_topic_pkg/src/目录下创建talker.cpp

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"using namespace std::chrono_literals;// 定义发布者节点类(继承自rclcpp::Node)
class TalkerNode : public rclcpp::Node {
public:TalkerNode() : Node("talker_node"), count_(0) {// 创建发布者:话题名为"chatter",消息类型String,队列长度10publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);// 创建定时器,每500ms触发一次回调(即2Hz频率发送消息)timer_ = this->create_wall_timer(500ms, std::bind(&TalkerNode::timer_callback, this));RCLCPP_INFO(this->get_logger(), "发布者节点已启动");}private:// 定时器回调函数:生成并发布消息void timer_callback() {auto msg = std_msgs::msg::String();msg.data = "hello world: " + std::to_string(count_++);RCLCPP_INFO(this->get_logger(), "发布: '%s'", msg.data.c_str());publisher_->publish(msg);}rclcpp::TimerBase::SharedPtr timer_;  // 定时器rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;  // 发布者size_t count_;  // 计数器
};int main(int argc, char * argv[]) {// 初始化ROS 2rclcpp::init(argc, argv);// 运行节点(阻塞当前线程)rclcpp::spin(std::make_shared<TalkerNode>());// 退出时清理rclcpp::shutdown();return 0;
}
3.2 Python版本发布者

my_topic_pkg/my_topic_pkg/目录下创建talker.py

import rclpy
from rclpy.node import Node
from std_msgs.msg import String# 定义发布者节点类(继承自Node)
class TalkerNode(Node):def __init__(self):super().__init__("talker_node")  # 节点名称为"talker_node"# 创建发布者:话题名为"chatter",消息类型String,队列长度10self.publisher_ = self.create_publisher(String, "chatter", 10)# 创建定时器,每0.5秒触发一次回调timer_period = 0.5  # 单位:秒self.timer = self.create_timer(timer_period, self.timer_callback)self.count = 0self.get_logger().info("发布者节点已启动")# 定时器回调函数:生成并发布消息def timer_callback(self):msg = String()msg.data = f"hello world: {self.count}"self.publisher_.publish(msg)self.get_logger().info(f"发布: '{msg.data}'")self.count += 1def main(args=None):# 初始化ROS 2rclpy.init(args=args)# 创建并运行节点talker = TalkerNode()rclpy.spin(talker)  # 阻塞当前线程,处理节点回调# 退出时清理talker.destroy_node()rclpy.shutdown()if __name__ == "__main__":main()

4. 编写订阅者(Subscriber)节点

订阅者节点从话题接收消息,并通过回调函数处理消息。

4.1 C++版本订阅者

my_topic_pkg/src/目录下创建listener.cpp

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"// 定义订阅者节点类
class ListenerNode : public rclcpp::Node {
public:ListenerNode() : Node("listener_node") {// 创建订阅者:话题名为"chatter",队列长度10,收到消息时调用回调函数subscription_ = this->create_subscription<std_msgs::msg::String>("chatter",  // 话题名称10,         // 消息队列长度std::bind(&ListenerNode::topic_callback, this, std::placeholders::_1)  // 回调函数绑定);RCLCPP_INFO(this->get_logger(), "订阅者节点已启动");  // 日志输出}private:// 话题回调函数:处理收到的消息void topic_callback(const std_msgs::msg::String & msg) const {RCLCPP_INFO(this->get_logger(), "收到: '%s'", msg.data.c_str());}rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;  // 订阅者
};int main(int argc, char * argv[]) {rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<ListenerNode>());  // 运行节点rclcpp::shutdown();return 0;
}

核心代码解释:

  • create_subscription 方法:用于创建订阅者。
    • 模板参数<std_msgs::msg::String> 指定订阅的消息类型为字符串。
    • 参数解释
      • "chatter":订阅的话题名称。节点会监听名为 chatter 的话题。
      • 10:消息队列长度。当消息到达速度超过处理速度时,最多缓存10条消息,避免丢失。
      • std::bind(...):绑定回调函数。std::bind 将成员函数 topic_callback 绑定到当前对象(this),std::placeholders::_1 表示回调函数的第一个参数(即收到的消息)。
  • 日志输出RCLCPP_INFO 是ROS 2的日志宏,用于输出信息级日志。this->get_logger() 获取节点日志器,输出 "订阅者节点已启动" 表示节点初始化成功。
void topic_callback(const std_msgs::msg::String & msg) const {RCLCPP_INFO(this->get_logger(), "收到: '%s'", msg.data.c_str());  // 处理消息
}
  • 函数作用:当话题 chatter 有新消息时,ROS 2自动调用此回调函数。
  • 参数const std_msgs::msg::String & msg 是常量引用,表示接收到的消息。使用引用避免复制开销。
  • 消息处理msg.data.c_str() 提取消息中的字符串数据(msgstd_msgs::msg::String 类型,其 data 成员为 std::string)。RCLCPP_INFO 打印日志,格式化为 "收到: '消息内容'"
4.2 Python版本订阅者

my_topic_pkg/my_topic_pkg/目录下创建listener.py

import rclpy
from rclpy.node import Node
from std_msgs.msg import String# 定义订阅者节点类
class ListenerNode(Node):def __init__(self):super().__init__("listener_node")  # 节点名称为"listener_node"# 创建订阅者:话题名为"chatter",队列长度10,收到消息时调用回调self.subscription = self.create_subscription(String, "chatter", self.listener_callback, 10)self.subscription  # 防止未使用变量警告self.get_logger().info("订阅者节点已启动")# 话题回调函数:处理收到的消息def listener_callback(self, msg):self.get_logger().info(f"收到: '{msg.data}'")def main(args=None):rclpy.init(args=args)listener = ListenerNode()rclpy.spin(listener)  # 运行节点listener.destroy_node()rclpy.shutdown()if __name__ == "__main__":main()

5. 配置编译规则

5.1 C++功能包配置(CMakeLists.txt

编辑my_topic_pkg/CMakeLists.txt,在ament_package()前添加:

# 编译发布者节点
add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETStalkerDESTINATION lib/${PROJECT_NAME})# 编译订阅者节点
add_executable(listener src/listener.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
install(TARGETSlistenerDESTINATION lib/${PROJECT_NAME})
5.2 Python功能包配置(setup.py

编辑my_topic_pkg/setup.py,在entry_points中添加节点入口:

entry_points={'console_scripts': ['talker = my_topic_pkg.talker:main',  # 发布者节点'listener = my_topic_pkg.listener:main'  # 订阅者节点],
},

6. 编译工作空间

cd ~/ros2_ws
colcon build  # 编译所有功能包(首次编译耗时较长)
source install/setup.bash  # 加载工作空间环境变量(每次新终端需执行)

7. 测试话题通信

打开3个终端,分别执行以下命令(每个终端需先source ~/ros2_ws/install/setup.bash):

  1. 运行发布者节点

    • C++版本:
      ros2 run my_topic_pkg talker
      
    • Python版本:
      ros2 run my_topic_pkg talker
      
  2. 运行订阅者节点

    • C++版本:
      ros2 run my_topic_pkg listener
      
    • Python版本:
      ros2 run my_topic_pkg listener
      

8. 验证话题状态

使用ROS 2命令行工具查看话题信息:

ros2 topic list  # 列出所有活跃话题(应包含"/chatter")
ros2 topic echo /chatter  # 实时打印话题消息
ros2 topic info /chatter  # 查看话题的消息类型和节点信息
ros2 interface show std_msgs/msg/String  # 查看消息结构

通过以上步骤,即可在ROS 2 Humble中实现基于话题的节点通信。核心是确保发布者和订阅者使用相同的话题名称和消息类型

总结 ROS 2与ROS 1的核心差异

  1. 工具链:ROS 2使用colcon编译,替代ROS 1的catkin_make
  2. 节点API:ROS 2节点通过类继承(rclcpp::Node/Node)实现,更面向对象。
  3. 启动方式:ROS 2无需单独启动roscore,节点启动时自动连接到ROS 2系统。
  4. 消息处理:ROS 2默认支持DDS中间件,通信更可靠,支持分布式系统。
http://www.dtcms.com/a/403342.html

相关文章:

  • 常州企业网站建设公司500m网站
  • 企业版的ODX为何需要制定ODX编写指南(AGL-Authoring Guidelines)
  • 27.Linux swap交换空间管理
  • 2024-WSDM-DeSCo: Towards Generalizable and Scalable Deep Subgraph Counting
  • 黄山最佳旅游攻略青岛网络优化费用
  • 人脸图像识别实战:使用 LFW 数据集对比四种机器学习模型(SVM、逻辑回归、随机森林、MLP)
  • unity+trae国际版开发环境
  • wordpress网站图片丢失网站备案找谁
  • 从编程到合规:构建安全随机数生成体系
  • 在Word中,如何引入λ γ δ α ε
  • 同步辐射X射线磁圆二色性(XMCD)测试能力和技术应用
  • 基于UML/MARTE的汽车安全关键系统设计方法
  • 新网$网站优化建筑机械人才培训网官网
  • 【Java八股文】12-分布式面试篇
  • 星环科技TDH社区版详解:从零搭建企业级大数据平台
  • 网站建设的运用场景男女做暖暖免费网站
  • APP应用接入华为推送SDK
  • 网站建设整个过程全网搜索指数
  • 安科瑞ADL200N-CT 单相户用储能表
  • 网站如何建立这么做简单的网站
  • Zabbix7.4.8(一):通过Zabbix agent 2监控postgresql相关指标
  • 实战:基于 BRPC+Etcd 打造轻量级 RPC 服务 —— 从注册到调用的完整实现
  • 企业为什么建设网站万网主机怎么上传网站吗
  • 【C++】Visual Studio+CMake 开发 C++ 入门指南:从环境搭建到项目实战
  • web3hardhat 框架实战-ERC20
  • Linux《线程同步和互斥(上)》
  • Hibernate批量操作详解及最优实践
  • 住房与建设部网站如何修改dns 快速使用境外网站
  • 【复习】计网每日一题--PPP链路
  • cpt和pretrain的差别,IFT和SFT的差别是怎么样的