开源之夏 2025 项目申请书


项目名称: RT-Thread的uORB订阅发布机制组件
项目编号:2556e0542
项目主导师:latercomer
申请人:张博文
日期:2025.06.06
邮箱:honest-zh@qq.com


一、项目背景

1.1 基本需求

uORB(微型对象请求代理)是PX4飞控系统中的一种高效、解耦、事件驱动的轻量级通信机制,旨在为模块之间提供高效、可扩展、类型安全的发布-订阅通信。uORB在嵌入式系统中的适用性已经在无人机等领域得到了广泛的验证。然而,在大部分的RTOS之中,uORB并没有作为默认的操作系统通讯机制被广泛使用。

为了解决这一问题,本项目的初衷是设计并实现一款基于C语言开发的uORB通信机制组件,其接口风格应该与PX4上uORB保持一致。uORB应该使用POSIX标准接口以尽可能地保证在不同操作系统上的可移植性。uORB应该至少在一种操作系统上实现并完成基本功能测试。RT-Thread 是国内具有广泛应用的开源嵌入式实时操作系统,在物联网、工业控制、机器人等领域广受欢迎,具备极大的应用与推广潜力。因此,uORB决定基于RT-thread操作系统内核开发通信机制组件,尽可能保证组件在不同操作系统上的可移植性。

总而言之,本项目的最终目标是在RT-Thread上实现一套兼容uORB风格的发布-订阅通信机制组件,并对其进行优化,以提升RT-Thread在模块解耦、数据传递效率、可扩展性等方面的能力。

1.2 预期成果

本项目的预计产出的成果如下所示:

  1. 一个基于C语言实现的可较容易地移植的 uORB 风格发布-订阅组件。
  2. 至少具有两个示例应用用来展示组件的实际应用场景。
  3. uORB的设计说明&完善的API 文档。
  4. 将组件集成到RT-Thread的构建系统当中,可以通过menuconfig配置启用/禁用组件功能。

1.3 相关仓库

• RT-Thread 官方仓库: https://github.com/RT-Thread/rt-thread

• PX4 uORB 参考实现: https://github.com/PX4/PX4-Autopilot
源码路径:PX4-Autopilot/platforms/common/uORB/

• 基于c++实现的uORB GitHub 仓库(已有的): https://gitee.com/nextpilot/nextpilot-flight-control/tree/main/pkgs/lib/uORB

二、技术方案与可行性

本项目计划在RT-thread上实现与PX4的uORB功能和接口风格都相近的uORB通讯机制组件。因此首先需要了解PX4上的uORB的接口和实现机制,本项目应该基于这种接口和实现方式使用C语言实现。

2.1 uORB与一般的中间件的区别

PX4的uORB虽然叫做微型对象请求代理(micro Object Request Broker),但他的ORB是和RPC方式的中间件(比如CORBA)中的ORB是不同的。uORB更多的是借助其解耦思想,更偏向高实时性的嵌入式场景。uORB是系统的通信机制而不是一种操作系统之上的中间件。更准确的说,uORB是一种基于共享内存方式实现的发布订阅模式本地进程间通信机制。

uORB其与传统意义上的ORB的区别如下表所示。

特性 传统 ORB(如 CORBA) uORB(PX4)
功能 远程方法调用(RPC) 数据发布/订阅
通信范围 分布式系统中,跨进程/跨设备通信 嵌入式系统内,本地通信
是否为中间件 是中间件,基于TCP/IP 无中间件,基于共享内存
对象粒度 真正的对象实例(远程对象) 结构体形式的数据
代理形式 Stub / Skeleton orb_advert_t/subscription handler
实时性 非实时/软实时 硬实时性,适用于RTOS

2.2 uORB的发布订阅模式

PX4系统由多个模块(tasks)组成,比如传感器驱动、姿态估算、位置控制器、飞行任务管理器等,这些模块之间需要频繁通信。uORB就是为了避免这些模块之间的耦合和复杂的依赖而设计的,uORB提供一种基于“发布者-订阅者(Publisher-Subscriber)”模式的通信机制进行不同模块间的通信解耦。

