ROS2 Plugin
Plugin Overview
ros2_plugin is a communication transport plugin implemented based on ROS2 Humble. This plugin provides the following components:
ros2
type RPC backendros2
type Channel backend
The plugin configuration items are as follows:
Node |
Type |
Optional |
Default Value |
Description |
---|---|---|---|---|
node_name |
string |
Required |
“” |
ROS2 node name |
executor_type |
string |
Optional |
“MultiThreaded” |
ROS2 executor type, options: “SingleThreaded”, “StaticSingleThreaded”, “MultiThreaded” |
executor_thread_num |
int |
Optional |
2 |
When executor_type == “MultiThreaded”, indicates the thread count of ROS2 executor |
auto_initialize_logging |
bool |
Optional |
true |
Whether to initialize ROS2’s default SPLOG logging system |
Regarding the configuration of ros2_plugin, the following points should be noted:
node_name
represents the ROS2 node name. From an external perspective, an AimRT node loaded with the ROS2 plugin appears as a ROS2 node, and its node name is configured through this item.executor_type
indicates the type of ROS2 node executor. Currently, there are three options:SingleThreaded
,StaticSingleThreaded
, andMultiThreaded
. For specific meanings, please refer to the ROS2 Humble documentation.executor_thread_num
only takes effect whenexecutor_type
is set toMultiThreaded
, indicating the thread count of ROS2.auto_initialize_logging
determines whether to initialize ROS2’s default SPLOG logging system. If set totrue
, ROS2’s default SPLOG logging system will be used, and related logs will be stored in the directory specified by the environment variable ROS_LOG_DIR.
Additionally, when using ros2_plugin, the executor provided by ros2_plugin is used for Channel subscription callbacks, RPC Server processing, and RPC Client returns. If the callback blocks the thread, it may exhaust the ros2_plugin thread pool, preventing further message reception/sending. As mentioned in the Module interface documentation, generally, if the task in the callback is very lightweight, it can be processed directly in the callback. However, if the task is relatively heavy, it is better to schedule it to another dedicated task executor for processing.
Here is a simple example:
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 Type RPC Backend
The ros2
type RPC backend is an RPC backend provided by ros2_plugin, used to invoke and process AimRT RPC requests through the ROS2 RPC approach. All its configuration items are as follows:
Node |
Type |
Optional |
Default |
Description |
---|---|---|---|---|
timeout_executor |
string |
Optional |
“” |
Executor for RPC timeout scenarios on the Client side |
clients_options |
array |
Optional |
[] |
Rules for the client when initiating RPC requests |
clients_options[i].func_name |
string |
Required |
“” |
RPC Func name, supports regular expressions |
clients_options[i].qos |
map |
Optional |
- |
QOS configuration |
clients_options[i].qos.history |
string |
Optional |
“default” |
QOS history option |
clients_options[i].qos.depth |
int |
Optional |
10 |
QOS queue depth option (can only be used with Keep_last) |
clients_options[i].qos.reliability |
string |
Optional |
“default” |
QOS reliability option |
clients_options[i].qos.durability |
string |
Optional |
“default” |
QOS durability option |
clients_options[i].qos.deadline |
int |
Optional |
-1 |
QOS expected maximum time interval (in milliseconds) between subsequent message publications to the topic |
clients_options[i].qos.lifespan |
int |
Optional |
-1 |
QOS maximum time interval (in milliseconds) between message publication and reception |
clients_options[i].qos.liveliness |
string |
Optional |
“default” |
QOS option for determining publisher liveliness |
clients_options[i].qos.liveliness_lease_duration |
int |
Optional |
-1 |
QOS liveliness lease duration option (in milliseconds). If the publisher does not declare liveliness within this duration, it is considered inactive |
clients_options[i].remapping_rule |
string |
Optional |
“” |
Used to remap the func_name matched by the regular expression in clients_options[i].func_name according to new rules to generate a new ros2_func_name |
servers_options[i].func_name |
string |
Required |
“” |
RPC Func name, supports regular expressions |
servers_options[i].qos |
map |
Optional |
- |
QOS configuration |
servers_options[i].qos.history |
string |
Optional |
“default” |
QOS history options |
servers_options[i].qos.depth |
int |
Optional |
10 |
QOS queue depth option (only works with Keep_last) |
servers_options[i].qos.reliability |
string |
Optional |
“default” |
QOS reliability options |
servers_options[i].qos.durability |
string |
Optional |
“default” |
QOS durability options |
servers_options[i].qos.deadline |
int |
Optional |
-1 |
QOS maximum expected time between successive messages on a topic (in milliseconds) |
servers_options[i].qos.lifespan |
int |
Optional |
-1 |
QOS maximum duration between message publication and acceptance (in milliseconds) |
servers_options[i].qos.liveliness |
string |
Optional |
“default” |
QOS publisher liveliness determination method |
servers_options[i].qos.liveliness_lease_duration |
int |
Optional |
-1 |
QOS liveliness lease duration (in milliseconds) |
servers_options[i].remapping_rule |
string |
Optional |
“” |
Used to remap the func_name matched by servers_options[i].func_name regex to generate a new ros2_func_name |
Here’s a simple example of remap usage: For an AimRT func_name pb:/aimrt_server/GetFooData
, if no remap is needed, the final generated ros func_name would be /aimrt_5Fserver/GetFooData
(note that the “<msg_type>” before “:” is removed, and non-alphanumeric/‘/’ characters are HEX encoded with ‘_’ prefix). If remapping is needed, configure as follows:
rpc:
backends:
- type: ros2
options:
servers_options:
- func_name: "(.*)/(.*)/(.*)" #这里是填写匹配 AimRT func_name 的正则表达式
remapping_rule: "{1}/{2}" # 这里填写重映射规则,用于生成新的ros2_func_name。 这里也可以简化写成 /{2}
With this configuration, the first (.*)
captures pb:
, the second (.*)
captures aimrt_server
, and the third (.*)
captures GetFooData
. The final generated ros func_name becomes /GetFooData
. To simplify writing, the remapping_rule
option can omit the message type represented by {1}
, as the system will automatically generate it to adapt the conversion between AimRT func_name and ros2 func_name. Below are some quick examples demonstrating richer usage, assuming the AimRT func_name is 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)/(.*) |
/{3}/eee |
/bbb/eee |
Here is a simple client example:
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]
Here is a simple server example:
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]
In the above examples, the Server starts a ROS2 node example_ros2_server_node
, while the Client starts a ROS2 node example_ros2_client_node
. The Client initiates RPC calls through the ROS2 backend, and the Server receives and processes the RPC requests via the ROS2 backend.
When the Client initiates a call to the Server, if the protocol layer uses the native ROS2 protocol, the communication will fully reuse ROS2’s native protocol. Native ROS2 nodes can seamlessly interface with AimRT nodes based on this protocol.
If the Client initiates a call to the Server without using the ROS2 protocol, the communication will be wrapped based on the ROS2 protocol RosRpcWrapper.srv, whose content is as follows:
string serialization_type
string[] context
byte[] data
---
int64 code
string serialization_type
byte[] data
If native ROS2 nodes need to interface with AimRT nodes in this case, developers of native ROS2 nodes must use the Req/Rsp data
field of this protocol to serialize/deserialize the actual request/response packets.
Additionally, since ROS2 has specific requirements for service_name
, the service_name
for AimRT-ROS2 interoperability is generated by AimRT Func following a URL-like encoding rule:
Encode the ASCII codes of all symbols except numbers, letters, and ‘/’ in HEX format, prefixed with ‘_’.
For example, if the AimRT Func name is /aaa.bbb.ccc/ddd
, the encoded ROS2 service name becomes /aaa_2Ebbb_2Eccc/ddd
. The specific value will also be printed when the ros2_plugin starts.
Based on these features, the ros2
-type RPC backend can be used to establish RPC links with native ROS2 nodes, enabling AimRT’s compatibility with ROS2.## ros2 Type Channel Backend
The ros2
type Channel backend is a Channel backend provided in the ros2_plugin, used to publish and subscribe AimRT Channel messages via ROS2 Topics. All its configurations are as follows:
Node |
Type |
Optional |
Default |
Description |
---|---|---|---|---|
pub_topics_options |
array |
Optional |
[] |
Rules for publishing Topics |
pub_topics_options[i].topic_name |
string |
Required |
“” |
Topic name, supports regular expressions |
pub_topics_options[i].qos |
map |
Optional |
- |
QOS configuration |
pub_topics_options[i].qos.history |
string |
Optional |
“default” |
QOS history option |
pub_topics_options[i].qos.depth |
int |
Optional |
10 |
QOS queue depth option (can only be used with Keep_last) |
pub_topics_options[i].qos.reliability |
string |
Optional |
“default” |
QOS reliability option |
pub_topics_options[i].qos.durability |
string |
Optional |
“default” |
QOS durability option |
pub_topics_options[i].qos.deadline |
int |
Optional |
-1 |
QOS expected maximum time interval (in milliseconds) between subsequent message publications to the topic |
pub_topics_options[i].qos.lifespan |
int |
Optional |
-1 |
QOS maximum time interval (in milliseconds) between message publication and reception |
pub_topics_options[i].qos.liveliness |
string |
Optional |
“default” |
QOS option for determining how to check if the publisher is alive |
pub_topics_options[i].qos.liveliness_lease_duration |
int |
Optional |
-1 |
QOS liveliness lease duration (in milliseconds) option. If the publisher does not declare liveliness within this time, it is considered inactive |
sub_topics_options |
array |
Optional |
[] |
Rules for subscribing Topics |
sub_topics_options[i].topic_name |
string |
Required |
“” |
Topic name, supports regular expressions |
sub_topics_options[i].qos.history |
string |
Optional |
“default” |
QOS history option |
sub_topics_options[i].qos.depth |
int |
Optional |
10 |
QOS queue depth option (only works with Keep_last) |
sub_topics_options[i].qos.reliability |
string |
Optional |
“default” |
QOS reliability option |
sub_topics_options[i].qos.durability |
string |
Optional |
“default” |
QOS durability option |
sub_topics_options[i].qos.deadline |
int |
Optional |
-1 |
QOS maximum expected time between message publications on a topic (in milliseconds) |
sub_topics_options[i].qos.lifespan |
int |
Optional |
-1 |
QOS maximum time between message publication and reception (in milliseconds) before considering it stale/expired (expired messages are silently discarded) |
sub_topics_options[i].qos.liveliness |
string |
Optional |
“default” |
QOS option for determining publisher liveliness |
sub_topics_options[i].qos.liveliness_lease_duration |
int |
Optional |
-1 |
QOS liveliness lease duration (in milliseconds). If publisher doesn’t declare liveliness within this period, it’s considered inactive. |
Here is a simple publisher example:
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]
Here is a simple subscriber example:
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]
In the above examples, the publisher starts a ROS2 node example_ros2_pub_node
, while the subscriber starts a ROS2 node example_ros2_sub_node
. The publisher sends messages via ROS2 backend, and the subscriber receives and processes these messages through ROS2 backend.
If the message publishing/subscribing uses native ROS2 protocol, the communication will fully reuse ROS2’s native protocol, allowing seamless integration between native ROS2 nodes and AimRT nodes.If the protocol layer does not use the ROS2 protocol when publishing/subscribing messages, communication will be based on the ROS2 protocol wrapped by RosMsgWrapper.msg, with the following content:
string serialization_type
string[] context
byte[] data
If native ROS2 nodes need to interface with AimRT nodes in this case, developers of the native ROS2 nodes must use the data
field of this protocol to serialize/deserialize the actual messages.
Additionally, the Topic
name for communication between AimRT and ROS2 is generated by the following rule: ${aimrt_topic}/${ros2_encode_aimrt_msg_type}
. Here, ${aimrt_topic}
is the AimRT Topic name, and ${ros2_encode_aimrt_msg_type}
is generated from the AimRT Msg name using a URL-like encoding rule:
Encode all non-alphanumeric characters (except ‘/’) as HEX values of their ASCII codes, prefixed with ‘_’.
For example, if the AimRT Topic name is test_topic
and the AimRT Msg name is pb:aaa.bbb.ccc
, the resulting ROS2 Topic value will be test_topic/pb_3Aaaa_2Ebbb_2Eccc
. The specific value will also be printed when the ros2_plugin starts.
Based on this feature, the ros2
-type Channel backend can be used to establish a Channel link with native ROS2 nodes, enabling AimRT’s compatibility with ROS2.
Developers can also refer to the example in ros2_plugin to communicate with native ROS2 Humble nodes.