ros2 & c++学习笔记+参考ZY_75843《机器人操作系统(ROS2)入门与实践 》_刘相权等+记录

开发环境搭建
开发环境:ros2 humble @ ubuntu22.04
开发工具:VScode及插件

第3章 ROS2编程基础
3.1 节点与软件包
3.1.1 创建workspace 然后package
ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~$ mkdir -p ros2_ws_cha3 ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~$ mkdir -p ros2_ws_cha3/src ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~$ cd ~/ros2_ws-cha3/src ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~/ros2_ws_cha3/src$ ros2 pkg create my_pkg going to create a new package package name: my_pkg destination directory: /home/ubuntu/ros2_ws_cha3/src package format: 3 version: 0.0.0 description: TODO: Package description maintainer: ['ubuntu <ubuntu@todo.todo>'] licenses: ['TODO: License declaration'] build type: ament_cmake dependencies: [] creating folder ./my_pkg creating ./my_pkg/package.xml creating source and include folder creating folder ./my_pkg/src creating folder ./my_pkg/include/my_pkg creating ./my_pkg/CMakeLists.txt [WARNING]: Unknown license 'TODO: License declaration'. This has been set in the package.xml, but no LICENSE file has been created. It is recommended to use one of the ament license identitifers: Apache-2.0 BSL-1.0 BSD-2.0 BSD-2-Clause BSD-3-Clause GPL-3.0-only LGPL-3.0-only MIT MIT-0
在VScode中打开工作空间 ros2_ws_cha3。在VScode文件树中观察工作空间中的文件结构。

3.1.2 编写node
3.1.2.1 编写节点代码

按照上述图片展示的操作,添加first_node.cpp节点源文件
// 包含ROS 2 C++客户端库的头文件 #include "rclcpp/rclcpp.hpp" int main(int argc, char * argv[]) { // 初始化ROS 2系统 // 处理命令行参数并设置必要的通信环境 rclcpp::init(argc, argv); // 创建一个共享指针指向ROS 2节点对象 // std::make_shared<rclcpp::Node>("my_node") 创建了一个名为"my_node"的节点 // auto关键字自动推导节点类型为 std::shared_ptr<rclcpp::Node> auto node = std::make_shared<rclcpp::Node>("my_node"); // 使用ROS 2的日志系统输出信息 // RCLCPP_INFO宏用于输出INFO级别的日志 // node->get_logger() 获取节点的日志记录器 // "Hello world!" 是要输出的信息 RCLCPP_INFO(node->get_logger(), "Hello world!"); // 空循环,保持程序运行 // rclcpp::ok() 检查ROS 2系统是否正常运行 // 当收到SIGINT信号(如Ctrl+C)或调用rclcpp::shutdown()时返回false while (rclcpp::ok()) { ; // 空语句,循环体什么都不做 } // 关闭ROS 2系统 // 清理资源,终止通信 rclcpp::shutdown(); // 程序正常退出 return 0; }

3.1.2.2设置编译规则
修改CMakeLists.txt
cmake_minimum_required(VERSION 3.8) project(my_pkg) 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) # uncomment the following section in order to fill in # further dependencies manually. # find_package(<dependency> REQUIRED) find_package(rclcpp REQUIRED) add_executable(first_node src/first_node.cpp) ament_target_dependencies(first_node "rclcpp") install(TARGETS first_node DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # comment the line when a copyright and license is added to all source files set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # comment the line when this package is in a git repo and when # a copyright and license is added to all source files set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif()
ament_package()
3.1.2.3 修改软件包信息
修改package.xml
<?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>my_pkg</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="ubuntu@todo.todo">ubuntu</maintainer> <license>TODO: License declaration</license> <buildtool_depend>ament_cmake</buildtool_depend> <depend>rclcpp</depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <export> <build_type>ament_cmake</build_type> </export> </package>
3.1.2.4 编译软件包
ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~/ros2_ws_cha3$ colcon build
Starting >>> my_pkg
Finished <<< my_pkg [6.39s]
Summary: 1 package finished [6.85s]
3.1.3 运行节点
ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~/ros2_ws_cha3$ source install/setup.bash
ubuntu@ubuntu-Lenovo-XiaoXin-14API-2019:~/ros2_ws_cha3$ ros2 run my_pkg first_node
[INFO] [1773564269.835462423] [my_node]: Hello world!
3.2 函数式的话题和消息
3.2.0 话题通讯简介
话题通讯是ROS2使用频率最高的一种通信方式,模拟报纸或杂志传播过程,一个话题发布者,多个话题订阅者,有发布者发布指定话题的数据,订阅者只要订阅了该话题的数据,就可以接收到数据。
话题通信是基于发布/订阅模型,如图:

