ROS2 + C++ 自定义接口(自用笔记)

系列文章目录

留空



前言

自用


一、什么是接口

1.什么是接口

在ROS 2中,接口就是一种规范,它可以帮助不同系统或组件之间进行通信和交互。接口的使用可以帮助抹平不同数据类型和编程语言之间的差异,使得不同厂家生产的硬件设备或传感器可以统一使用相同的接口格式,从而简化程序适配和开发过程。

ROS2接口类似于手机充电口,在手机发展的早期阶段,各家手机制造商为了区分自己的产品,导致了市场上出现了各种不同形状和规格的手机充电口,如圆形、扁平、长形或短形等,这些充电口往往互不兼容,若是一家四口出门,每个人的手机牌子不一样,出门就需要带四根线,特别麻烦。

随着技术的发展,手机行业开始寻求标准化的解决方案。现在,所有手机品牌都使用标准Type-C接口,无论哪个手机品牌,每个人都可以使用Type-C的充电口充电。这不仅减少了混乱和不便,还使得充电更加高效和便捷。

类似地,ROS2中的接口定义了一组标准化的规则和约定,用于实现不同模块之间的通信和交互。上一篇“话题”笔记里,编写发布者和订阅者代码时的第一步都是导入接口类型,发布者在“小说”话题上发布的是字符串std_msgs::msg::String类型的消息,那么“小说”话题的订阅者也要导入相同的消息类型才能接收到正确的消息。

2.自定义接口

在ROS2中,话题、服务和动作三种通信方式都支持自定义接口,而参数则通常用于在节点之间共享配置信息,无自定义接口。现在,让我们更详细地解释这三种自定义接口(话题接口、服务接口、动作接口)之间的区别,以及如何在程序中使用它们。

以下是这三种自定义接口(话题接口、服务接口、动作接口)之间的区别。

(1)话题接口

话题接口定义了在节点之间传递的消息类型,通常使用.msg文件定义。

xxx.msg

int64 num

(2)服务接口

使用.srv文件定义,包含请求(request)和响应(response)两部分,中间用三条杠杠隔开。

xxx.srv

int64 a  
int64 b  
---  
int64 sum

(3)动作接口

使用.action文件定义,包含了目标(goal)、反馈(feedback)和结果(result)三部分。

xxx.action

int32 order  
---  
int32[] sequence  
---  
int32[] partial_sequence

ROS2提供了IDL(接口定义语言)工具来处理这些接口定义文件(.msg、.srv、.action),并将它们转换为各种编程语言(如Python和C++)的头文件。这些头文件包含了消息、服务和动作的定义,以及用于序列化和反序列化消息的函数。

有了这些头文件,我们就可以在ROS2节点代码中导入对应的接口,使用里面的接口定义了。

二、题目

在下一篇文章中,刚好是学习"服务"的内容。在这之前,我们可以先编写一个自定义的服务接口,然后将其应用到"服务"相关的代码中。

服务是一种请求/响应模式的通信方式,节点A可以向节点B发送请求,并等待节点B响应。

那么下一章服务中,我们就延续"话题"的小故事。

李四创建一个“小说”话题,并在上面发布和更新“没空”小说的章节,王二订阅此话题。

然后,张三也想看“没空”小说,但是不想一章一章的看,所以支付给王二钱,请求王二小说每更新五章,就一块发给他(请求)。王二同意了并等待李四更新小说,凑够五章就给张三发过去(响应)。

那么我们就需要两种消息类型,一个是张三给王二的钱“uint32”无符号整形,王二的返回是“string”类型的数组,表示多个小说章节。( 一章就是string,多个章节就需要数组string[ ] )

三、创建功能包

cd towm_ws/src
ros2 pkg create learning_interfaces1 --build-type ament_cmake --dependencies rclcpp

四、编写接口代码

编写接口代码的步骤:

1、确定数据结构
2、新建srv文件夹,并在文件夹下新建xxx.srv
3、在xxx.srv下编写服务接口内容并保存
4、在CmakeLists.txt添加依赖和srv文件目录
5、在package.xml中添加xxx.srv所需的依赖
6、编译功能包即可生成python与c++头文件

1.确定数据结构

  • 请求(request):张三给王二的钱 uint32 money
  • 响应(response):王二返回张三五个小说章节 String[ ] novels

2.新建srv文件夹,并在文件夹下新建xxx.srv

注:不是在src文件夹下创建
在这里插入图片描述

3.在xxx.srv下编写服务接口内容并保存

SellNovel.srv

#张三给王二的钱
uint32 money
---
#王二给张三五章小说
string[] novels