和传统的发布订阅模式中间件一样,uORB也有着发布者、订阅者这两种角色,通过主题的方式实现通信。发布者负责将消息发布到某个主题上,订阅者负责从某个topic上订阅消息,读取发布的内容。发布订阅模式下,每个主题就像一个消息队列。发布者是生产者,向某个主题(消息队列)写入数据;订阅者是消费者,从订阅的主题(消息队列)读取数据;发布者和订阅者只考虑向主题的缓冲区(消息队列)写入/读取数据而无需考虑数据是谁接收/发送,从而实现了通信的解耦合。

2.3 uORB的消息生成机制

和传统的发布订阅模式中间件不同的是,uORB用于操作系统中的本地通信。消息并没有通过网络协议栈发出,而是通过共享内存的方式直接从各个主题的缓冲区进行零拷贝的读写。uORB传输的不是网络协议栈消息而是内存缓冲区里结构体形式的数据结构,自然也不存在中间件的porotobuf/json这样的中间形式,更不存在序列化和反序列化这样的问题。这种方法带来的问题是一个主题种只能传输一个预先设定好的数据结构。

uORB中,订阅方唯一需要知道的是在内存缓冲区中数据结构的存放形式。uORB的使用者(发布方/订阅方)会编写一个XXX.msg格式的文件来指定主题唯一的数据结构。这个XXX.msg会在编译时由Python脚本在编译时生成对应的XXX.c和XXX.文件,其中包含结构体形式的数据结构和数据结构注册为topic的宏。

2.4 uORB的主题缓冲区(无锁环形队列)

在uORB的底层,主题的缓冲区实际上是通过无锁的环形队列实现的。每个主题对应一个环形队列,当消息数量超过队列的长度时就会从头开始覆写,为了保证多个进程同时读写缓冲区的一致性问题,环形队列需要使用无锁的原子访问机制实现。

2.5 uORB的多实例机制

在实际使用当中,可能同时存在多个设备,他们在传输的是相同数据结构,具有不同的物理意义的消息。这个时候让它们使用同一个主题就显得并不合适。为了解决这一问题,uORB使用了多实例机制。

在单实例模式下,每个主题名称只有一个实例,对应一个缓冲区,主题名称是其唯一标识。在多实例模式下,一个主题可以有等多个实例以表示并行的数据源,每个实例有一个缓冲区,主题名称+指定的实例号才能识别一个实例。多实例模式下,订阅方可以指定连接主题的哪个实例。通过多实例机制,“同一类数据”可以被多个“独立的数据源”发布,并被区分地进行处理。

2.6 uORB的写回调

uORB具有写回调,用来保证主题被发布新数据后自动触发某个指定到的函数,让通知订阅者在接收到消息之外做附加的处理。某种程度上说,写回调可以代替poll、event等相对复杂的事件监听功能,实现轻量级的类似中断的通知机制。

三、项目实现细节

根据导师反馈,uORB已有基于C++实现的项目参考,但是出于提升移植性、资源控制能力与适配嵌入式低资源环境的考虑,需要使用C语言进行重写。项目目标为在RT-Thread上实现一个接口风格与PX4高度兼容、内部机制可靠高效的uORB通信机制,底层采用纯C语言实现,并向上封装C和C++风格的两种风格的接口(C++接口风格与基于C++实现的uORB一致)。

本uORB需要实现以下功能:

3.1 实现PX4的uORB的功能。

3.1.1 msg 消息定义支持:

具有将XXX.msg的文件转化为结构体数据结构描述的python脚本,可以根据XXX.msg自动生成数据结构体定义(头文件)、主题元信息与注册表、类型描述及调试辅助数据

3.1.2 基于无锁环形缓冲区的主题数据结构:

每个主题(topic)拥有独立的环形队列作为缓冲区,支持并发写入与多个订阅者读取,采用原子操作或轻量级临界区实现线程安全,支持按需配置缓冲深度。

3.1.3 具有发布订阅通信功能:

  • 数据发布模块:通过orb_publish()等接口发布数据到主题队列中

-数据订阅模块:支持阻塞与非阻塞的orb_subscribe()等接口,支持多线程读取,提供事件通知与主动轮询机制

3.1.4 具有主题的单实例和多实例模式:

  • 单实例:一个主题名只对应一个缓冲区

  • 多实例:一个主题名可以创建多个逻辑实例

3.1.5 具有写回调机制:

支持用户在发布数据时注册钩子函数,数据写入后立即触发。

3.2 具有PX4的uORB的orb_XXX_XXX用户接口。

3.2.1 发布相关接口:

  • orbadvertt orbadvertise(const struct orbmetadata meta, const void data):广播一个主题,初始化并发布单实例。返回一个广告句柄(orb_advert_t)。

  • orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority):广播一个多实例主题,自动分配空闲实例编号。

  • orb_unadvertise(orb_advert_t handle):取消广播,释放广告句柄相关资源。适用于任务销毁时。

  • int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data):向主题写入新数据,通知订阅者。

  • int orb_publish_jieauto(const struct orb_metadata *meta, orb_advert_t *handle, const void *data, int *instance):自动发布,如果未曾advertise则自动注册后发布,简化使用流程。

3.2.2 订阅相关接口:

  • int orb_subscribe(const struct orb_metadata *meta):订阅单实例主题,返回订阅句柄(文件描述符形式)。

  • int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance): 订阅多实例主题。

  • int orb_unsubscribe(int handle):取消订阅,释放资源。

3.2.3 数据交互接口:

  • orb_copy(const struct orb_metadata *meta, int handle, void *buffer) :从主题读取数据,拷贝到用户缓冲区

  • orb_check(int handle, bool *updated) :检查某主题是否有新数据(是否“更新”)

  • orb_exists(const struct orb_metadata *meta, int instance):检查指定主题实例是否存在(是否已发布)。

  • orb_group_count(const struct orb_metadata *meta):获取某多实例主题当前的活跃发布者数量。

3.2.4 订阅节流控制接口:

  • orb_set_interval(int handle, unsigned interval_ms):设置订阅最小读取时间间隔,单位毫秒,用于限速通知。
  • orb_get_interval(int handle, unsigned *interval):获取当前订阅间隔。
  • 2.5 调试/管理接口

  • uorb_start(void):启动 uORB 管理器(初始化内部结构)

  • uorb_status(void):打印/获取 uORB 当前内部状态

  • uorb_top(char **topic_filter, int num_filters):输出当前所有主题的状态信息,用于 top 命令或状态可视化工具

2.6 工具函数接口:

  • orb_get_queue_size(const struct orb_metadata *meta):获取指定主题的队列深度(缓冲区长度)。

  • orb_get_c_type(unsigned char short_type):将msg文件中的简写类型转换为 C 类型(用于生成代码)。

  • orb_print_message_internal(const struct orb_metadata *meta, const void *data, bool print_topic_name):打印消息结构体内容,调试用。

3.3 具有和PX4功能相似的命令行工具:

  • uorb start:启动uORB服务(初始化中间件对象管理器)

  • uorb status:显示所有uORB主题的统计信息,如订阅数、队列深度等

  • uorb top:实时监控uORB主题的发布频率(类似 top 命令的视图)

3.4 将uORB作为RT-thread的功能组件,使用menuconfigs在构建系统中启用/禁用。

四、项目计划和时间表

周次 时间范围 主要任务
第1周 7.1–7.7 学习PX4 uORB和RT-Thread通信机制,搭建开发测试环境
第2周 7.8–7.14 设计核心接口与数据结构,完成.msg到C代码的Python生成脚本
第3周 7.15–7.21 实现主题注册与uORB管理模块,测试代码生成效果
第4周 7.22–7.28 实现环形缓冲区、发布订阅核心API(orb_advertise/orb_subscribe等)
第5周 7.29–8.4 实现多实例支持、订阅节流、状态检查与非阻塞读取等增强功能
第6周 8.5–8.11 添加调试接口、打印工具与Shell命令支持
第7周 8.12–8.18 集成RT-Thread构建系统,完善Kconfig与组件注册
第8周 8.19–8.25 编写典型使用示例:发布订阅的使用
第9周 8.26–9.1 编写高级示例:多任务并发订阅+Topic过滤与节流演示
第10周 9.2–9.8 撰写API文档、示例文档,进行中期总结与导师反馈优化
第11周 9.9–9.15 优化健壮性、内存管理与边界处理,增加测试覆盖
第12周 9.16–9.22 项目重构、代码规范整理、接口注释与文档完善
第13周 9.23–9.30 编写最终报告,开源发布,完成项目结题材料提交