话题数据传输的特性是从一个节点到另外一个节点,发送数据的对象称之为发布者,接收数据的对象称之为订阅者,每一个话题都需要有一个名字,传输的数据也需要有固定的数据类型。
3.2.1 编写话题发布者Publisher
编写节点代码
// 包含ROS 2 C++客户端库的头文件 #include "rclcpp/rclcpp.hpp" // 包含标准字符串消息类型的头文件 // std_msgs/msg/string.hpp 定义了ROS 2中的字符串消息格式 #include "std_msgs/msg/string.hpp" int main(int argc, char * argv[]) { // 初始化ROS 2系统 // 处理命令行参数并设置必要的通信环境 rclcpp::init(argc, argv); // 创建一个共享指针指向ROS 2节点对象 // 节点名称为"publisher_node",用于在ROS 2网络中标识这个节点 auto node = std::make_shared<rclcpp::Node>("publisher_node"); // 创建发布者对象 // create_publisher<消息类型>("话题名称", 队列大小) // 参数说明: // - std_msgs::msg::String: 要发布的消息类型 // - "/my_topic": 话题名称,订阅者通过这个话题接收消息 // - 10: 消息队列大小,如果发布速度超过处理速度,最多缓存10条消息 auto publisher = node->create_publisher<std_msgs::msg::String>("/my_topic", 10); // 创建字符串消息对象 std_msgs::msg::String message; // 设置消息内容 message.data = "Hello World!"; // 创建速率控制对象 // loop_rate(1) 表示每秒循环1次(即1Hz的频率) rclcpp::Rate loop_rate(1); // 主循环:当ROS系统正常运行时持续执行 while (rclcpp::ok()) { // 发布消息到话题"/my_topic" // 所有订阅了该话题的节点都会收到这条消息 publisher->publish(message); // 控制循环速率 // sleep()方法会使程序暂停足够长的时间,以保持1Hz的发布频率 // 这样可以避免CPU占用过高,并控制消息发布的节奏 loop_rate.sleep(); } // 关闭ROS 2系统 // 当收到中断信号(如Ctrl+C)退出循环后,清理资源 rclcpp::shutdown(); // 程序正常退出 return 0; }
设置编译规则,修改CMakeLists.txt
cmake_minimum_required(VERSION 3.8) project(my_pkg) 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) # uncomment the following section in order to fill in # further dependencies manually. # find_package(<dependency> REQUIRED) find_package(rclcpp REQUIRED) add_executable(first_node src/first_node.cpp) ament_target_dependencies(first_node "rclcpp") find_package(std_msgs REQUIRED) add_executable(publisher_node src/publisher_node.cpp) ament_target_dependencies(publisher_node "rclcpp" "std_msgs")
install(TARGETS first_node publisher_node DESTINATION lib/${PROJECT_NAME}) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # comment the line when a copyright and license is added to all source files set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # comment the line when this package is in a git repo and when # a copyright and license is added to all source files set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif()
ament_package()
修改软件包信息,修改package.xml
<?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>my_pkg</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="ubuntu@todo.todo">ubuntu</maintainer> <license>TODO: License declaration</license> <buildtool_depend>ament_cmake</buildtool_depend> <depend>rclcpp</depend> <depend>std_msgs</depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <export> <build_type>ament_cmake</build_type> </export> </package>
3.2.2 编写话题订阅者Subscriber
编写节点代码subscriber_node.cpp
// 包含ROS 2 C++客户端库,提供ROS 2核心功能 #include "rclcpp/rclcpp.hpp" // 包含标准字符串消息类型,用于接收文本消息 #include "std_msgs/msg/string.hpp" // 声明全局节点指针,以便在回调函数中使用 // 全局变量使得回调函数能够访问节点对象进行日志输出 std::shared_ptr<rclcpp::Node> node; // 回调函数:当接收到新消息时自动调用 // 参数:接收到的消息的共享指针,包含消息数据 void Callback(const std_msgs::msg::String::SharedPtr msg) { // 使用节点的日志记录器输出接收到的消息内容 // RCLCPP_INFO宏用于输出INFO级别的日志信息 // msg->data.c_str()获取字符串消息的内容并转换为C风格字符串 RCLCPP_INFO(node->get_logger(),"Receive : %s", msg->data.c_str()); } int main(int argc, char * argv[]) { // 初始化ROS 2系统,建立与ROS 2基础设施的连接 rclcpp::init(argc, argv); // 创建ROS 2节点对象,节点名为"subscriber_node" // 使用全局变量node存储节点指针,以便在回调函数中访问 node = std::make_shared<rclcpp::Node>("subscriber_node"); // 创建订阅者对象 // create_subscription模板参数指定消息类型为std_msgs::msg::String // 参数1:话题名称"/my_topic",订阅这个话题上的消息 // 参数2:队列大小10,当接收速度超过处理速度时最多缓存10条消息 // 参数3:回调函数指针&Callback,当收到新消息时自动调用此函数 auto subscriber = node->create_subscription<std_msgs::msg::String>( "/my_topic", // 话题名称 10, // 队列大小 &Callback // 回调函数 ); // spin()函数是ROS 2的核心循环 // 它会阻塞程序运行,持续处理回调函数 // 当收到新消息时,自动调用Callback函数 // 当收到中断信号(如Ctrl+C)或调用rclcpp::shutdown()时退出 rclcpp::spin(node); // 关闭ROS 2系统,清理资源 // spin()退出后执行,确保所有通信资源被正确释放 rclcpp::shutdown(); // 程序正常退出 return 0; }
设置编译规则,修改CMakeLists.txt
cmake_minimum_required(VERSION 3.8) project(my_pkg) 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) # uncomment the following section in order to fill in # further dependencies manually. # find_package(<dependency> REQUIRED) find_package(rclcpp REQUIRED) add_executable(first_node src/first_node.cpp) ament_target_dependencies(first_node "rclcpp") find_package(std_msgs REQUIRED) add_executable(publisher_node src/publisher_node.cpp) ament_target_dependencies(publisher_node "rclcpp" "std_msgs") add_executable(subscriber_node src/subscriber_node.cpp) ament_target_dependencies(subscriber_node "rclcpp" "std_msgs") install(TARGETS first_node publisher_node subscriber_node DESTINATION lib/${PROJECT_NAME}) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # comment the line when a copyright and license is added to all source files set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # comment the line when this package is in a git repo and when # a copyright and license is added to all source files set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() ament_package()
修改软件包信息,修改package.xml
同3.2.1的package.xml
3.3 类式的话题和消息
3.3.1 发布话题
#include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" /** * 发布者节点类 * 这个类继承自 rclcpp::Node,代表一个ROS 2节点 * 节点本身是一个逻辑实体,不直接对应线程,需要被添加到执行器中才能运行 */ class PublisherNode : public rclcpp::Node { public: PublisherNode() // 调用父类构造函数,指定节点名称 // 这个节点名称会在ROS 2网络中唯一标识该节点 : Node("publisher_node") { // 创建发布者 // 参数1: 话题名称 "/my_topic" - 其他节点可以通过订阅这个话题接收消息 // 参数2: 队列大小 10 - 如果发布速度快于网络传输,最多缓存10条消息 publisher_ = create_publisher<std_msgs::msg::String>("/my_topic", 10); // 创建定时器 // 参数1: 时间间隔 1000ms - 每秒钟触发一次回调 // 参数2: 回调函数 - 定时器触发时要执行的函数 // 注意:定时器本身只是一个计时器对象,它依赖执行器来调用其回调函数 timer_ = create_wall_timer( std::chrono::milliseconds(1000), // 1秒定时 std::bind(&PublisherNode::publishMessage, this) // 绑定成员函数作为回调 ); // 思考:这里创建的回调(定时器回调)并不会立即执行 // 它们被注册到节点中,等待被添加到执行器的线程中执行 } private: /** * 定时器回调函数 * 这个函数会被执行器在检测到定时器超时时调用 * 在单线程执行器中,这个函数会串行执行(不会被其他回调打断) * 在多线程执行器中,这个函数可能与其他回调并行执行 */ void publishMessage() { // 设置消息内容 // 注意:message_ 是成员变量,在多线程环境下需要保护 message_.data = "Hello World! from a class publisher node"; // 发布消息 // 发布操作是线程安全的,ROS 2内部已经处理好同步 publisher_->publish(message_); // 可选的日志输出(取消注释即可使用) // RCLCPP_INFO(this->get_logger(), "Published: '%s'", message_.data.c_str()); } // 发布者智能指针 // SharedPtr 表示这是一个可以被多个所有者共享的指针 rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; // 消息对象(成员变量) // 这里复用一个消息对象以减少内存分配 // 注意:如果使用多线程执行器,这里可能存在线程安全问题 std_msgs::msg::String message_; // 定时器智能指针 // 只要定时器存在,它就会持续触发回调 rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char * argv[]) { /** * 初始化ROS 2 * 这一步会设置全局状态,包括信号处理、全局参数等 * 必须在任何ROS 2函数调用之前执行 */ rclcpp::init(argc, argv); /** * 创建节点实例 * make_shared 在堆上创建节点对象,返回智能指针 * 节点对象包含了所有的发布者、订阅者、定时器等 * 但节点本身只是一个"静态"的实体,需要执行器来"驱动" */ auto node = std::make_shared<PublisherNode>(); /** * 关键点:spin 函数 * * 这个调用会: * 1. 在内部创建一个单线程执行器 (SingleThreadedExecutor) * 2. 将我们的节点添加到执行器中 * 3. 让执行器在当前线程(主线程)中开始事件循环 * * 执行器的工作流程: * while(ros ok) { * 等待任何就绪的回调(定时器、订阅消息、服务请求等) * if (有就绪回调) { * 执行这个回调(如 publishMessage) * } * } * * 因此,spin(node) 是一个阻塞调用,它会一直运行直到 rclcpp::shutdown() 被调用 */ rclcpp::spin(node); /** * 清理ROS 2资源 * 当 spin 返回后(例如按了 Ctrl+C),执行清理工作 * 会释放全局资源,销毁所有节点 */ rclcpp::shutdown(); return 0; }
设置编译规则,修改CMakeLists.txt,注意和3.2.1其他相同,修改为:
3.3.2 订阅话题
#include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" /** * 订阅者节点类 * 演示如何接收并处理来自话题的消息 * 与发布者节点配合,构成完整的发布-订阅通信模型 */ class SubscriberNode : public rclcpp::Node { public: SubscriberNode() : Node("subscriber_node") // 节点名称,会在ROS网络中唯一标识 { /** * 创建订阅者 * * 参数说明: * 1. "/my_topic" : 要订阅的话题名称 * 2. 10 : 队列大小 - 当消息到达速度超过处理速度时,最多缓存10条消息 * 3. 回调函数 : 收到消息时被执行的函数 * * 关键概念: * - 订阅者只是一个"监听器",不主动获取数据 * - 当消息到达时,ROS 2的执行器(Executor)会调用注册的回调函数 * - 回调函数在哪个线程执行,取决于执行器的配置(单线程/多线程) */ subscriber_ = create_subscription<std_msgs::msg::String>( "/my_topic", 10, std::bind(&SubscriberNode::Callback, this, std::placeholders::_1) ); } private: /** * 消息回调函数 * * 执行时机:每当有消息发布到"/my_topic"时,此函数会被执行器调用 * 参数类型:std_msgs::msg::String::SharedPtr - 共享指针,避免复制消息数据 * * 线程安全注意: * - 在单线程执行器中,此回调会串行执行,不会同时有多个实例运行 * - 在多线程执行器中,可能多个消息同时被处理,需要保护共享数据 * * 性能考虑: * - 回调函数应尽快返回,避免阻塞执行器 * - 耗时操作应放到单独的线程或使用async处理 */ void Callback(const std_msgs::msg::String::SharedPtr msg) { // RCLCPP_INFO 是ROS 2的日志宏,会输出到控制台和日志文件 // get_logger() 返回节点特有的日志器,日志会带上节点名称前缀 RCLCPP_INFO(get_logger(), "Receive : %s", msg->data.c_str()); // 可以在这里添加更多处理逻辑 // 注意:如果处理耗时较长,应考虑使用回调组(Callback Groups) // 避免影响其他回调的执行 } // 订阅者智能指针 // 只要订阅者对象存在,回调函数就会一直有效 rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_; }; int main(int argc, char * argv[]) { /** * ROS 2初始化 * 必须在使用任何ROS 2功能之前调用 * 初始化全局状态、命令行参数、信号处理等 */ rclcpp::init(argc, argv); /** * 创建订阅者节点实例 * 此时节点对象已在内存中,但尚未"激活" * 订阅者已创建,但回调函数还不会被调用 */ auto node = std::make_shared<SubscriberNode>(); /** * 关键:spin 函数 * * 这个调用会: * 1. 内部创建单线程执行器(SingleThreadedExecutor) * 2. 将节点添加到执行器中 * 3. 当前线程进入事件循环,持续等待并处理事件 * * 事件循环的工作流程: * while (rclcpp::ok()) { // 直到收到Ctrl+C或调用shutdown * wait_for_work(); // 等待消息到达或定时器触发 * execute_any_ready_callbacks(); // 执行所有就绪的回调函数 * } * * 对于订阅者节点,这个循环会: * - 检测是否有新消息到达"/my_topic" * - 如果有,调用 Callback 函数处理消息 * - 如果没有,继续等待 */ rclcpp::spin(node); /** * 清理ROS 2资源 * spin返回后(通常是因为按了Ctrl+C),执行清理工作 * 释放全局资源,销毁所有节点 */ rclcpp::shutdown(); return 0; } /** * 运行时结构分析 * * 这个订阅者节点与发布者节点配合,构成完整的通信链路: * * ┌─────────────────────────────────────────────────────────┐ * │ 进程1 (发布者) │ * │ ┌───────────────────────────────────────────────────┐ │ * │ │ 线程1 (主线程) │ │ * │ │ └── 执行器 │ │ * │ │ └── 节点 (publisher_node) │ │ * │ │ └── 定时器回调 → 发布消息 │ │ * │ └───────────────────────────────────────────────────┘ │ * └─────────────────────────────────────────────────────────┘ * │ * │ DDS中间件 (进程间通信) * ↓ * ┌─────────────────────────────────────────────────────────┐ * │ 进程2 (订阅者) │ * │ ┌───────────────────────────────────────────────────┐ │ * │ │ 线程1 (主线程) │ │ * │ │ └── 执行器 │ │ * │ │ └── 节点 (subscriber_node) │ │ * │ │ └── 收到消息 → 执行Callback() │ │ * │ └───────────────────────────────────────────────────┘ │ * └─────────────────────────────────────────────────────────┘ */ /** * 与发布者节点的对比总结 * * | 特性 | 发布者节点 | 订阅者节点 | * |---------------|-------------------------------|-------------------------------| * | 主要职责 | 主动产生数据 | 被动接收数据 | * | 核心组件 | Publisher + Timer | Subscription | * | 回调触发 | 定时器到期 | 消息到达 | * | 数据流向 | 向外发送 | 向内接收 | * | 执行器角色 | 调用定时器回调 | 调用消息回调 | * * 两者都遵循相同的ROS 2执行模型: * - 节点是功能容器 * - 执行器是调度中心 * - 线程是实际执行者 * - 回调是具体工作单元 */ /** * 扩展练习建议 * * 1. 运行这个订阅者节点,同时运行之前的发布者节点 * 观察终端输出的消息 * * 2. 修改代码,添加多个订阅者订阅不同话题 * * 3. 尝试使用 MultiThreadedExecutor,观察多线程处理效果 * * 4. 在回调函数中添加sleep,观察单线程和多线程执行器的行为差异 */
设置编译规则,修改CMakeLists.txt,注意和3.2.2其他相同,修改为:
3.4 自定义消息接口
3.4.1 通讯接口简介
在ROS系统中,无论话题、服务还是动作,都会用到一个重要的概念——通信接口。
通信并不是一个人自言自语,而是两个甚至更多个人,你来我往的交流,交流的内容是什么呢?为了让大家都好理解,我们可以给传递的数据定义一个标准的结构,这就是通信接口。
接口可以让程序之间的依赖降低,便于我们使用别人的代码,也方便别人使用我们的代码,这就是ROS的核心目标,减少重复造轮子。

