前文链接
【轻量级开源ROS 的机器人设备(4)】--(2)通信实现_无水先生的博客-CSDN博客
数据流 虽然 XML-RPC 为远程方法调用提供了一种简单而干净的协议,但其冗长和以文本为中心的编码使其不适合高带宽和低延迟任务。数据流就是这种情况,例如由传感器或执行器发送的数据流。 ROS 为此类流定义了自定义二进制协议;这允许在节点之间传输原始数据,所需的消息长度最小(带宽最大化)并且几乎没有处理时间(延迟最小化)。有关主题和服务消息语法的详细信息 。
6.1 类型描述符数据流基于特定类型的消息交换,在注册时由主题/服务指定(参见上一节)。消息是一系列值,按照类型描述符定义的顺序排列。 ROS中有两种,主题描述符和服务描述符。我们将首先考虑主题描述符,因为服务类型只是一个简单的扩展。 主题类型 ROS 主题消息描述符被编写为纯文本文件,其中文件名与类型名称一致,加上 .msg 扩展名。
每行都可以定义一个强类型变量,其中类型可以是原始类型,也可以是另一个消息类型描述符的名称,甚至可以是任何此类类型的数组(固定或变量)。因此,消息可以嵌套,并且由于是强类型,它们的流式传输得到简化(无需处理动态类型)。描述符还可以声明原始类型的常量名称。
清单 4.3 显示了 rosgraph_msgs/msg/Log.msg 的内容,而清单 4.4 是其干净的扩展版本,由 rosmsg show 生成。
服务类型 虽然主题是单向的,因此只需要定义一种消息类型,但服务以请求/响应方式工作
1 ##
2 ## Severity level constants
3 ##
4 byte DEBUG=1 #debug level
5 byte INFO=2 #general level
6 byte WARN=4 #warning level
7 byte ERROR=8 #error level
8 byte FATAL=16 #fatal/critical level
9 ##
10 ## Fields
11 ##
12 Header header
13 byte level
14 string name # name of the node
15 string msg # message
16 string file # file the message came from
17 string function # function the message came from
18 uint32 line # line the message came from
19 string[] topics # topic names that the node publishes
Listing 4.3: Contents of rosgraph_msgs/msg/Log.msg
byte DEBUG=1
2 byte INFO=2
3 byte WARN=4
4 byte ERROR=8
5 byte FATAL=16
6 std_msgs/Header header
7 uint32 seq
8 time stamp
9 string frame_id
10 byte level
11 string name
12 string msg
13 string file
14 string function
15 uint32 line
16 string[] topics
Listing 4.4: Clean, expanded contents of rosgraph_msgs/msg/Log.ms
因此,服务涉及两种类型,一种用于请求,一种用于响应。服务类型描述符的存储方式与主题类型类似,但扩展名为 .srv。描述符被 -- 分隔线分成两部分,其中第一部分是请求消息描述符,第二部分是响应消息描述符。清单 4.5 是 dynamic_reconfigure/srv/Reconfigure.srv 内容的示例,而清单 4.6 是其干净的扩展版本,由 rossrv show 生成。
散列 因为节点之间的描述符文本可能不同,而它们的名称可以相同,所以必须有一种方法来确定两个节点是否实际上共享相同的类型。对于此类任务,选择 MD5 和来比较两种不同的类型描述。比较函数对每个类型描述符执行以下操作:
1.评论被删除;
2. 去除空格;
3.去除依赖包名;
4. 常量移到变量声明之前,保持它们的排列;
5. 对于嵌套类型,计算它们的散列文本并按照它们出现的顺序附加到当前散列文本;
6.计算整个哈希文本的MD5和。如果 MD5 和相同,则两个描述符相等。
1 Config config
2 ---
3 Config config
Listing 4.5: Contents of dynamic_reconfigure/srv/Reconfigure.srv
1 dynamic_reconfigure/Config config
2 dynamic_reconfigure/BoolParameter[] bools
3 string name
4 bool value
5 dynamic_reconfigure/IntParameter[] ints
6 string name
7 int32 value
8 dynamic_reconfigure/StrParameter[] strs
9 string name
10 string value
11 dynamic_reconfigure/DoubleParameter[] doubles
12 string name
13 float64 value
14 dynamic_reconfigure/GroupState[] groups
15 string name
16 bool state
17 int32 id
18 int32 parent
19 ---
20 dynamic_reconfigure/Config config
21 dynamic_reconfigure/BoolParameter[] bools
22 string name
23 bool value
24 dynamic_reconfigure/IntParameter[] ints
25 string name
26 int32 value
27 dynamic_reconfigure/StrParameter[] strs
28 string name
29 string value
30 dynamic_reconfigure/DoubleParameter[] doubles
31 string name
32 float64 value
33 dynamic_reconfigure/GroupState[] groups
34 string name
35 bool state
36 int32 id
37 int32 parent
Listing 4.6: Clean, expanded contents of dynamic_reconfigure/srv/Reconfigure.srv
ROS type C99 type
uint8 uint8_t
uint16 uint16_t
uint32 uint32_t
uint64 uint64_t
int8 int8_t
int16 int16_t
int32 int32_t
int64 int64_t
time uros_time_t
duration uros_time_t
string struct UrosString { size_t length; char *datap; };
type[] struct UrosTcpRosArray { uint32_t length; void *entriesp; };
byte uint8_t
char char
uint uint32_t
int int32_t
Table 4.1: ROS to C type mapping
消息以小端二进制形式序列化。这使得市场上大多数小端 CPU 架构(例如基于 x86 或 ARM 设计的架构)的处理变得简单。因此,数据流消息很像 C 结构变量的序列化;例如,在 x86 架构上,消息是变量本身的一对一转储,因为内存中的 8 位字段对齐(相比之下,ARM 内核为 32 位)。
基本类型可以直接映射到 C 类型,如表 4.1 所示。可变长度数组和字符串(字符数组)是一种特殊情况,因为它们提供 uint32 长度的条目数,后跟序列化条目本身。相比之下,固定长度数组被视为指定类型的条目序列。任何嵌套类型都被扩展为它们各自的基本类型或数组。
在刚刚描述的消息内容之前,uint32 值预计内容本身的总长度。
主题消息简单地遵循上面描述的长度+内容方案,因为主题涉及相同类型的单向消息。 “你好,世界!”的一个例子std_msgs/String 类型的消息如图 4.4 所示。流偏移量以粗体字写为十六进制索引;如果可打印,值将写为 ASCII 字符,否则将写为十六进制数字。右侧的注释表示正在流式传输的特定字段及其人类可读的值。
00 01 02 03 uint32 message_length
11 00 00 00 17
04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 string data
0D 00 00 00 H e l l o , W o r l d ! 13 "Hello, World!"
Figure 4.4: Dump of a "Hello, World!" message of type std_msgs/String
00 01 02 03 uint32 message_length
10 00 00 00 16
04 05 06 07 08 09 0A 0B int64 a
D2 04 00 00 00 00 00 00 1234
0C 0D 0E 0F 10 11 12 13 int64 b
2E 16 00 00 00 00 00 00 5678
Figure 4.5: Dump of a call to service of type rospy_tutorials/AddTwoInts
服务消息 服务请求的序列化方式与主题消息相同。相反,响应的序列化需要考虑响应的状态,它可以是实际的响应值,也可以是错误消息。
在响应之前发送一个所谓的 ok 字节。如果为 1,则表示后面的流数据是预期的服务响应类型。如果为 0 则表示在处理请求时发生错误,后面的流值是字符串类型,代表人类可读的错误文本。
rospy_tutorials/AddTwoInts服务请求和响应示例如图4.5和图4.6所示。相反,可以在图 4.7 中看到报告未知错误的服务响应。
一旦主题/服务客户端和相关服务器连接,它们就必须就流参数达成一致。它们包括主题/服务名称和类型、多个服务请求的可能性、带宽优化的使用等。所有这些参数都由连接标头设置,连接标头是两个端点在整个流的最开始发送的特殊消息。首先,客户端发送一个标头,作为一个请求。然后服务器将使用另一个标头进行回复,该标头充当响应。
连接标头由字段组成。它们像字符串一样被编码,因此告诉字符串长度的 uint32 在字符串字符之前发送。该值遵循 field=value 格式,其中可用字段及其语义取决于主题/服务请求/响应标头。连接标头由一个包含整个标头长度的 uint32 引入,就像普通消息一样。
00 uint8 ok
01 acknowledge
01 02 03 04 uint32 message_length
08 00 00 00 8
05 06 07 08 09 0A 0B 0C int64 sum
00 1B 00 00 00 00 00 00 6912
Figure 4.6: Dump of the response to the service call of Figure 4.5
Figure 4.6: Dump of the response to the service call of Figure 4.5
00 uint8 ok
00 error
01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 string error_text
0D 00 00 00 U n k n o w n e r r o r 13 "Unknown error"
Figure 4.7: Dump of a service error response