4、在CmakeLists.txt添加依赖和srv文件目录

我们还需要修改CMakeLists.txt文件。

第一行代码:告诉CMake“我需要一个叫做rosidl_default_generators的包。
(这个包是ROS2的接口定义语言(IDL)工具的一部分,它负责根据.msg、.srv和.action文件生成各种编程语言的源代码。)

第二行代码: “CMake,我需要一个叫rosidl_default_generators的工具包来帮我从.srv文件中生成代码。这样我就可以在我的ROS2节点中使用这个服务了。”

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/SellNovel.srv"
 )

5、在package.xml中添加xxx.srv所需的依赖

为了能够正确生成和使用自定义消息、服务和动作接口,需要在package.xml文件中声明对相应的生成器和运行时的依赖。

第一行代码:这个依赖项是为了使用rosidl_generate_interfaces函数生成自定义的消息、服务和动作接口的代码。
第二行代码:这个依赖项是为了在运行时使用生成的自定义消息、服务和动作接口。
第三行代码:如果软件包包含自定义的接口,并且使用了rosidl_default_generatorsrosidl_default_runtime,则建议添加这行代码来明确说明我们的软件包属于rosidl接口包的一部分。

  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>

6、编译功能包即可生成python与c++头文件

colcon build --packages-select village_interfaces

五、测试

使用命令行看看我们的自定义接口。

ros2 interface list

Services:
在这里插入图片描述
查看包下所有接口

ros2 interface package learning_interfaces1

在这里插入图片描述
查看内容

ros2 interface show learning_interfaces1/srv/SellNovel

在这里插入图片描述
显示属性

ros2 interface proto learning_interfaces1/srv/SellNovel

在这里插入图片描述


总结

自用

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要将自定义消息类型通过UDP发送,需要按以下步骤进行操作: 1. 定义自定义消息类型 在ROS中,自定义消息类型通常是通过.msg文件定义的。您可以使用ROS提供的标准消息类型,也可以自己定义消息类型。例如,假设您希望定义一个名为“my_message”的自定义消息类型,可以按照以下步骤进行操作: - 在ROS工作空间中创建一个名为“my_message”的包。 - 在该包中创建一个名为“msg”的文件夹。 - 在“msg”文件夹中创建一个名为“MyMessage.msg”的文件。 - 在该文件中定义您的自定义消息类型,例如: ``` Header header uint16 id float32 x float32 y ``` 2. 生成消息源代码 在定义自定义消息类型后,需要使用ROS的消息生成工具生成消息源代码。运行以下命令: ``` $ cd <catkin_ws> $ catkin_make ``` 该命令将自动生成消息源代码,并将其放在“<catkin_ws>/devel/include”目录中。 3. 编写发送程序 现在,您可以编写发送程序来发送自定义消息类型。在ROS中,您可以使用ROS的发布者(Publisher)来发送消息。以下是一个简单的发送程序示例: ``` #include <ros/ros.h> #include <my_message/MyMessage.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <unistd.h> int main(int argc, char **argv) { ros::init(argc, argv, "udp_publisher"); ros::NodeHandle nh; // 创建UDP套接字 int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return 1; } // 设置目标地址和端口号 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 创建ROS发布者 ros::Publisher pub = nh.advertise<my_message::MyMessage>("my_topic", 1000); // 发送消息 while (ros::ok()) { my_message::MyMessage msg; // 填充消息 msg.id = 1; msg.x = 1.0; msg.y = 2.0; pub.publish(msg); // 将消息序列化为字节数组 uint8_t buffer[1024]; ros::serialization::OStream stream(buffer, sizeof(buffer)); ros::serialization::serialize(stream, msg); // 发送消息 if (sendto(sock, buffer, stream.getLength(), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("sendto"); return 1; } // 等待1秒 ros::Duration(1.0).sleep(); } // 关闭UDP套接字 close(sock); return 0; } ``` 在该程序中,我们首先创建了一个UDP套接字,并设置目标地址和端口号。然后,我们创建了一个ROS发布者,并在循环中发送自定义消息类型。在发送消息之前,我们将消息序列化为字节数组,并使用sendto()函数将其发送到目标地址。 4. 运行程序 在编写发送程序后,您可以使用ROS的运行工具(如roslaunch)来启动程序。在启动之前,请确保ROS主节点已经运行。运行以下命令: ``` $ roslaunch my_package udp_publisher.launch ``` 在程序运行时,它将定期发送自定义消息类型。您可以使用ROS的订阅者(Subscriber)来接收该消息。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值