ROS有三种常用的通信机制,分别是话题、服务、动作,通过每一种通信种定义的接口,各种节点才能有机的联系到一起。
创建自定义接口流程
自定义接口消息的流程与在功能包中编写可执行程序的流程类似,主要步骤如下:
- 创建接口功能包;
- 创建并编辑 .msg文件 、.srv文件、.action文件
- 编辑配置文件;
- 编译;
- 测试。
文件树(一定要按照文件树添加代码文件)
ros2 pkg create --build-type ament_cmake pkg_interfaces

3.4.2 用于 话题 的消息 topic publish-subscribe
0、在工作空间的src目录下新建pkg_interfaces功能包
ros2 pkg create --build-type ament_cmake pkg_interfaces
1、在【ROS2动作通讯服务端实现】课程中我们已经创建了自定义接口功能包,功能包pkg_interfaces下新建 msg 文件夹,msg文件夹下新建Jointmotion.msg文件,文件中输入如下内容:
# 按照等腰梯形的速度规律控制电机位移
int32 id # 关节编号,也是通信地址
string cmd # 命令
float64 d # displacement # 位移 (单位: 米)
float64 v # velocity # 速度 (单位: 米/秒)
float64 a # acceleration # 加速度 (单位: 米/秒²)
|
|
2、在package.xml和CMakeLists.txt中添加如下配置:

