![han J](/assets/img/avatar_default.png)
* add remapping function for ros2 plugin * Optimize the logical structure of remapping. * Optimize the remapping's logic * Simplify details * - update the ROS2 plugin documentation to correct the description of the remapping rules and optimize examples; - enhance the Ros2RpcBackend class by adding exception handling logic to the GetRemappedFuncName method; - add a function initialization report feature; - write unit test cases for GetRemappedFuncName covering various scenarios. * Update the table format in the documentation, correcting the regular expression capturing groups for function names * Decrease Redundancy * simplify the function : GetRealRosFuncName * Optimized the way of obtaining the message type, reducing unnecessary string constructions. --------- Co-authored-by: hanjun <hanjun@agibot.com>
307 lines
27 KiB
Markdown
307 lines
27 KiB
Markdown
# ROS2 插件
|
||
|
||
## 相关链接
|
||
|
||
参考示例:
|
||
- {{ '[ros2_plugin]({}/src/examples/plugins/ros2_plugin)'.format(code_site_root_path_url) }}
|
||
|
||
|
||
## 插件概述
|
||
|
||
|
||
**ros2_plugin**是一个基于 [ROS2 Humble](https://docs.ros.org/en/humble/index.html) 实现的通信传输插件,此插件提供了以下组件:
|
||
- `ros2`类型 RPC 后端
|
||
- `ros2`类型 Channel 后端
|
||
|
||
插件的配置项如下:
|
||
|
||
| 节点 | 类型 | 是否可选 | 默认值 | 作用 |
|
||
| ------------------- | ------ | -------- | --------------- | ---------------------------------------------------------------------------------- |
|
||
| node_name | string | 必选 | "" | ROS2 节点名称 |
|
||
| executor_type | string | 可选 | "MultiThreaded" | ROS2 执行器类型,可选值:"SingleThreaded"、"StaticSingleThreaded"、"MultiThreaded" |
|
||
| executor_thread_num | int | 可选 | 2 | 当 executor_type == "MultiThreaded" 时,表示 ROS2 执行器的线程数 |
|
||
|
||
|
||
关于**ros2_plugin**的配置,使用注意点如下:
|
||
- `node_name`表示 ROS2 节点名称,在外界看来,加载了 ROS2 插件的 AimRT 节点就是一个 ROS2 节点,它的 node 名称就是根据此项来配置。
|
||
- `executor_type`表示 ROS2 节点执行器的类型,当前有三种选择:`SingleThreaded`、`StaticSingleThreaded`、`MultiThreaded`,具体的含义请参考 ROS2 Humble 的文档。
|
||
- `executor_thread_num`仅在`executor_type`值为`MultiThreaded`时生效,表示 ROS2 的线程数。
|
||
|
||
|
||
此外,在使用**ros2_plugin**时,Channel 订阅回调、RPC Server 处理、RPC Client 返回时,使用的都是**ros2_plugin**提供的执行器,当使用者在回调中阻塞了线程时,有可能导致**ros2_plugin**线程池耗尽,从而无法继续接收/发送消息。正如 Module 接口文档中所述,一般来说,如果回调中的任务非常轻量,那就可以直接在回调里处理;但如果回调中的任务比较重,那最好调度到其他专门执行任务的执行器里处理。
|
||
|
||
|
||
以下是一个简单的示例:
|
||
```yaml
|
||
aimrt:
|
||
plugin:
|
||
plugins:
|
||
- name: ros2_plugin
|
||
path: ./libaimrt_ros2_plugin.so
|
||
options:
|
||
node_name: example_ros2_node
|
||
executor_type: MultiThreaded
|
||
executor_thread_num: 4
|
||
```
|
||
|
||
|
||
## ros2 类型 RPC 后端
|
||
|
||
|
||
`ros2`类型的 RPC 后端是**ros2_plugin**中提供的一种 RPC 后端,用于通过 ROS2 RPC 的方式来调用和处理 AimRT RPC 请求。其所有的配置项如下:
|
||
|
||
| 节点 | 类型 | 是否可选 | 默认值 | 作用 |
|
||
| ------------------------------------------------ | ------ | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| timeout_executor | string | 可选 | "" | Client 端 RPC 超时情况下的执行器 |
|
||
| clients_options | array | 可选 | [] | 客户端发起 RPC 请求时的规则 |
|
||
| clients_options[i].func_name | string | 必选 | "" | RPC Func 名称,支持正则表达式 |
|
||
| clients_options[i].qos | map | 可选 | - | QOS 配置 |
|
||
| clients_options[i].qos.history | string | 可选 | "default" | QOS 的历史记录选项<br/>keep_last:保留最近的记录(缓存最多 N 条记录,可通过队列长度选项来配置)<br/>keep_all:保留所有记录(缓存所有记录,但受限于底层中间件可配置的最大资源)<br/>default:使用系统默认 |
|
||
| clients_options[i].qos.depth | int | 可选 | 10 | QOS 的队列深度选项(只能与 Keep_last 配合使用) |
|
||
| clients_options[i].qos.reliability | string | 可选 | "default" | QOS 的可靠性选项<br/>reliable:可靠的(消息丢失时,会重新发送,反复重传以保证数据传输成功)<br/>best_effort:尽力而为的(尝试传输数据但不保证成功传输,当网络不稳定时可能丢失数据)<br/>default:系统默认 |
|
||
| clients_options[i].qos.durability | string | 可选 | "default" | QOS 的持续性选项<br/>transient_local:局部瞬态(发布器为晚连接(late-joining)的订阅器保留数据)<br/>volatile:易变态(不保留任何数据)<br/>default:系统默认 |
|
||
| clients_options[i].qos.deadline | int | 可选 | -1 | QOS 的后续消息发布到主题之间的预期最大时间量选项<br/>需填毫秒级时间间隔,填 -1 为不设置,按照系统默认 |
|
||
| clients_options[i].qos.lifespan | int | 可选 | -1 | QOS 的消息发布和接收之间的最大时间量(单位毫秒)选项<br/>而不将消息视为陈旧或过期(过期的消息被静默地丢弃,并且实际上从未被接收<br/>填-1保持系统默认 不设置 |
|
||
| clients_options[i].qos.liveliness | string | 可选 | "default" | QOS 的如何确定发布者是否活跃选项<br/>automatic:自动(ROS2 会根据消息发布和接收的时间间隔来判断)<br/>manual_by_topic:需要发布者定期声明<br/>default:保持系统默认 |
|
||
| clients_options[i].qos.liveliness_lease_duration | int | 可选 | -1 | QOS 的活跃性租期的时长(单位毫秒)选项,如果超过这个时间发布者没有声明活跃,则被认为是不活跃的<br/>填-1保持系统默认 不设置 |
|
||
| clients_options[i].remapping_rule | string | 可选 | "" | 用于将 clients_options[i].func_name 所正则匹配到的 func_name 按照新则规则进行重映射, 以生成新的 ros2_func_name <br/>在书写的时候支持替换规则: {j} 表示第 j 个被正则匹配的捕获组 |
|
||
| servers_options | array | 可选 | [] | 服务端接收处理 RPC 请求时的规则 |
|
||
| servers_options[i].func_name | string | 必选 | "" | RPC Func 名称,支持正则表达式 |
|
||
| servers_options[i].qos | map | 可选 | - | QOS 配置 |
|
||
| servers_options[i].qos.history | string | 可选 | "default" | QOS 的历史记录选项<br/>keep_last:保留最近的记录(缓存最多 N 条记录,可通过队列长度选项来配置)<br/>keep_all:保留所有记录(缓存所有记录,但受限于底层中间件可配置的最大资源)<br/>default:使用系统默认 |
|
||
| servers_options[i].qos.depth | int | 可选 | 10 | QOS 的队列深度选项(只能与 Keep_last 配合使用) |
|
||
| servers_options[i].qos.reliability | string | 可选 | "default" | QOS 的可靠性选项<br/>reliable:可靠的(消息丢失时,会重新发送,反复重传以保证数据传输成功)<br/>best_effort:尽力而为的(尝试传输数据但不保证成功传输,当网络不稳定时可能丢失数据)<br/>default:系统默认 |
|
||
| servers_options[i].qos.durability | string | 可选 | "default" | QOS 的持续性选项<br/>transient_local:局部瞬态(发布器为晚连接(late-joining)的订阅器保留数据)<br/>volatile:易变态(不保留任何数据)<br/>default:系统默认 |
|
||
| servers_options[i].qos.deadline | int | 可选 | -1 | QOS 的后续消息发布到主题之间的预期最大时间量选项<br/>需填毫秒级时间间隔,填 -1 为不设置,按照系统默认 |
|
||
| servers_options[i].qos.lifespan | int | 可选 | -1 | QOS 的消息发布和接收之间的最大时间量(单位毫秒)选项<br/>而不将消息视为陈旧或过期(过期的消息被静默地丢弃,并且实际上从未被接收<br/>填-1保持系统默认 不设置 |
|
||
| servers_options[i].qos.liveliness | string | 可选 | "default" | QOS 的如何确定发布者是否活跃选项<br/>automatic:自动(ROS2 会根据消息发布和接收的时间间隔来判断)<br/>manual_by_topic:需要发布者定期声明<br/>default:保持系统默认 |
|
||
| servers_options[i].qos.liveliness_lease_duration | int | 可选 | -1 | QOS 的活跃性租期的时长(单位毫秒)选项,如果超过这个时间发布者没有声明活跃,则被认为是不活跃的<br/>填 -1 保持系统默认 不设置 |
|
||
| servers_options[i].remapping_rule | string | 可选 | "" | 用于将 servers_options[i].func_name 所正则匹配到的 func_name 按照新则规则进行重映射, 以生成新的 ros2_func_name <br/>在书写的时候支持替换规则: {j} 表示第 j 个被正则匹配的捕获组 |
|
||
|
||
|
||
下面是一个 remap 用法的简单示例:名为 `pb:/aimrt_server/GetFooData` 的 AimRT func_name ,若不需要 remap,则 最终生成的 ros func_name 就是 `/aimrt_5Fserver/GetFooData` (可以看到把 ":" 及之前的 <msg_type> 去掉并且将非数字、字母和'/'的符号的 ascii 码以 HEX 编码,加上 '_' 作为前缀)。 若需要 remap,则可以配置如下:
|
||
|
||
```yaml
|
||
rpc:
|
||
backends:
|
||
- type: ros2
|
||
options:
|
||
servers_options:
|
||
- func_name: "(.*)/(.*)/(.*)" #这里是填写匹配 AimRT func_name 的正则表达式
|
||
remapping_rule: "{1}/{2}" # 这里填写重映射规则,用于生成新的ros2_func_name。 这里也可以简化写成 /{2}
|
||
|
||
```
|
||
经过该配置,第一个`(.*)`捕获到了 `pb:`, 第二个`(.*)`捕获到了 `aimrt_server`, 第三个`(.*)`捕获到了 `GetFooData`, 最后生成的 ros func_name 就是 `/GetFooData` , 为了简化书写,在选项中的`remapping_rule`可以不填写`{1}`所代表的消息类型,系统会自动生成以适配AimRT func_name 和 ros2 func_name 的转换关系。以下是一些快速的例子,用于演示更丰富的用法,假设 AimRT func_name 为 `pb:/aaa/bbb/ccc` :
|
||
|
||
| func_name | remapping_rule | ros2_func_name |
|
||
| ----------------------- | --------------- | -------------- |
|
||
| (.\*)/(.\*)/(.\*)/(.\*) | | /aaa/bbb/ccc |
|
||
| (.\*)/(.\*)/(.\*)/(.\*) | {1}/{2}/ddd/{4} | /aaa/ddd/ccc |
|
||
| (.\*)/(.\*)/(.\*)/(.\*) | /{2}/ddd/{4} | /aaa/ddd/ccc |
|
||
| (.\*)/(.\*)/(bbb)/(.\*) | {1}/{3}_{4} | /bbb_5Fccc |
|
||
| (.\*)/(.\*)/(bbb)/(.\*) | /{3}/eee | /bbb/eee |
|
||
|
||
|
||
以下是一个简单的客户端的示例:
|
||
```yaml
|
||
aimrt:
|
||
plugin:
|
||
plugins:
|
||
- name: ros2_plugin
|
||
path: ./libaimrt_ros2_plugin.so
|
||
options:
|
||
node_name: example_ros2_client_node
|
||
executor_type: MultiThreaded
|
||
executor_thread_num: 4
|
||
rpc:
|
||
backends:
|
||
- type: ros2
|
||
options:
|
||
clients_options:
|
||
- func_name: "(.*)"
|
||
qos:
|
||
history: keep_last
|
||
depth: 10
|
||
reliability: reliable
|
||
durability: volatile
|
||
deadline: -1
|
||
lifespan: -1
|
||
liveliness: automatic
|
||
liveliness_lease_duration: -1
|
||
clients_options:
|
||
- func_name: "(.*)"
|
||
enable_backends: [ros2]
|
||
```
|
||
|
||
以下则是一个简单的服务端的示例:
|
||
```yaml
|
||
aimrt:
|
||
plugin:
|
||
plugins:
|
||
- name: ros2_plugin
|
||
path: ./libaimrt_ros2_plugin.so
|
||
options:
|
||
node_name: example_ros2_server_node
|
||
executor_type: MultiThreaded
|
||
executor_thread_num: 4
|
||
rpc:
|
||
backends:
|
||
- type: ros2
|
||
options:
|
||
servers_options:
|
||
- func_name: "(.*)"
|
||
qos:
|
||
history: keep_last
|
||
depth: 10
|
||
reliability: reliable
|
||
durability: volatile
|
||
deadline: -1
|
||
lifespan: -1
|
||
liveliness: automatic
|
||
liveliness_lease_duration: -1
|
||
servers_options:
|
||
- func_name: "(.*)"
|
||
enable_backends: [ros2]
|
||
```
|
||
|
||
|
||
以上示例中,Server 端启动了一个 ROS2 节点`example_ros2_server_node`,Client 端则启动了一个 ROS2 节点`example_ros2_client_node`,Client 端通过 ROS2 的后端发起 RPC 调用,Server 端通过 ROS2 后端接收到 RPC 请求并进行处理。
|
||
|
||
Client 端向 Server 端发起调用时,如果协议层是原生 ROS2 协议,那么通信时将完全复用 ROS2 的原生协议,原生 ROS2 节点可以基于该协议无缝与 AimRT 节点对接。
|
||
|
||
如果 Client 端向 Server 端发起调用时,协议层没有使用 ROS2 协议,那么通信时将基于{{ '[RosRpcWrapper.srv]({}/src/protocols/plugins/ros2_plugin_proto/srv/RosRpcWrapper.srv)'.format(code_site_root_path_url) }}这个 ROS2 协议进行包装,该协议内容如下:
|
||
```
|
||
string serialization_type
|
||
string[] context
|
||
byte[] data
|
||
---
|
||
int64 code
|
||
string serialization_type
|
||
byte[] data
|
||
```
|
||
|
||
如果此时原生的 ROS2 节点需要和 AimRT 的节点对接,原生 ROS2 节点的开发者需要搭配此协议 Req/Rsp 的 data 字段来序列化/反序列化真正的请求包/回包。
|
||
|
||
此外,由于 ROS2 对`service_name`有一些特殊要求,AimRT 与ROS2 互通时的`service_name`由 AimRT Func 根据一个类似于 URL 编码的规则来生成:
|
||
- 将所有除数字、字母和'/'的符号的 ascii 码以 HEX 编码,加上 '_' 作为前缀。
|
||
|
||
例如,如果 AimRT Func 名称为`/aaa.bbb.ccc/ddd`,则编码后的 ROS2 service name 就是`/aaa_2Ebbb_2Eccc/ddd`。具体值也会在 ros2_plugin 启动时打印出来。
|
||
|
||
基于以上特性,`ros2`类型的 RPC 后端可以用于打通与原生 ROS2 节点的 RPC 链路,从而实现 AimRT 对 ROS2 的兼容。
|
||
|
||
|
||
## ros2 类型 Channel 后端
|
||
|
||
`ros2`类型的 Channel 后端是**ros2_plugin**中提供的一种 Channel 后端,用于通过 ROS2 Topic 的方式来发布和订阅 AimRT Channel 消息。其所有的配置如下:
|
||
|
||
| 节点 | 类型 | 是否可选 | 默认值 | 作用 |
|
||
| --------------------------------------------------- | ------ | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| pub_topics_options | array | 可选 | [] | 发布 Topic 时的规则 |
|
||
| pub_topics_options[i].topic_name | string | 必选 | "" | Topic 名称,支持正则表达式 |
|
||
| pub_topics_options[i].qos | map | 可选 | - | QOS 配置 |
|
||
| pub_topics_options[i].qos.history | string | 可选 | "default" | QOS 的历史记录选项<br/>keep_last:保留最近的记录(缓存最多 N 条记录,可通过队列长度选项来配置)<br/>keep_all:保留所有记录(缓存所有记录,但受限于底层中间件可配置的最大资源)<br/>default:使用系统默认 |
|
||
| pub_topics_options[i].qos.depth | int | 可选 | 10 | QOS 的队列深度选项(只能与Keep_last配合使用) |
|
||
| pub_topics_options[i].qos.reliability | string | 可选 | "default" | QOS 的可靠性选项<br/>reliable:可靠的(消息丢失时,会重新发送,反复重传以保证数据传输成功)<br/>best_effort:尽力而为的(尝试传输数据但不保证成功传输,当网络不稳定时可能丢失数据)<br/>default:系统默认 |
|
||
| pub_topics_options[i].qos.durability | string | 可选 | "default" | QOS 的持续性选项<br/>transient_local:局部瞬态(发布器为晚连接(late-joining)的订阅器保留数据)<br/>volatile:易变态(不保留任何数据)<br/>default:系统默认 |
|
||
| pub_topics_options[i].qos.deadline | int | 可选 | -1 | QOS 的后续消息发布到主题之间的预期最大时间量选项<br/>需填毫秒级时间间隔,填 -1 为不设置,按照系统默认 |
|
||
| pub_topics_options[i].qos.lifespan | int | 可选 | -1 | QOS 的消息发布和接收之间的最大时间量(单位毫秒)选项<br/>而不将消息视为陈旧或过期(过期的消息被静默地丢弃,并且实际上从未被接收<br/>填-1保持系统默认 不设置 |
|
||
| pub_topics_options[i].qos.liveliness | string | 可选 | "default" | QOS 的如何确定发布者是否活跃选项<br/>automatic:自动(ROS2 会根据消息发布和接收的时间间隔来判断)<br/>manual_by_topic:需要发布者定期声明<br/>default:保持系统默认 |
|
||
| pub_topics_options[i].qos.liveliness_lease_duration | int | 可选 | -1 | QOS 的活跃性租期的时长(单位毫秒)选项,如果超过这个时间发布者没有声明活跃,则被认为是不活跃的<br/>填-1保持系统默认 不设置 |
|
||
| sub_topics_options | array | 可选 | [] | 订阅 Topic 时的规则 |
|
||
| sub_topics_options[i].topic_name | string | 必选 | "" | Topic 名称,支持正则表达式 |
|
||
| sub_topics_options[i].qos | map | 可选 | - | QOS 配置 |
|
||
| sub_topics_options[i].qos.history | string | 可选 | "default" | QOS 的历史记录选项<br/>keep_last:保留最近的记录(缓存最多 N 条记录,可通过队列长度选项来配置)<br/>keep_all:保留所有记录(缓存所有记录,但受限于底层中间件可配置的最大资源)<br/>default:使用系统默认 |
|
||
| sub_topics_options[i].qos.depth | int | 可选 | 10 | QOS 的队列深度选项(只能与Keep_last配合使用) |
|
||
| sub_topics_options[i].qos.reliability | string | 可选 | "default" | QOS 的可靠性选项<br/>reliable:可靠的(消息丢失时,会重新发送,反复重传以保证数据传输成功)<br/>best_effort:尽力而为的(尝试传输数据但不保证成功传输,当网络不稳定时可能丢失数据)<br/>default:系统默认 |
|
||
| sub_topics_options[i].qos.durability | string | 可选 | "default" | QOS 的持续性选项<br/>transient_local:局部瞬态(发布器为晚连接(late-joining)的订阅器保留数据)<br/>volatile:易变态(不保留任何数据)<br/>default:系统默认 |
|
||
| sub_topics_options[i].qos.deadline | int | 可选 | -1 | QOS 的后续消息发布到主题之间的预期最大时间量选项<br/>需填毫秒级时间间隔,填 -1 为不设置,按照系统默认 |
|
||
| sub_topics_options[i].qos.lifespan | int | 可选 | -1 | QOS 的消息发布和接收之间的最大时间量(单位毫秒)选项<br/>而不将消息视为陈旧或过期(过期的消息被静默地丢弃,并且实际上从未被接收<br/>填-1保持系统默认 不设置 |
|
||
| sub_topics_options[i].qos.liveliness | string | 可选 | "default" | QOS 的如何确定发布者是否活跃选项<br/>automatic:自动(ROS2 会根据消息发布和接收的时间间隔来判断)<br/>manual_by_topic:需要发布者定期声明<br/>default:保持系统默认 |
|
||
| sub_topics_options[i].qos.liveliness_lease_duration | int | 可选 | -1 | QOS 的活跃性租期的时长(单位毫秒)选项,如果超过这个时间发布者没有声明活跃,则被认为是不活跃的<br/>填 -1 保持系统默认 不设置 |
|
||
|
||
|
||
以下是一个简单的发布端的示例:
|
||
```yaml
|
||
aimrt:
|
||
plugin:
|
||
plugins:
|
||
- name: ros2_plugin
|
||
path: ./libaimrt_ros2_plugin.so
|
||
options:
|
||
node_name: example_ros2_pub_node
|
||
executor_type: MultiThreaded
|
||
executor_thread_num: 4
|
||
channel:
|
||
backends:
|
||
- type: ros2
|
||
options:
|
||
pub_topics_options:
|
||
- topic_name: "(.*)"
|
||
qos:
|
||
history: keep_last
|
||
depth: 10
|
||
reliability: reliable
|
||
durability: volatile
|
||
deadline: -1
|
||
lifespan: -1
|
||
liveliness: automatic
|
||
liveliness_lease_duration: -1
|
||
pub_topics_options:
|
||
- topic_name: "(.*)"
|
||
enable_backends: [ros2]
|
||
```
|
||
|
||
以下则是一个简单的订阅端的示例:
|
||
```yaml
|
||
aimrt:
|
||
plugin:
|
||
plugins:
|
||
- name: ros2_plugin
|
||
path: ./libaimrt_ros2_plugin.so
|
||
options:
|
||
node_name: example_ros2_sub_node
|
||
executor_type: MultiThreaded
|
||
executor_thread_num: 4
|
||
channel:
|
||
backends:
|
||
- type: ros2
|
||
options:
|
||
sub_topics_options:
|
||
- topic_name: "(.*)"
|
||
qos:
|
||
history: keep_last
|
||
depth: 10
|
||
reliability: reliable
|
||
durability: volatile
|
||
deadline: -1
|
||
lifespan: -1
|
||
liveliness: automatic
|
||
liveliness_lease_duration: -1
|
||
sub_topics_options:
|
||
- topic_name: "(.*)"
|
||
enable_backends: [ros2]
|
||
```
|
||
|
||
以上示例中,发布端启动了一个 ROS2 节点`example_ros2_pub_node`,订阅端则启动了一个 ROS2 节点`example_ros2_sub_node`,发布端通过 ROS2 的后端发布消息,订阅端通过 ROS2 后端接收到消息并进行处理。
|
||
|
||
如果消息发布订阅时,协议层是原生 ROS2 协议,那么通信时将完全复用 ROS2 的原生协议,原生 ROS2 节点可以基于该协议无缝与 AimRT 节点对接。
|
||
|
||
如果消息发布订阅时,协议层没有使用 ROS2 协议,那么通信时将基于{{ '[RosMsgWrapper.msg]({}/src/protocols/plugins/ros2_plugin_proto/msg/RosMsgWrapper.msg)'.format(code_site_root_path_url) }}这个 ROS2 协议进行包装,该协议内容如下:
|
||
```
|
||
string serialization_type
|
||
string[] context
|
||
byte[] data
|
||
```
|
||
|
||
如果此时原生的 ROS2 节点需要和 AimRT 的节点对接,原生 ROS2 节点的开发者需要搭配此协议的 data 字段来序列化/反序列化真正的消息。
|
||
|
||
此外,AimRT 与 ROS2 互通时的`Topic`名称由以下规则生成:`${aimrt_topic}/${ros2_encode_aimrt_msg_type}`。其中`${aimrt_topic}`是 AimRT Topic 名称,`${ros2_encode_aimrt_msg_type}`根据 AimRT Msg 名称由一个类似于 URL 编码的规则来生成:
|
||
- 将所有除数字、字母和'/'的符号的 ascii 码以 HEX 编码,加上 '_' 作为前缀。
|
||
|
||
例如,如果 AimRT Topic 名称是`test_topic`,AimRT Msg 名称为`pb:aaa.bbb.ccc`,则最终 ROS2 Topic 值就是`test_topic/pb_3Aaaa_2Ebbb_2Eccc`。具体值也会在 ros2_plugin 启动时打印出来。
|
||
|
||
基于这个特性,`ros2`类型的 Channel 后端可以用于打通与原生 ROS2 节点的 Channel 链路,从而实现 AimRT 对 ROS2 的兼容。
|
||
|
||
|
||
开发者还可以参考{{ '[ros2_plugin]({}/src/examples/plugins/ros2_plugin)'.format(code_site_root_path_url) }}中的示例,与原生 ros2 humble 节点进行通信。
|