表 4.2 总结了连接标头的字段及其存在,在主题、服务或错误标头 (X) 中,其中一些是可选的 ((X))或者可以通过请求标头探测(字段值等于 *)。
图 4.8 和图 4.9 分别显示了 /turtlesim 节点的 /turtle1/command_velocity 的请求和响应连接头,通过 turtlesim_teleop 节点。
00 01 02 03 uint32 header_length
91 00 00 00 145
04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A (string field)
13 00 00 00 c a l l e r i d = / t u r t l e s i m
1B 1C 1D 1E 1F 20 21 22 23 24 25 (string field)
27 00 00 00 m d 5 s u m =
26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35
9 d 5 c 2 d c d 3 4 8 a c 8 f 7
36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45
6 c e 2 a 4 3 0 7 b d 6 3 a 1 3
46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 (string field)
0D 00 00 00 t c p _ n o d e l a y = 0
57 58 59 5A 5B 5C 5D 5E 5F 60 (string field)
1F 00 00 00 t o p i c =
61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79
/ t u r t l e 1 / c o m m a n d _ v e l o c i t y
7A 7B 7C 7D 7E 7F 80 81 82 (string field)
17 00 00 00 t y p e =
83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94
t u r t l e s i m / V e l o c i t y
Figure 4.8: Dump of a connection header request
00 01 02 03 uint32 header_length
A5 00 00 00 165
04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E
13 00 00 00 c a l l e r i d = / t e l e o p _ t u r t l e
1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C
0A 00 00 00 l a t c h i n g = 0
2D 2E 2F 30 31 32 33 34 35 36 37
27 00 00 00 m d 5 s u m =
38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47
9 d 5 c 2 d c d 3 4 8 a c 8 f 7
48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57
6 c e 2 a 4 3 0 7 b d 6 3 a 1 3
58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E
32 00 00 00 m e s s a g e _ d e f i n i t i o n =
6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D
f l o a t 3 2 l i n e a r \n
7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D
f l o a t 3 2 a n g u l a r \n
8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8
17 00 00 00 t y p e = t u r t l e s i m / V e l o c i t y
Figure 4.9: Dump of a response to the connection header in Figure 4.8
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复
require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame
Devise是一个Ruby库,它为我提供了这个User类:classUser当写入:confirmable时,注册时会发送一封确认邮件。上周我不得不批量创建300个用户,所以我在恢复之前注释掉了:confirmable几分钟。现在我正在为用户批量创建创建一个UI,因此我需要即时添加/删除:confirmable。(我也可以直接修改Devise的源码,但我宁愿不去调和它)问题:如何即时添加/删除:confirmable? 最佳答案 WayneConrad的解决方案:user=User.newuser.skip_confirmation