ROS2——什么是接口
接口简介
在机器人系统中, 往往有许许多多的硬件, 比如摄像头, 激光雷达, 每一个硬件发送的数据/数据类型是不同的, 那么ROS2是如何使用这些数据的呢?
前文KFC
和Hamburger
中我们使用过这些定义:
1 2 3 4 5
| #include "std_msgs/msg/string.hpp" #include "std_msgs/msg/u_int32.hpp" ... <std_msgs::msg::String> <std_msgs::msg::UInt32>
|
这里的std_msgs
是ROS2内置的一个接口功能包.
查看某一个接口包下所有的接口:
1
| ros2 interface package std_msgs
|

当然, 这些内置接口也许不能满足你的需求, 此时就可以自定义接口.
自定义接口
针对ROS2的通信方式, 接口可以分为:
话题接口
文件名: *.msg
This is your custom message that transfers a single 64-bit integer called num
.
服务接口
文件名:*.srv
1 2 3 4 5
| int64 a int64 b int64 c --- int64 sum
|
This is your custom service that requests three integers named a
, b
, and c
, and responds with an integer called sum
.
动作接口
文件名:*.action
1 2 3 4 5
| int32 order --- int32[] sequence --- int32[] partial_sequence
|
自定义话题接口
前情提要: 前面的KFC实例中, KFC会定时发送一条广告话题, 如果要发送图片信息怎么办? 这就需要自定义KFC专属消息接口.
编辑.msg文件
cd到工作空间的/src
文件夹下, 新建接口包
1 2
| cd ros2_ws/src ros2 pkg create topic_interfaces --build-type ament_cmake
|
cd进入topic_interfaces
文件夹, 新建KFC.msg
文件(首字母要求大写)
1 2
| mkdir msg touch msg/KFC.msg
|
编辑KFC.msg
1 2 3 4 5 6 7 8
| # 原始数据类型string string txt
# 原始数据类型 uint32 uint32 money
# 图像消息,调用sensor_msgs下的Image类型 sensor_msgs/Image image
|
修改Cmakelist.txt
注意:rosidl_generate_interfaces()
必须在 ament_package()
前
1 2 3 4 5 6 7 8 9
| find_package(sensor_msgs REQUIRED) find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME} "msg/Ad.msg" DEPENDENCIES sensor_msgs )
|
修改package.xml
1 2 3 4 5
| <depend>sensor_msgs</depend> <build_depend>rosidl_default_generators</build_depend> <exec_depend>rosidl_default_runtime</exec_depend> <member_of_group>rosidl_interface_packages</member_of_group>
|
编译并刷新环境变量
1 2
| colcon build --packages-select topic_interfaces source ~/.bashrc
|
使用自定义话题接口修改KFC与Customer
上代码! 请读者自行对比区别, // CHANGE
为修改的地方
新建文件Customer_with_interfaces.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include "rclcpp/rclcpp.hpp"
#include "topic_interfaces/msg/kfc.hpp"
using namespace std::chrono_literals;
using std::placeholders::_1;
class CustomerNode : public rclcpp::Node { public: CustomerNode(std::string name) : Node(name) { RCLCPP_INFO(this->get_logger(), "大家好,我是一个%s.",name.c_str());
sub_hamburger = this->create_subscription<topic_interfaces::msg::KFC>("hamburger", 10, std::bind(&CustomerNode::hamburger_callback, this, _1)); sub_advertisement = this->create_subscription<topic_interfaces::msg::KFC>("advertisement", 10, std::bind(&CustomerNode::advertisement_callback, this, _1)); hungry_timer = this->create_wall_timer(1000ms, std::bind(&CustomerNode::hungry_timer_callback, this));
pub_money = this->create_publisher<topic_interfaces::msg::KFC>("money_of_hamburger", 10); money.money = 10;
pub_money->publish(money); RCLCPP_INFO(this->get_logger(), "我饿了, 我要吃汉堡! 付款 %d 元", money.money);
} private: topic_interfaces::msg::KFC money;
rclcpp::TimerBase::SharedPtr hungry_timer;
rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_hamburger;
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_money;
rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_advertisement; void hamburger_callback(const topic_interfaces::msg::KFC::SharedPtr msg) { RCLCPP_INFO(this->get_logger(), "这是我吃的 %s ", msg->txt.c_str()); }
void hungry_timer_callback() { RCLCPP_INFO(this->get_logger(), "我又饿了, 还想再吃一个! 付款 %d 元", money.money); pub_money->publish(money); }
void advertisement_callback(const topic_interfaces::msg::KFC::SharedPtr msg) { RCLCPP_INFO(this->get_logger(), "我收到了一条广告: %s ", msg->txt.c_str()); } };
int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<CustomerNode>("Customer"); rclcpp::spin(node); rclcpp::shutdown(); return 0; }
|
新建文件KFC_with_interfaces.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include "rclcpp/rclcpp.hpp" #include "topic_interfaces/msg/kfc.hpp"
using namespace std::chrono_literals;
using std::placeholders::_1;
class KFCNode : public rclcpp::Node { public: KFCNode(std::string name) : Node(name), count(1) { RCLCPP_INFO(this->get_logger(), "大家好, 我是%s的服务员.",name.c_str()); pub_hamburger = this->create_publisher<topic_interfaces::msg::KFC>("hamburger", 10); pub_advertisement = this->create_publisher<topic_interfaces::msg::KFC>("advertisement", 10); advertisement_timer = this->create_wall_timer(5000ms, std::bind(&KFCNode::advertisement_timer_callback, this)); sub_money = this->create_subscription<topic_interfaces::msg::KFC>("money_of_hamburger", 10, std::bind(&KFCNode::money_callback, this, _1)); } private: size_t count;
rclcpp::TimerBase::SharedPtr advertisement_timer;
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_hamburger; rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_money;
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_advertisement;
void advertisement_timer_callback() { auto str_advertisement = topic_interfaces::msg::KFC(); str_advertisement.txt = "大鸡腿降价啦"; RCLCPP_INFO(this->get_logger(), "KFC发布了一个广告:%s", str_advertisement.txt.c_str()); pub_advertisement->publish(str_advertisement); } void money_callback(const topic_interfaces::msg::KFC::SharedPtr msg) { if(msg->money == 10) { RCLCPP_INFO(this->get_logger(), "收款 %d 元", msg->money);
auto str_hamburger_num = topic_interfaces::msg::KFC(); str_hamburger_num.txt = "第" + std::to_string(count++) + "个汉堡"; RCLCPP_INFO(this->get_logger(), "这是我卖出的%s", str_hamburger_num.txt.c_str()); pub_hamburger->publish(str_hamburger_num); } } };
int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<KFCNode>("KFC"); rclcpp::spin(node); rclcpp::shutdown(); return 0; }
|
修改Cmakelist.txt, 添加/修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| find_package(topic_interfaces REQUIRED)
add_executable(Customer_with_interfaces_node src/Customer_with_interfaces.cpp) ament_target_dependencies(Customer_with_interfaces_node rclcpp topic_interfaces)
add_executable(KFC_with_interfaces_node src/KFC_with_interfaces.cpp) ament_target_dependencies(KFC_with_interfaces_node rclcpp topic_interfaces)
install(TARGETS Customer_node KFC_node Customer_with_interfaces_node KFC_with_interfaces_node DESTINATION lib/${PROJECT_NAME} )
|
修改package.xml, 添加:
1
| <depend>topic_interfaces</depend>
|
编译并刷新环境
1 2
| colcon build --packages-select customer_and_kfc source ~/.bashrc
|
开启两个终端, 分别运行使用自定义接口的KFC与Customer
1 2
| ros2 run customer_and_kfc Customer_with_interfaces_node ros2 run customer_and_kfc KFC_with_interfaces_node
|
成功~
详细查看接口
要想详细查看接口, 可以使用ros2 interface
命令
查看包下所有接口
1
| ros2 interface package topic_interfaces
|

查看内容
1
| ros2 interface show topic_interfaces/msg/Ad
|

显示属性
1
| ros2 interface proto topic_interfaces/msg/Ad
|

自定义服务接口
自定义服务接口将在下一篇文章讲解服务时一起讲解.