文章目录
1. 环境搭建
环境搭建参考以下两篇文档
2. ros2基本框架
3. ros2通信的四种方式
3.1 话题topics
3.1.1 概念
话题采用的是发布者-订阅者模型。
话题是ROS中节点交换消息的总线,节点可以订阅/发布任意数量的话题。数据通过publish节点发送到所有订阅了这个topic的subcriber节点
3.1.2 测试
- 查看topic列表
ros2 topic list -t
- 查看topic内容回显
ros2 topic echo /chatter
- 查看topic信息
ros2 topic info /chatter
- 发布topic信息
ros2 topic pub --once /chatter std_msgs/msg/String "{123456test}"
ros2 topic pub --rate 1 /chatter std_msgs/msg/String "{data: '123456test'}"
3.2 服务services
3.2.1 概念
服务基于**请求和响应模型**,对具体调用的客户端提供数据,由Service发起请求,Client接收后应答Response,适用于短时间的同步任务,发起任务后,Client会阻塞等待响应。
3.2.2 测试
- 查看service列表
ros2 service list
ros2 service list -t
- 查找指定type对应的service
ros2 service find rcl_interfaces/srv/GetParameterTypes
- 通过service发送数据
ros2 service call /listener/set_parameters_atomically rcl_interfaces/srv/SetParametersAtomically "{parameters: [{name: 'use_sim_time', value: {type: 1, bool_value: true}}]}"
3.3 动作action
3.3.1 概念
动作Action建立在话题和服务之上,并且Action是可抢占的,可以在执行时取消,主要用于长期、异步运行的任务。
3.3.2 测试
3.4 参数parameters
3.4.1 概念
参数是节点的配置,每个节点维护自己的参数,参数可以是整数、浮点数、布尔、字符串和列表
3.4.2 测试
- 查看param列表
ros2 param list
- 设置指定param值
ros2 param set /listener use_sim_time False
- 获取指定param值
ros2 param get /listener use_sim_time
- param列表转存
ros2 param dump /listener
- param文件加载
ros2 param load /listener listener.yaml
4. 基本概念
4.1 节点
4.1.1 概念
ros2中每个节点代表一个单独的模块,例如一个用于控制电机,一个用于精光测距。每个节点可以通过话题、服务、动作或参数向其他节点发送和接收数据
4.1.2 测试
- 在两个终端中分别运行ros2节点
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp listener
可以看到两个节点在正常通信
- 在另外一个终端中查看已有节点信息
ros2 node list
可以看到当前的两个节点,分别为listener和talker
- ros节点重映射
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=my_talker
此时能看到三个节点,包括重映射的my_talker
- 查看节点详细信息
ros2 node info my_talker
4.2 domain ID
1)在同一个物理网络中,ros2基于Domain ID切分为若干个逻辑网络。
2)在同一域(domain)中的ROS 2节点可以被自由发现并通信,在不同域中则不能互通。
3)所有的ROS 2节点默认使用domain ID 0。
4)为避免消息混淆,同网络内运行ROS 2的不同组的设备应该使用不同的domain ID
5. 常用工具
5.1 bag数据记录和回放
bag可以用于记录和回放话题topic的数据,便于分享操作数据给其他人,以及复现问题
# bag记录指定topic数据
ros2 bag record <topic_name>
# bag记录多个topic数据
ros2 bag record -o <bag_file_name> <topic_name1> <topic_name2>
ros2 bag record -o subset /turtle1/cmd_vel /turtle1/pose
# bag回放记录的数据
ros2 bag play <bag_file_name>
ros2 bag play rosbag2_2025_01_21-16_49_40
# bag查看包信息
ros2 bag info <bag_file_name>
ros2 bag info rosbag2_2025_01_21-16_49_40/
6. ros2 demo
这里以topic话题的demo为例,其他的demo大同小异
6.1 如何从0开始创建工程
# 使能环境变量
source /opt/ros/foxy/setup.bash
# 创建工作目录
mkdir -p /mnt/ros2/quicktron_example/src
cd /mnt/ros2/quicktron_example/src
# 创建example_topic包,其中包含一个node(sub_node 订阅节点)
ros2 pkg create --build-type ament_cmake --node-name sub_node example_topic
# 创建另外一个node(pub_node 发布节点)
touch src/example_topic/src/pub_node.cpp
6.2 如何在工程中添加两个节点
- 编辑pub_node.cpp
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
/// 继承自ros2节点核心类 rclcpp::Node
class MinimalPublisher : public rclcpp::Node
{
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0) ///< node名为minimal_publisher
{
/// 创建publish,消息类型为std_msgs::msg::String,topic名为example_topic,消息队列最大深度为10
publisher_ = this->create_publisher<std_msgs::msg::String>("example_topic", 10);
/// 创建定时任务,500MS执行一次
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
/// 初始化ros2系统
rclcpp::init(argc, argv);
/// 节点开始运行,阻塞直到被终止
rclcpp::spin(std::make_shared<MinimalPublisher>());
/// 终止节点运行,清理资源
rclcpp::shutdown();
return 0;
}
- 编辑sub_node.cpp
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
/// 继承自ros2节点核心类 rclcpp::Node
class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
///创建subscript,消息类型为std_msgs::msg::String,topic名为example_topic
subscription_ = this->create_subscription<std_msgs::msg::String>(
"example_topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
/// 初始化ros2系统
rclcpp::init(argc, argv);
/// 节点开始运行,阻塞直到被终止
rclcpp::spin(std::make_shared<MinimalSubscriber>());
/// 终止节点运行,清理资源
rclcpp::shutdown();
return 0;
}
- 更改package.xml,添加依赖项
主要更改的是以下内容,添加rclcpp及std_msgs的依赖:
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
更改后的完整内容如下:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>example_topic</name>
<version>0.0.0</version>
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
- 更改CMakeLists.txt
主要更改的是以下内容,添加了两个节点的编译依赖,头文件包含,目标产物生成等
add_executable(sub_node src/sub_node.cpp)
add_executable(pub_node src/pub_node.cpp)
ament_target_dependencies(sub_node rclcpp std_msgs)
ament_target_dependencies(pub_node rclcpp std_msgs)
target_include_directories(sub_node PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(pub_node PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS
sub_node
pub_node
DESTINATION lib/${PROJECT_NAME})
更改后的完整内容如下:
cmake_minimum_required(VERSION 3.5)
project(example_topic)
# 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)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
add_executable(sub_node src/sub_node.cpp)
add_executable(pub_node src/pub_node.cpp)
ament_target_dependencies(sub_node rclcpp std_msgs)
ament_target_dependencies(pub_node rclcpp std_msgs)
target_include_directories(sub_node PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(pub_node PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS
sub_node
pub_node
DESTINATION lib/${PROJECT_NAME})
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()
ament_package()
6.3 如何进行工程编译与运行
cd /mnt/ros2/quicktron_example
# 使能工程local环境变量
. install/local_setup.bash
export CYCLONEDDS_URI=file:///opt/ros/cyclonedds/config/cyclonedds.xml
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
source /opt/ros/foxy/setup.bash
source /opt/ros/cyclonedds/setup.bash
# 编译并安装
colcon build --symlink-install
# 使用两个终端,一个终端运行sub,一个运行pub
source /opt/ros/foxy/setup.bash
. install/local_setup.bash
ros2 run example_topic sub_node
source /opt/ros/foxy/setup.bash
. install/local_setup.bash
ros2 run example_topic pub_node
运行结果如下:
使用ros2 topic list及ros2 node list查询可看到当前的节点及node
7. ros2常用命令
# source 环境变量
source /opt/ros/foxy/setup.bash
source /opt/ros/cyclonedds/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///opt/ros/cyclonedds/config/cyclonedds.xml
# 检查ros环境变量,版本
printenv | grep -i ROS
# 例如
ROS_VERSION=2
ROS_PYTHON_VERSION=3
ROS_DISTRO=foxy
# 编译并安装
colcon build --symlink-install
# 编译指定package
colcon build --packages-select example_topic
# 编译并安装到指定目录
colcon build --symlink-install --install-base /opt/ros/cyclonedds/
# 运行ros2节点
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp listener
# 查看ros2节点列表
ros2 node list
# 重映射节点
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=my_talker
# 查看节点详细信息
ros2 node info /my_talker
# 查看话题列表
ros2 topic list
# 查看话题列表及数据类型
ros2 topic list -t
# 查看topic发送数据频率
ros2 topic hz /chatter
# 查看话题回显
ros2 topic echo /chatter
ros2 topic info /chatter
# 查看数据类型构成信息
ros2 interface show std_msgs/msg/String
# 向topic发送一条信息
ros2 topic pub --once /chatter std_msgs/msg/String "{data: '123456test'}"
# 以1HZ频率(1S)向topic发送一条信息
ros2 topic pub --rate1 /chatter std_msgs/msg/String "{data: '123456test'}"
# 查看service列表
ros2 service list
# 查看service列表及服务类型
ros2 service list -t
# 查看service类型
ros2 service type /talker/describe_parameters
# 查找特定类型service
ros2 service find rcl_interfaces/srv/GetParameterTypes
# service服务调用,修改参数
ros2 service call /listener/set_parameters_atomically rcl_interfaces/srv/SetParametersAtomically "{parameters: [{name: 'use_sim_time', value: {bool_value: true}}]}"
# 查看参数列表
ros2 param list
# 参数获取
ros2 param get <node_name> <parameter_name>
ros2 param get /listener use_sim_time
# 参数设置
ros2 param set <node_name> <parameter_name> <value>
ros2 param set /listener use_sim_time False
# 参数转存到文件
ros2 param dump <node_name>
ros2 param dump /listener
# 参数从文件加载
ros2 param load <node_name> <parameter_file>
ros2 param load /listener listener.yaml
# 节点启动时加载参数文件
ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>
# 查看动作列表
ros2 action list
ros2 action list -t
ros2 action info <action_name>
ros2 interface show <action_name>
ros2 action send_goal <action_name> <action_type> <values>
# bag记录指定topic数据
ros2 bag record <topic_name>
# bag记录多个topic数据
ros2 bag record -o <bag_file_name> <topic_name1> <topic_name2>
ros2 bag record -o subset /turtle1/cmd_vel /turtle1/pose
# bag回放记录的数据
ros2 bag play <bag_file_name>
ros2 bag play rosbag2_2025_01_21-16_49_40
# bag查看包信息
ros2 bag info <bag_file_name>
ros2 bag info rosbag2_2025_01_21-16_49_40/