【ROS2快速学习】
ROS2
去中心化master
不重复造轮子,更换为DDS
工具:
1. 常用指令
功能 | ROS1 | ROS2 |
---|---|---|
录制话题 | rosbag record xxx | ros2 bag record xxx |
播放话题 | rosbag play xxx.bag | ros2 bag play xxx |
查看数据包内容 | Rosbag info xxx.bag | Ros2 bag info xxx.db3 |
查看话题列表 | rostopic list | Ros2 topic list |
查看话题列表, 增加消息类型 | ros2 topic list -t | |
输出话题内容 | Rostopic echo xxx | Ros2 topic echo xxx |
查看某话题 | rostopic info xxx | ros2 topic info xxx |
查看msg 内容 | rosmsg show xxx | ros2 interface show xxx |
话题发布 | rostopic pub xxx | ros2 topic pub xxx |
2. 数据包转换
# 安装 工具
pip3 install rosbags
# 更新
pip3 install --upgrade rosbags
# 下载慢,指定下载源
pip3 install rosbags -i https://pypi.tuna.tsinghua.edu.cn/simple
# 使用方法 目标文件夹路径: 转换后的数据包存放的文件夹和转换后数据包的名称
rosbags-convert <源文件路径> --dst <目标文件夹路径>
# ros2 -> ros1
rosbags-convert <your-ros2-bag>
# ros1 -> ros2
rosbags-convert <your-ros1-bag>
3. 具体区别
消息订阅
ROS1
void chatterCallback(const std_msgs::String::ConstPtr& msg) {ROS_INFO("ROS 1 Heard: [%s]", msg->data.c_str());
}
int main(int argc, char** argv) {ros::init(argc, argv, "ros1_subscriber");ros::NodeHandle nh;// 创建订阅者ros::Subscriber sub = nh.subscribe("chatter", 10, chatterCallback);ROS_INFO("ROS 1 Subscriber ready...");// 进入循环处理回调ros::spin();return 0;
}
ROS2
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"using std::placeholders::_1;class ROS2Subscriber : public rclcpp::Node {
public:ROS2Subscriber() : Node("ros2_subscriber") {// 创建订阅者 (使用lambda表达式或bind)subscription_ = this->create_subscription<std_msgs::msg::String>("chatter", 10, std::bind(&ROS2Subscriber::topic_callback, this, _1));RCLCPP_INFO(this->get_logger(), "ROS 2 Subscriber ready...");}private:void topic_callback(const std_msgs::msg::String::SharedPtr msg) const {RCLCPP_INFO(this->get_logger(), "ROS 2 Heard: '%s'", msg->data.c_str());}rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};int main(int argc, char* argv[]) {rclcpp::init(argc, argv);auto node = std::make_shared<ROS2Subscriber>();rclcpp::spin(node);rclcpp::shutdown();return 0;
}
消息发布
ROS1
// 初始化ROS节点ros::init(argc, argv, "ros1_publisher");ros::NodeHandle nh;// 创建发布者ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);ros::Rate rate(1); // 1Hzint count = 0;while (ros::ok()) {std_msgs::String msg;msg.data = "Hello ROS 1: " + std::to_string(count++);// 发布消息pub.publish(msg);ROS_INFO("Published: %s", msg.data.c_str());rate.sleep();}
ROS2
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"using namespace std::chrono_literals;class ROS2Publisher : public rclcpp::Node {
public:ROS2Publisher() : Node("ros2_publisher"), count_(0) {// 创建发布者 (使用泛型模板)publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);// 创建定时器timer_ = this->create_wall_timer(1000ms, std::bind(&ROS2Publisher::timer_callback, this));}private:void timer_callback() {auto message = std_msgs::msg::String();message.data = "Hello ROS 2: " + std::to_string(count_++);// 发布消息publisher_->publish(message);RCLCPP_INFO(this->get_logger(), "Published: '%s'", message.data.c_str());}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);// 创建并运行节点auto node = std::make_shared<ROS2Publisher>();rclcpp::spin(node);// 关闭ROS 2rclcpp::shutdown();return 0;
}
消息区别
ROS1
# std_msgs/String.msg
# #include "std_msgs/String.h"
string data
ROS2
# std_msgs/msg/String.msg
#include "std_msgs/msg/string.hpp"
# ROS 2 消息在 msg 子命名空间中
string data
自定义消息
ROS1
# my_package/msg/MyCustom.msgstring first_name
string last_name
uint32 age
float32 score
package.xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt
find_package(catkin REQUIRED COMPONENTSmessage_generationstd_msgs
)add_message_files(FILESMyCustom.msg
)generate_messages(DEPENDENCIESstd_msgs
)catkin_package(CATKIN_DEPENDS message_runtime std_msgs
)
ROS2
string first_name
string last_name
uint32 age
float32 score
package.xml
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rosidl_default_generators</depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
CMakeLists.txt
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"msg/MyCustom.msg"DEPENDENCIES std_msgs
)ament_export_dependencies(rosidl_default_runtime)
主要架构差别
特性 | ROS 1 | ROS 2 |
---|---|---|
节点初始化 | ros::init() | rclcpp::init() |
节点创建 | ros::NodeHandle | 继承 rclcpp::Node |
发布者 | nh.advertise<>() | create_publisher<>() |
订阅者 | nh.subscribe() | create_subscription<>() |
消息类型 | std_msgs::String | std_msgs::msg::String |
回调处理 | ros::spin() | rclcpp::spin() |
消息指针 | ConstPtr | SharedPtr |
命名空间 | 扁平结构 | msg 子命名空间 |
构建系统 | catkin | ament_cmake |
消息生成 | message_generation | rosidl_default_generators |
4. ROS2 调试
# 错误
error while loading shared libraries: libOgreMain.so.1.12.1: cannot open shared object file: No such file or directory# 解决
source /opt/ros/humble/setup.bash
# 错误
root@nuc:/workspace# ros2 run rviz2 rviz2
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, xcb.
[ros2run]: Aborted
# 解决
ROS2 Docker 可视化
https://blog.csdn.net/qq_39779233/article/details/142900876
# 这个写到brshrc 中
export DISPLAY=:0
# 在docker 环境外执行
xhost +local:
ROS2多机
-
物理连接性测试 组播测试
# 发送端 ros2 multicast send# 接收端 ros2 multicast receive
-
通讯测试
sudo apt install ros-humble-examples-rclpy-minimal-publisher # 发送端 ros2 run examples_rclcpp_minimal_publisher publisher_member_function # 接收端 ros2 topic list
-
设置ROS_DOMAIN_ID
# 设置 domain_id export ROS_DOMAIN_ID=1 # 打印 domain_id echo $ROS_DOMAIN_ID
- ROS_DOMAIN_ID 相同,即可相互通信
- ROS_DOMAIN_ID 不同,不可相互通信,但是组播测试成功