![zhangyi1357](/assets/img/avatar_default.png)
* feat: add checks for ROS 2 message type support Implement validation functions to ensure message or service types are compliant with ROS 2 standards, enhancing compatibility and error handling when integrating ROS 1 and ROS 2 environments. * build: update file globbing to include configure dependencies Ensure source files are automatically detected during configuration by adding CONFIGURE_DEPENDS to the file globbing commands for C++ and Python files. This improves build reliability as it accounts for changes in the file structure. * feat: add ROS2 message support and enhance publishing functionality Implement functions to register, publish, and subscribe to ROS2 messages, improving interoperability with ROS2 systems. Adjust type support to handle both protobuf and ROS2 message types, streamlining the process of message serialization and ensuring robustness in message handling. * feat: add HTTP and ROS2 example configurations and applications Introduce example configurations and applications for both HTTP and ROS2 channels, enabling greater flexibility and showcasing the capabilities of the system for publishing and subscribing to messages. * chore: disable unimplemented copy and move functions Throw runtime errors for the unimplemented copy and move methods to ensure clarity on their current status and prevent accidental usage. * feat: enhance message publishing with iteration and logging Implement continuous publishing of messages with incremental identifiers in the publisher application. Update the subscriber to log message count, improving visibility into received messages. * refactor: reorganize and encapsulate ROS2 message handling functionality Streamline the handling of ROS2 messages by encapsulating related functionality into a new utility header file. This enhances code clarity and maintainability while adhering to best practices in modular design. * refactor: streamline protobuf and ROS2 message publishing Enhance context handling and unify publish type registration for better clarity and usability. Simplify serialization type management and improve the architecture for future extensions, particularly for messaging features. * refactor: streamline messaging functions Enhance message publishing by consolidating function calls and improving clarity. Update context handling to better support message serialization types, allowing for a more flexible interaction with both protobuf and ROS2 messages. * refactor: simplify message subscription handling and reduce publish frequency Adjust the message subscription method to handle both Protobuf and ROS2 message types more efficiently and eliminate redundant subscription functions. Decrease the publishing frequency in the example app for improved performance and readability. * fix: remove unnecessary error message for ROS 1 type detection Eliminate confusion by removing the printed warning about ROS 1 message types, as it was not providing significant value in handling unsupported types. * docs: update README and add ros2 channel examples Clarify publisher behavior in examples and provide comprehensive documentation for ros2 channel implementations. * feat: support ros2 message types in aimrt_py channel Add support for ros2 message types in the aimrt_py channel to enhance interoperability with ROS2-based applications. * fix: improve error handling during ROS2 message serialization and deserialization Enhance robustness by adding exception handling to ensure the process correctly handles serialization and deserialization failures, improving overall stability in handling ROS2 messages. * fix: correct argument validation and formatting Remove unnecessary whitespace and enhance error handling for argument types in the publishing function, ensuring clear type checking and preventing potential runtime errors. * refactor: streamline message conversion logic Simplify the conversion of ROS2 messages to Python objects by moving the conversion logic inline. This eliminates unnecessary function calls and improves performance while maintaining clarity in the subscription method. * feat: add ROS2 support to Python runtime Enable linking and compilation of ROS2 interfaces based on the build configuration. This improves flexibility by allowing the Python runtime to interface with ROS2 when needed, enhancing interoperability and feature set. * feat: enhance message type handling for publishers and subscribers Add utility functions to generate names for ROS2 and protobuf message types, centralizing logic for type name creation. Update the publisher and subscriber to utilize these functions, improving code readability and maintainability while ensuring accurate message type management. * feat: update message types to use RosTestMsg for better compatibility Register new message type RosTestMsg in both publisher and subscriber applications. Update data format for publishing events to improve clarity and consistency in message handling. Ensure proper setup in the shell scripts for localization configuration. * chore: consolidate pybind11 includes Simplify header files by removing redundant pybind11 includes, reducing clutter and improving maintainability. * docs: add ROS2 message support details and example links Enhance the documentation by including information on ROS2 message formats and how to generate corresponding Python code. This provides users with clearer guidance on integrating ROS2 functionality within the project. * chore: update copyright information to reflect new authorship Update copyright year and licensing information across multiple configuration files and application scripts to maintain proper attribution for the AimRT project. * feat: add ROS2 RPC client and server examples with configurations Introduce example applications for ROS2 RPC communication, including client and server scripts, along with necessary configuration files. Implement a build script to set up the protocol buffers correctly, enhancing usability by supporting both HTTP and gRPC backends. * feat: update RPC request and response handling Refactor the RPC client and server implementations to utilize the updated request and response types from the example ROS2 service, improving code clarity and ensuring compatibility with the new service structure. * refactor: simplify RPC response handling Remove unnecessary response object instantiation and streamline the response creation process for improved clarity and efficiency in the RPC invocation logic. * refactor: streamline publisher and subscriber method names Simplify method names related to publishing and subscribing to enhance readability and maintainability. Transition from a prefix-based naming convention to a more concise approach, ensuring consistency across the codebase. * chore: update configuration file paths for gRPC client and server examples Align the configuration file paths in the example scripts to use the gRPC-specific settings, ensuring the correct configuration is utilized for both the client and server. * refactor: rename internal methods for clarity Improve naming conventions for protobuf message type functions, enhancing code readability and maintainability. Update related usages to keep consistent across the runtime and RPC generator. * feat: add service type validation and improve bash scripts Introduce a new bash script for debugging the ROS2 client and enhance existing scripts with proper sourcing. Implement service type validation to ensure the reliability of service registration. This improves the robustness of RPC interactions and simplifies the setup process for developers. * feat: extend RPC response data structure and improve logging Add additional data types to the RosTestRpc response for enhanced functionality. Set logging level to trace for more detailed output during execution. * feat: add support for static and dynamic arrays in RPC service Enhance the RPC service by introducing static and dynamic array types for various data types, enabling more complex data handling in requests and responses. This improves flexibility and usability for clients interacting with the service. * feat: add support for new ROS message types Enhance the RPC server to handle additional ROS message types, including dynamic and static arrays of custom data structures. This improves the server's data handling capabilities and aligns with the updated service specifications. * refactor: improve type mapping and message copy functions Update type mappings for better consistency and clarity. Enhance the readability of message copying functions by formatting multi-line expressions. * refactor: improve message copying and moving functions Enhance the handling of ROS message copying and moving by updating the parameter order and adding tailored move functions for better memory management. This optimizes the performance of message manipulations and ensures cleaner, more maintainable code. * refactor: simplify message type support functions Remove inline implementations and enhance readability of message type support functions, while maintaining functionality and improving code organization. * refactor: streamline ROS message handling and improve introspection support Enhance readability and maintainability by cleaning up function definitions related to ROS message type support. Implement a more robust method for obtaining message members information, addressing potential failure scenarios. Adjust example service implementation for consistency in dynamic data handling. * fix: ensure pointers for ROS message creation and destruction are valid Throw runtime exceptions if the function pointers for creating or destroying ROS messages are null, enhancing error handling and stability during object initialization. * feat: enhance RosTestRpc method signature for better type safety Improve the RosTestRpc method by adding overloads for different argument configurations, ensuring clearer type expectations and reducing potential runtime errors. This change aids in maintaining robust interactions with the ROS2 service framework. * refactor: simplify parameter naming in RPC proxy function Rename the `ctx_ref` parameter to `ctx` in the RPC function signature for cleaner and more concise code. This enhances readability and maintains consistency across the codebase. * fix: restrict static array sizes in RosTestRpc service Limit the size of static array fields to three elements each for better resource management and to prevent potential overflow issues. * feat: enhance python runtime generation for ROS2 services Add support for generating code from ROS2 service definitions, improving interoperability with the aimrt framework. Update build scripts to include new generation scripts and ensure file dependencies are configured correctly. * refactor: update RPC service and client implementations to use ROS2 naming convention Modify scripts to utilize the new aimrt_py-gen-ros2-rpc tool and update service and client references accordingly for consistency and clarity. Remove deprecated code to streamline functionality and improve maintainability. * chore: update .gitignore to exclude generated files Add pattern to ignore *_aimrt_rpc_ros2.py files to keep the repository clean from auto-generated files. * refactor: streamline response structure for RosTestRpc Remove unused fields from RosTestRpc response to simplify the data structure and enhance clarity. Adjust logging level for better control over output verbosity. * chore: update log level to INFO for consistency Adjust the core log level to INFO to ensure a more standard logging output and reduce unnecessary verbosity in the logs. * docs: add README for ros2 rpc examples Provide detailed instructions and explanations for setting up and running Python RPC examples using ros2 protocols with HTTP, gRPC, and native ros2 backends. * feat: enhance aimrt_py channel with rpc support for ros2 message types Add support for ros2 message types in both aimrt_py channel and rpc to improve compatibility and functionality. * docs: add ros2_rpc example to Python interface section Include a new example link to enhance the documentation and provide more resources for users exploring the Python interfaces. * chore: add spacing for improved readability in service proxy class Improve code readability by adding blank lines in the service proxy class definition. * chore: standardize argument names for RPC generator Update argument names in the RPC generation script and implementation for consistency, enhancing readability and usability. * docs: update RPC documentation to include ROS2 Srv support Add examples and instructions for using ROS2 Srv in RPC, enhancing integration options for developers. Adjust existing protobuf references for clarity and consistency. * style: standardize whitespace in RosTestRpc.srv Improve readability by ensuring consistent spacing in the service definition. * build: exclude ROS2 specific files from the build if not using ROS2 Remove ROS2-related source files from the compilation when the project is configured without ROS2 support, helping to streamline the build process and avoid unnecessary dependencies. * style: add missing newline at end of file for consistency * refactor: rename copy function to improve clarity Refactor the function used for copying messages within dynamic arrays to better convey its functionality. This change enhances code readability and maintainability. --------- Co-authored-by: zhangyi <zhangyi@agibot.com>
11 KiB
Channel
相关链接
参考示例:
- {{ 'examples_py_pb_chn_publisher_app.py'.format(code_site_root_path_url) }}
- {{ 'examples_py_pb_chn_subscriber_app.py'.format(code_site_root_path_url) }}
协议
协议用于确定通信各端的消息格式。一般来说,协议都是使用一种与具体的编程语言无关的 IDL ( Interface description language )描述,然后由某种工具转换为各个语言的代码。
Protobuf
Protobuf是一种由 Google 开发的、用于序列化结构化数据的轻量级、高效的数据交换格式,是一种广泛使用的 IDL。
当前版本 AimRT Python 只支持 protobuf 协议。在使用 AimRT Python 发送/订阅消息之前,使用者需要先基于 protobuf 协议生成一些 python 的桩代码。
在使用时,开发者需要先定义一个.proto
文件,在其中定义一个消息结构。例如example.proto
:
syntax = "proto3";
message ExampleMsg {
string msg = 1;
int32 num = 2;
}
然后使用 Protobuf 官方提供的 protoc 工具进行转换,生成 Python 代码,例如:
protoc --python_out=. example.proto
这将生成example_pb2.py
文件,包含了根据定义的消息类型生成的 Python 接口,我们的业务代码中需要 import 此文件。
ROS2 Message
ROS2 Message 是一种用于在 ROS2 中进行通信和数据交换的结构化数据格式。在使用时,开发者需要先定义一个 ROS2 Package,在其中定义一个.msg
文件,比如example.msg
:
int32 num
float32 num2
char data
然后直接通过 ROS2 提供的 CMake 方法rosidl_generate_interfaces
,为消息生成 C++ 代码和 CMake Target,例如:
rosidl_generate_interfaces(
example_ros2_msg_gencode
"msg/example.msg"
)
构建之后,还需要设置相应的环境变量,才能在 python 中使用生成的消息类型,在 aimrt 的 build 目录中,执行:
source install/share/example_ros2/setup.bash
关于更多生成自定义 ROS2 Message 的详细信息,请参考ROS2 官方文档。
ChannelHandle
模块可以通过调用CoreRef
句柄的GetChannelHandle()
接口,获取ChannelHandleRef
句柄,来使用 Channel 功能。其提供的核心接口如下:
GetPublisher(str)->PublisherRef
GetSubscriber(str)->SubscriberRef
开发者可以调用ChannelHandleRef
中的GetPublisher
方法和GetSubscriber
方法,获取指定 Topic 名称的PublisherRef
和SubscriberRef
类型句柄,分别用于 Channel 发布和订阅。这两个方法使用注意如下:
- 这两个接口是线程安全的。
- 这两个接口可以在
Initialize
阶段和Start
阶段使用。
Publish
用户如果需要发布一个 Msg,牵涉的接口主要有以下两个:
aimrt_py.RegisterPublishType(publisher, msg_type)->bool
: 用于注册此消息类型;- 第一个参数
publisher
是一个PublisherRef
句柄,代表某个 Topic; - 第二个参数
msg_type
是一个Protobuf
类型; - 返回值是一个 bool 值,表示注册是否成功;
- 第一个参数
aimrt_py.Publish(publisher, msg, ctx | serialization_type)
: 用于发布消息;- 第一个参数
publisher
是一个PublisherRef
句柄,代表某个 Topic; - 第二个参数
msg
是一个Protobuf
类型实例,需要与注册时的消息类型对应; - 第三个参数可以传入
Context
类型实例或者ContextRef
句柄或者serialization_type
字符串,用于指定消息的上下文或者序列化类型,serialization_type
仅可指定pb
或json
,ctx
和serialization_type
均可为空,当为空时,默认使用 pb 序列化; - 该函数同时有以下重载:
aimrt_py.Publish(publisher, ctx | serialization_type, msg)
- 第一个参数
用户需要两个步骤来实现逻辑层面的消息发布:
- Step 1:使用
aimrt_py.RegisterPublishType
方法注册协议类型;- 只能在
Initialize
阶段注册; - 不允许在一个
PublisherRef
中重复注册同一种类型; - 如果注册失败,会返回 false;
- 只能在
- Step 2:使用
aimrt_py.Publish
方法发布数据;- 只能在
Start
阶段之后发布数据; - 在调用
Publish
接口时,开发者应保证传入的 Msg 在Publish
接口返回之前都不会发生变化,否则行为是未定义的;
- 只能在
用户Publish
一个消息后,特定的 Channel 后端将处理具体的消息发布请求。此时根据不同后端的实现,有可能会阻塞一段时间,因此Publish
方法耗费的时间是未定义的。但一般来说,Channel 后端都不会阻塞Publish
方法太久,详细信息请参考对应后端的文档。
Subscribe
用户如果需要订阅一个 Msg,需要使用以下接口:
aimrt_py.Subscribe(subscriber, msg_type, handle)->bool
: 用于订阅一种消息;- 第一个参数
subscriber
是一个SubscriberRef
句柄,代表某个 Topic; - 第二个参数
msg_type
是一个Protobuf
类型; - 第三个参数
handle
是一个签名为(msg)->void
或者(ctx_ref, msg)->void
的消息处理回调,msg
类型是订阅时传入的msg_type
类型,ctx_ref
是消息的上下文句柄; - 返回值是一个 bool 值,表示注册是否成功;
- 第一个参数
注意:
- 只能在
Initialize
调用订阅接口; - 不允许在一个
SubscriberRef
中重复订阅同一种类型; - 如果订阅失败,会返回 false;
此外还需要注意的是,由哪个执行器来执行订阅的回调,这和具体的 Channel 后端实现有关,在运行阶段通过配置才能确定,使用者在编写逻辑代码时不应有任何假设。详细信息请参考对应后端的文档。
最佳实践是:如果回调中的任务非常轻量,比如只是设置一个变量,那就可以直接在回调里处理;但如果回调中的任务比较重,那最好调度到其他专门执行任务的执行器里进行处理。
Context
Context
是 AimRT 中用于传递上下文信息的数据结构,其所支持的接口如下:
Reset()->void
: 重置上下文,重置后上下文可以被再次使用;CheckUsed()->bool
: 检查上下文是否被使用;SetUsed()->void
: 设置上下文为已使用;GetType()->aimrt_channel_context_type_t
: 获取上下文类型;SetMetaValue(key: str, value: str)->void
: 设置元数据;GetMetaValue(key: str)->str
: 获取元数据;GetMetaKeys()->List[str]
: 获取所有元数据键值对中的键列表;SetSerializationType(serialization_type: str)->void
: 设置序列化类型;GetSerializationType()->str
: 获取序列化类型;ToString()->str
: 获取上下文信息,以字符串形式返回可读性高的信息;
ContextRef
是Context
的引用类型,除不具备Reset
接口外,其他接口与Context
完全相同。
aimrt_channel_context_type_t
是一个枚举类型,定义了上下文类型,具体值为AIMRT_CHANNEL_PUBLISHER_CONTEXT
或AIMRT_CHANNEL_SUBSCRIBER_CONTEXT
,表明这是发布者还是订阅者的上下文。
使用示例
以下是一个使用 AimRT Python 进行 Publish 的示例,通过 Create Module 方式拿到CoreRef
句柄。如果是基于Module
模式在Initialize
方法中拿到CoreRef
句柄,使用方式也类似:
import aimrt_py
import threading
from google.protobuf.json_format import MessageToJson
import event_pb2
def main():
aimrt_core = aimrt_py.Core()
# Initialize
core_options = aimrt_py.CoreOptions()
core_options.cfg_file_path = "path/to/cfg/xxx_cfg.yaml"
aimrt_core.Initialize(core_options)
# Create Module
module_handle = aimrt_core.CreateModule("NormalPublisherPyModule")
# Register publish type
topic_name = "test_topic"
publisher = module_handle.GetChannelHandle().GetPublisher(topic_name)
assert publisher, f"Get publisher for topic '{topic_name}' failed."
aimrt_py.RegisterPublishType(publisher, event_pb2.ExampleEventMsg)
# Start
thread = threading.Thread(target=aimrt_core.Start)
thread.start()
# Sleep for seconds
time.sleep(1)
# Publish event
event_msg = event_pb2.ExampleEventMsg()
event_msg.msg = "Publish without ctx or serialization_type"
event_msg.num = 1
aimrt_py.Publish(publisher, event_msg)
aimrt_py.info(module_handle.GetLogger(),
f"Publish new pb event, data: {MessageToJson(event_msg)}")
# Publish event with json serialization
event_msg.msg = "Publish with json serialization"
event_msg.num = 2
aimrt_py.Publish(publisher, "json", event_msg)
aimrt_py.info(module_handle.GetLogger(),
f"Publish new pb event, data: {MessageToJson(event_msg)}")
# Publish event with context
ctx = aimrt_py.Context()
ctx.SetMetaValue("key1", "value1")
event_msg.msg = "Publish with context"
event_msg.num = 3
aimrt_py.Publish(publisher, ctx, event_msg)
aimrt_py.info(module_handle.GetLogger(),
f"Publish new pb event, data: {MessageToJson(event_msg)}")
# Publish event with context ref
ctx.Reset() # Reset context, then it can be used again
ctx_ref = aimrt_py.ContextRef(ctx)
ctx_ref.SetMetaValue("key2", "value2")
ctx_ref.SetSerializationType("json")
event_msg.msg = "Publish with context ref"
event_msg.num = 4
aimrt_py.Publish(publisher, ctx_ref, event_msg)
aimrt_py.info(module_handle.GetLogger(),
f"Publish new pb event, data: {MessageToJson(event_msg)}")
# Sleep for seconds
time.sleep(1)
# Shutdown
aimrt_core.Shutdown()
thread.join()
if __name__ == '__main__':
main()
以下是一个使用 AimRT Python 进行 Subscribe 的示例,通过 Create Module 方式拿到CoreRef
句柄。如果是基于Module
模式在Initialize
方法中拿到CoreRef
句柄,使用方式也类似:
import aimrt_py
import threading
from google.protobuf.json_format import MessageToJson
import event_pb2
global_aimrt_core = None
def signal_handler(sig, frame):
global global_aimrt_core
if (global_aimrt_core and (sig == signal.SIGINT or sig == signal.SIGTERM)):
global_aimrt_core.Shutdown()
return
sys.exit(0)
def main():
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
aimrt_core = aimrt_py.Core()
global global_aimrt_core
global_aimrt_core = aimrt_core
# Initialize
core_options = aimrt_py.CoreOptions()
core_options.cfg_file_path = "path/to/cfg/xxx_cfg.yaml"
aimrt_core.Initialize(core_options)
# Create Module
module_handle = aimrt_core.CreateModule("NormalSubscriberPyModule")
# Subscribe
topic_name = "test_topic"
subscriber = module_handle.GetChannelHandle().GetSubscriber(topic_name)
assert subscriber, f"Get subscriber for topic '{topic_name}' failed."
def EventHandle(ctx_ref, msg):
aimrt_py.info(module_handle.GetLogger(),
f"Get new pb event, ctx: {ctx_ref.ToString()}, data: {MessageToJson(msg)}")
aimrt_py.Subscribe(subscriber, event_pb2.ExampleEventMsg, EventHandle)
# Start
thread = threading.Thread(target=aimrt_core.Start)
thread.start()
while thread.is_alive():
thread.join(1.0)
if __name__ == '__main__':
main()