在机器人系统中, 往往有许许多多的硬件, 比如摄像头, 激光雷达, 每一个硬件发送的数据/数据类型是不同的, 那么ROS2是如何使用这些数据的呢?
前文KFC
和Hamburger
中我们使用过这些定义:
#include "std_msgs/msg/string.hpp"
#include "std_msgs/msg/u_int32.hpp"
...
<std_msgs::msg::String>
<std_msgs::msg::UInt32>
这里的std_msgs
是ROS2内置的一个接口功能包.
查看某一个接口包下所有的接口:
ros2 interface package std_msgs
当然, 这些内置接口也许不能满足你的需求, 此时就可以自定义接口.
针对ROS2的通信方式, 接口可以分为:
话题接口
文件名: *.msg
int64 num
This is your custom message that transfers a single 64-bit integer called num
.
服务接口
文件名:*.srv
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
int32 order
---
int32[] sequence
---
int32[] partial_sequence
前情提要: 前面的KFC实例中, KFC会定时发送一条广告话题, 如果要发送图片信息怎么办? 这就需要自定义KFC专属消息接口.
cd到工作空间的/src
文件夹下, 新建接口包
cd ros2_ws/src
ros2 pkg create topic_interfaces --build-type ament_cmake
cd进入topic_interfaces
文件夹, 新建KFC.msg
文件(首字母要求大写)
mkdir msg
touch msg/KFC.msg
编辑KFC.msg
# 原始数据类型string
string txt
# 原始数据类型 uint32
uint32 money
# 图像消息,调用sensor_msgs下的Image类型
sensor_msgs/Image image
注意:rosidl_generate_interfaces()
必须在 ament_package()
前
# 这两句添加依赖
find_package(sensor_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
# 声明msg文件所属的工程名字, 文件位置, 依赖DEPENDENCIES
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Ad.msg"
DEPENDENCIES sensor_msgs
)
<depend>sensor_msgsdepend>
<build_depend>rosidl_default_generatorsbuild_depend>
<exec_depend>rosidl_default_runtimeexec_depend>
<member_of_group>rosidl_interface_packagesmember_of_group>
colcon build --packages-select topic_interfaces
source ~/.bashrc
上代码! 请读者自行对比区别, // CHANGE
为修改的地方
新建文件Customer_with_interfaces.cpp
#include "rclcpp/rclcpp.hpp"
// 这个头文件是topic_interfaces接口包编译后自动生成的
#include "topic_interfaces/msg/kfc.hpp" // CHANGE
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)); // CHANGE
sub_advertisement = this->create_subscription<topic_interfaces::msg::KFC>("advertisement", 10, std::bind(&CustomerNode::advertisement_callback, this, _1)); // CHANGE
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); // CHANGE
money.money = 10; // CHANGE
pub_money->publish(money);
RCLCPP_INFO(this->get_logger(), "我饿了, 我要吃汉堡! 付款 %d 元", money.money); // CHANGE
}
private:
topic_interfaces::msg::KFC money; // CHANGE
rclcpp::TimerBase::SharedPtr hungry_timer;
rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_hamburger; // CHANGE
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_money; // CHANGE
rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_advertisement; // CHANGE
void hamburger_callback(const topic_interfaces::msg::KFC::SharedPtr msg) // CHANGE
{
RCLCPP_INFO(this->get_logger(), "这是我吃的 %s ", msg->txt.c_str()); // CHANGE
}
void hungry_timer_callback()
{
RCLCPP_INFO(this->get_logger(), "我又饿了, 还想再吃一个! 付款 %d 元", money.money); // CHANGE
pub_money->publish(money);
}
void advertisement_callback(const topic_interfaces::msg::KFC::SharedPtr msg) // CHANGE
{
RCLCPP_INFO(this->get_logger(), "我收到了一条广告: %s ", msg->txt.c_str()); // CHANGE
}
};
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
#include "rclcpp/rclcpp.hpp"
#include "topic_interfaces/msg/kfc.hpp" // CHANGE
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); // CHANGE
pub_advertisement = this->create_publisher<topic_interfaces::msg::KFC>("advertisement", 10); // CHANGE
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)); // CHANGE
}
private:
size_t count;
rclcpp::TimerBase::SharedPtr advertisement_timer;
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_hamburger; // CHANGE
rclcpp::Subscription<topic_interfaces::msg::KFC>::SharedPtr sub_money; // CHANGE
rclcpp::Publisher<topic_interfaces::msg::KFC>::SharedPtr pub_advertisement; // CHANGE
void advertisement_timer_callback()
{
auto str_advertisement = topic_interfaces::msg::KFC(); // CHANGE
str_advertisement.txt = "大鸡腿降价啦"; // CHANGE
RCLCPP_INFO(this->get_logger(), "KFC发布了一个广告:%s", str_advertisement.txt.c_str()); // CHANGE
pub_advertisement->publish(str_advertisement);
}
void money_callback(const topic_interfaces::msg::KFC::SharedPtr msg) // CHANGE
{
if(msg->money == 10) // CHANGE
{
RCLCPP_INFO(this->get_logger(), "收款 %d 元", msg->money); // CHANGE
auto str_hamburger_num = topic_interfaces::msg::KFC(); // CHANGE
str_hamburger_num.txt = "第" + std::to_string(count++) + "个汉堡"; // CHANGE
RCLCPP_INFO(this->get_logger(), "这是我卖出的%s", str_hamburger_num.txt.c_str()); // CHANGE
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, 添加/修改:
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, 添加:
<depend>topic_interfacesdepend>
编译并刷新环境
colcon build --packages-select customer_and_kfc
source ~/.bashrc
开启两个终端, 分别运行使用自定义接口的KFC与Customer
ros2 run customer_and_kfc Customer_with_interfaces_node
ros2 run customer_and_kfc KFC_with_interfaces_node
成功~
要想详细查看接口, 可以使用ros2 interface
命令
查看包下所有接口
ros2 interface package topic_interfaces
查看内容
ros2 interface show topic_interfaces/msg/Ad
显示属性
ros2 interface proto topic_interfaces/msg/Ad
自定义服务接口将在下一篇文章讲解服务时一起讲解.
更多教程欢迎进入我的博客学习~