CMakeLists.txt
package.xml
<buildtool_depend>rosidl_default_generators</buildtool_depend> <exec_depend>rosidl_default_runtime</exec_depend> <depend>action_msgs</depend> <member_of_group>rosidl_interface_packages</member_of_group>

3、编译
colcon build --packages-select pkg_interfaces
4、刷新terminal环境
source install/setup.bash
5、查看接口类型,正常情况下,终端将会输出与Person.msg文件一致的内容。
ros2 interface show pkg_interfaces/msg/Person
3.4.3 用于 服务 的消息 service request-response
服务通讯是一种基于请求响应的通信模型,在通信双方中,客户端发送请求数据到服务端,服务端响应结果给客户端。
客户端/服务器模型如下:

从服务的实现机制上来看,这种你问我答的形式叫做客户端/服务器模型,简称为CS模型,客户端在需要某些数据的时候,针对某个具体的服务,发送请求信息,服务器端收到请求之后,就会进行处理并反馈应答信息。
这种通信机制在生活中也很常见,比如我们经常浏览的各种网页,此时你的电脑浏览器就是客户端,通过域名或者各种操作,向网站服务器发送请求,服务器收到之后返回需要展现的页面数据。
|
在【ROS2动作通讯服务端实现】课程中我们已经创建了自定义接口功能包,功能包pkg_interfaces下新建srv文件夹,srv文件夹下新建Add.srv文件,文件中输入如下内容: int32 num1
int32 num2 --- int32 sum |
3.4.4 用于 动作 的消息 action goal feedback result
动作通信是一种带有连续反馈的通信模型,在通信双方中,客户端发送请求数据到服务端,服务端响应结果给客户端,但是在服务端接收到请求到产生最终响应的过程中,会发送连续的反馈信息到客户端。
动作通讯客户端/服务器模型如下:

| 接着在pkg_interfaces功能包下面创建一个action的文件夹,并在action文件夹内新建【Progress.action】文件,文件内容如下 |
int64 num
---
int64 sum
---
float64 progress
|

浙公网安备 33010602011771号