UORB讲解

2023-05-16

Pixhawk 飞控系统是基于ARM的四轴以上飞行器的飞行控制器, 它的前身是PX4-IMU,Pixhawk 把之前的IMU进行了完整的重构,最新版本是2.4.3。而对应的Pixhawk 1.x版本与2.x版本的区别在于,I/O板与FMU是否整合在一起。

uORB是Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。

  • uORB 的架构简述

uORB全称为micro object request broker (uORB),即 微对象请求代理器,**实际上uORB是一套跨进程的IPC通讯模块。**在Pixhawk中, 所有的功能被独立以进程模块为单位进行实现并工作。而进程间的数据交互就由为重要,必须要能够符合实时、有序的特点。

Pixhawk 使用NuttX实时ARM系统, 而uORB对于NuttX而言,它仅仅是一个普通的文件设备对象,这个设备支持Open、Close、Read、Write、Ioctl以及Poll机制。 通过这些接口的实现,uORB提供了一套“点对多”的跨进程广播通讯机制, “点”指的是通讯消息的“源”,“多”指的是一个源可以有多个用户来接收、处理。而“源”与“用户”的关系在于,源不需要去考虑用户是否可以收到某条被广播的消息或什么时候收到这条消息。它只需要单纯的把要广播的数据推送到uORB的消息“总线”上。对于用户而言,源推送了多少次的消息也不重要,重要的是取回最新的这条消息。

uORB实际上是多个进程打开同一个设备文件,进程间通过此文件节点进行数据交互和共享。

  • uORB 的系统实现

uORB的实现位于固件源码的src/modules/uORB/uORB.cpp文件,它通过重载CDev基类来组织一个uORB的设备实例。并且完成Read/Write等功能的重载。uORB 的入口点是uorb_main函数,在这里它检查uORB的启动参数来完成对应的功能,uORB支持start/test/status这3条启动参数,在Pixhawk的rcS启动脚本中,使用start参数来进行初始化,其他2个参数分别用来进行uORB功能的自检和列出uORB的当前状态。

在rcS中使用start参数启动uORB后,uORB会创建并初始化它的设备实例, 其中的实现大部分都在CDev基类完成。这个过程类似于Linux设备驱动中的Probe函数,或者Windows 内核的DriverEntry,通过init调用完成设备的创建,节点注册以及派遣例程的设置等。

  • uORB 源码分析之Open

    uORB 的Open接口实现了“源”或“用户” 打开uORB句柄的功能,打开uORB的句柄就意味着一个源的创建或把一个用户关联到某个源。我在这以源的创建为开端,逐步讲解Open的过程:

orb_advert_t orb_advertise(const struct orb_metadata *meta, const void data) { int result, fd; orb_advert_t advertiser; / open the node as an advertiser / fd = node_open(PUBSUB, meta, data, true); if (fd == ERROR) return ERROR; / get the advertiser handle and close the node / result = ioctl(fd, ORBIOCGADVERTISER, (unsigned long)&advertiser); close(fd); if (result == ERROR) return ERROR; / the advertiser must perform an initial publish to initialise the object */ result= orb_publish(meta, advertiser, data); if (result == ERROR) return ERROR; return advertiser; }
orb_advertise 其实就是一个int, meta是一个已定义好的源描述信息,里面就2个成员,分别为name以及size。保存了通讯的名称以及每次发送数据的长度。 创建源的过程为3个步骤, 打开uORB的设备节点, 获取设备实例, 推送第一条消息。

/*
 * Generate the path to the node and try to open it.
 */
ret = node_mkpath(path, f, meta);

if (ret != OK) {
	errno = -ret;
	return ERROR;
}

/* open the path as either the advertiser or the subscriber */
fd = open(path, (advertiser) ? O_WRONLY : O_RDONLY);

从代码中可以看出, 每个源都在/PUBSUB/目录下有一个设备节点。首先通过node_mkpath来拼接好设备节点路径,然后根据要打开的是源节点还是用户节点来选择标识

int
ORBDevNode::open(struct file *filp)
{
int ret;

/* is this a publisher? */
if (filp->f_oflags == O_WRONLY) {

	/* become the publisher if we can */
	lock();

	if (_publisher == 0) {
		_publisher = getpid();
		ret = OK;

	} else {
		ret = -EBUSY;
	}

	unlock();

	/* now complete the open */
	if (ret == OK) {
		ret = CDev::open(filp);

		/* open failed - not the publisher anymore */
		if (ret != OK)
			_publisher = 0;
	}

	return ret;
}

/* is this a new subscriber? */
if (filp->f_oflags == O_RDONLY) {

	/* allocate subscriber data */
	SubscriberData *sd = new SubscriberData;

	if (nullptr == sd)
		return -ENOMEM;

	memset(sd, 0, sizeof(*sd));

	/* default to no pending update */
	sd->generation = _generation;

	filp->f_priv = (void *)sd;

	ret = CDev::open(filp);

	if (ret != OK)
		free(sd);

	return ret;
}

/* can only be pub or sub, not both */
return -EINVAL;

}
uORB中规定了源节点只允许写打开,用户节点只允许只读打开。 我认为上面的Open代码里lock到unlock那段根本就不需要~ 那里仅仅是判断不允许重复创建同一个话题而已。而去重完全可以依赖其他的一些机制来解决, CDev::Open就不在继续往里说了。~如果oflags是RDONLY,那就表示要打开的是一个用户设备节点,sd是为这个用户准备的一个上下文结构。里面包含了一些同步计数器等信息,比如sd->generation,这里保存了当前用户读取到的消息的索引号,而_generation来源于源设备的每次写操作,每次源写入数据时,_generation会累加。每次用户读取数据时会把_generation同步到自己的sd->generation,通过这种处理,如果当前用户的sd->generation不等于全局的_generation就意味着源刚刚写入过数据,有最新的通讯消息可以供读取。

uORB设备的读和写

ssize_t
ORBDevNode::read(struct file *filp, char *buffer, size_t buflen)
{
SubscriberData *sd = (SubscriberData *)filp_to_sd(filp);

/* if the object has not been written yet, return zero */
if (_data == nullptr)
	return 0;

/* if the caller's buffer is the wrong size, that's an error */
if (buflen != _meta->o_size)
	return -EIO;

/*
 * Perform an atomic copy & state update
 */
irqstate_t flags = irqsave();

/* if the caller doesn't want the data, don't give it to them */
if (nullptr != buffer)
	memcpy(buffer, _data, _meta->o_size);

/* track the last generation that the file has seen */
sd->generation = _generation;

/*
 * Clear the flag that indicates that an update has been reported, as
 * we have just collected it.
 */
sd->update_reported = false;

irqrestore(flags);

return _meta->o_size;

}

读分为3步, 首先判断参数是否合理,然后屏蔽中断拷贝数据,最后更新同步信息。值得注意的是,如果没有源写数据,那么read会在第一个判断就退出,原因是_data缓冲区在首次write时才会成功申请。generation的同步这里也不在继续说了

ssize_t
ORBDevNode::write(struct file *filp, const char buffer, size_t buflen)
{
/

* Writes are legal from interrupt context as long as the
* object has already been initialised from thread context.
*
* Writes outside interrupt context will allocate the object
* if it has not yet been allocated.
*
* Note that filp will usually be NULL.
*/
if (nullptr == _data) {
if (!up_interrupt_context()) {

		lock();

		/* re-check size */
		if (nullptr == _data)
			_data = new uint8_t[_meta->o_size];

		unlock();
	}

	/* failed or could not allocate */
	if (nullptr == _data)
		return -ENOMEM;
}

/* If write size does not match, that is an error */
if (_meta->o_size != buflen)
	return -EIO;

/* Perform an atomic copy. */
irqstate_t flags = irqsave();
memcpy(_data, buffer, _meta->o_size);
irqrestore(flags);

/* update the timestamp and generation count */
_last_update = hrt_absolute_time();
_generation++;

/* notify any poll waiters */
poll_notify(POLLIN);

return _meta->o_size;

}
上面就是write的实现了,那个lock/unlock真心很鸡肋,我是感觉多余了,首次write会申请内存用于数据通讯, 然后关闭中断拷贝数据防止在复制的过程用有用户来read,最后是更新最后的更新时间以及同步计数器并且发送一个POLLIN的消息通知来唤醒那些还在等待uORB数据可读的用户

uORB设备的POLL状态

当用户没有指定数据读取的频率时,每次源的write都会触发一个POLLIN来唤醒用户去读取刚更新的数据。是否唤醒除了检查generation的值以外,另外一个要求就是读取频率的限制,每个用户可以单独指定自己打算读更新的频率。

bool
ORBDevNode::appears_updated(SubscriberData sd)
{
/
assume it doesn’t look updated */
bool ret = false;

/* avoid racing between interrupt and non-interrupt context calls */
irqstate_t state = irqsave();

/* check if this topic has been published yet, if not bail out */
if (_data == nullptr) {
	ret = false;
	goto out;
}

/*
 * If the subscriber's generation count matches the update generation
 * count, there has been no update from their perspective; if they
 * don't match then we might have a visible update.
 */
while (sd->generation != _generation) {

	/*
	 * Handle non-rate-limited subscribers.
	 */
	if (sd->update_interval == 0) {
		ret = true;
		break;
	}

	/*
	 * If we have previously told the subscriber that there is data,
	 * and they have not yet collected it, continue to tell them
	 * that there has been an update.  This mimics the non-rate-limited
	 * behaviour where checking / polling continues to report an update
	 * until the topic is read.
	 */
	if (sd->update_reported) {
		ret = true;
		break;
	}

	/*
	 * If the interval timer is still running, the topic should not
	 * appear updated, even though at this point we know that it has.
	 * We have previously been through here, so the subscriber
	 * must have collected the update we reported, otherwise
	 * update_reported would still be true.
	 */
	if (!hrt_called(&sd->update_call))
		break;

	/*
	 * Make sure that we don't consider the topic to be updated again
	 * until the interval has passed once more by restarting the interval
	 * timer and thereby re-scheduling a poll notification at that time.
	 */
	hrt_call_after(&sd->update_call,
		       sd->update_interval,
		       &ORBDevNode::update_deferred_trampoline,
		       (void *)this);

	/*
	 * Remember that we have told the subscriber that there is data.
	 */
	sd->update_reported = true;
	ret = true;

	break;
}

out:
irqrestore(state);

/* consider it updated */
return ret;

}
uORB 根据用户指定的周期来设置hrt(实时定时器),每过一个时间间隔,hrt会被发生调用,通过hrt_called来检查这个调用。如果未发生,即便此时源的数据已经更新那么也不会返回POLLIN来唤醒用户去读。 简单来说,它通过控制POLLIN的周期来单方面控制用户的读取间隔。 如果是linux平台,当用户指定了时间间隔后, 我会为它单独初始化一个内核定时器,每次poll调用时检查完可用更新后,再次检查定时器即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UORB讲解 的相关文章

  • PX4模块设计之二十一:uORB消息管理模块

    PX4模块设计之二十一 xff1a uORB消息管理模块 1 uORB模块构建模式2 uORB消息管理函数2 1 状态查询2 2 资源利用2 3 模块启动2 4 模块停止3 uORB消息接口3 1 消息主题注册3 2 消息主题去注册3 3
  • 查看uORB消息

    在调试检查某个具体传感器或者其驱动时 xff0c 这个很重要 虽然uorb top可以获得消息频率 xff0c 但是我还需要获得具体uorb消息的值 http docs px4 io master zh middleware uorb ht
  • 示例:PX4——添加msg、uORB

    git clone https github com PX4 Firmware cd Firmware git submodule update init recursive git checkout v1 11 0 beta1 make
  • UORB讲解

    Pixhawk 飞控系统是基于ARM的四轴以上飞行器的飞行控制器 xff0c 它的前身是PX4 IMU xff0c Pixhawk 把之前的IMU进行了完整的重构 xff0c 最新版本是2 4 3 而对应的Pixhawk 1 x版本与2 x
  • 飞控接收到的每一条MAVLink消息包都会上传到uORB消息池中吗?

    1 飞控接收到的每一条MAVLink消息包都会上传到uORB消息池中吗 xff1f 2 如何自定义 xff08 选择 配置 xff09 发送给QGC的MAVLink消息 xff1f
  • PX4-3-uORB

    uORB Micro Object Request Broker 微对象请求代理器 是PX4中非常重要且关键的一个模块 xff0c 用于各个模块之间的数据交互 实际上uORB是一套跨 进程 的IPC通讯模块 在PX4中 xff0c 所有的功
  • PX4:【uORB通讯机制】

    uORB xff1a Micro Object Request Broker PX4进程间的通讯机制 xff1a 多对多的信息发布与订阅方式 发布消息 xff1a 1 公告 advertise xff1a 相当于初始化 xff0c 在发布消
  • px4新建一个uORB消息

    px4新建一个uORB消息 方法非常简单 参考自 https dev px4 io master en middleware uorb html 第一步 在 src Firmware msg 下新建一个 msg文件 文件里面包含需要的变量
  • px4源码学习六--uORB模块研究

    UORB模块研读 uORB函数解析 xff1a uORB模块 xff08 Micro Object Request Broker xff0c 微对象请求代理器 xff09 uORB是Pixhawk系统中关键的一个模块 xff0c 肩负了数据
  • Pixhawk uORB通信

    Pixhawk 飞控 系统是基于ARM的四轴以上飞行器的飞行控制器 xff0c 它的前身是PX4 IMU xff0c Pixhawk 把之前的IMU进行了完整的重构 xff0c 最新版本是2 4 3 而对应的Pixhawk 1 x版本与2
  • PX4项目学习::(四)中间件::通信:uORB

    PX4代码学习系列博客 4 uORB相关 灰信网 xff08 软件开发博客聚合 xff09 freesion com
  • PX4二次开发——uorb订阅

    PX4二次开发 uorb订阅 一 写在前面 我们写了一个一个功能的模块 xff0c 这些模块并不是独立的 模块之间是有数据传递的 xff0c 这样才能组合到一起实现飞行控制的目的 那么解决模块之间的数据传递的方式就是通过uorb订阅的方式
  • PX4/Pixhawk---uORB深入理解和应用(最新版)

    1 简介 ps 第1章简介是参考 uORB深入理解和应用 1 1 PX4 Pixhawk的软件体系结构 PX4 Pixhawk的软件体系结构主要被分为四个层次 xff0c 这可以让我们更好的理解PX4 Pixhawk的软件架构和运作 xff
  • PX4源码分析6_uorb通信机制

    一 创建流程 xff1a 在Firmware msg下创建msg文件 xff0c eg xff1a xxx msg xff0c 内容格式仿照原有msg文件在Firmware msg CMakeLists txt中将对应的msg文件添加 xf
  • 利用uORB机制实现数据在不同进程中通信

    uORB实际上是一种设计模式中的观察者模式 xff0c 用于实现一种一对多的依赖关系 xff0c 让多个观察者 xff08 Observer xff09 同时监听某一主题对象 xff08 Subject xff09 当这个主题对象 xff0
  • PX4的UORB通信机制

    在Firmware的msg文件夹里面 xff0c 里面有很多 msg结尾的文件 xff0c 这些msg文件在编译的时候可以生成h头文件 xff0c 这些编译过程中生成的头文件一般是保存在build default src module uo
  • Nuttx下移植uorb笔记

    Nuttx下移植uorb笔记 之前接触过ros下的消息机制 xff08 生产者 消费者 xff09 模型 xff0c 第一感觉是灵活好用 xff0c 但是在资源有限的嵌入式环境里面 xff0c 邮箱 消息 显得就有点不那么灵活 xff0c
  • [pixhawk笔记]6-uORB流程及关键函数解析

    本文中将结合代码 文档及注释 xff0c 给出uORB执行流程及关键函数的解析 xff0c 由于uORB的机制实现较为复杂 xff0c 所以本文主要学习如何使用uORB的接口来实现通信 回到上一篇笔记中的代码 xff1a include l
  • PX4模块设计之三:自定义uORB消息

    PX4模块设计之三 xff1a 自定义uORB消息 1 新增自定义uORB消息步骤2 应用ext hello world消息示例3 编译执行结果4 参考资料 基于PX4开源软件框架简明简介和PX4模块设计之二 xff1a uORB消息代理
  • px4_simple_example和uorb机制

    px4 simple app PX4 Autopilot src exampes px4 simple app xff0c 这个程序是用c语言调用orb API和poll机制订阅和发布通讯数据 xff0c 但是这个例子并不是既有接收又有发送

随机推荐

  • CTeX安装及使用

    1 关于 LaTeX和CTeX quad LaTeX是一种基于 的排版系统 xff0c 由美国计算机学家莱斯利 兰伯特 xff08 Leslie Lamport xff09 在20世纪80年代初期开发 xff0c 利用这种格式 xff0c
  • 深度强化学习(7)深度确定性策略梯度(DDPG)

    深度确定性策略梯度 xff08 DDPG xff09 1 从随机策略到确定性策略 首先 xff0c 我们先了解一下随机策略和确定性策略 随机策略的公式为 xff1a a
  • 强化学习(1)马尔科夫决策过程(MDP)

    强化学习 开始强化学习之前先来了解强化学习 深度学习 深度强化学习 监督学习 无监督学习 机器学习和人工智能之间的关系 如下图 xff1a 强化学习是机器学习的一个重要分支 xff0c 它试图解决决策优化的问题 所谓决策优化 xff0c 是
  • 深度强化学习(3)Prioritized Replay DQN

    Prioritized Replay DQN 在深度强化学习 xff08 2 xff09 Double DQN 中 xff0c 我们讲到了DDQN使用两个Q网络 xff0c 用当前Q网络计算最大Q值对应的动作 xff0c 用目标Q网络计算这
  • 封装使用axios进行接口请求

    一 安装axios npm install axios在main js引入 import axios from 34 axios 34 Vue use axios vue3使用createApp App config globalPrope
  • VMware+Ubuntu20.04安装指南

    1 首先下载VMware Workstation Player 16 xff0c 下载链接为 https customerconnect vmware com en downloads all products 2 下载完成后 xff0c
  • Java8流式操作——中间操作

    文章目录 什么是中间操作 xff1f 方法实践说明一 前提条件Person类Data类 二 操作filter 过滤distinct 去重 xff08 去除集合中重复的元素 xff09 sorted 排序 sorted 无参构造 sorted
  • 数据挖掘(Data Mining)扫盲笔记

    知识框架来源 xff1a 人工智能之数据挖掘 其他补充来源 xff1a 拿下Offer 数据分析师求职面试指南 数据分析实战45讲 Data Mining 概述篇基础认知挖掘对象常见任务 xff1a 模型分类问题与挑战十大经典算法 C4 5
  • 信息论笔记(需要编辑格式)

    主要来源 xff1a 吴军 信息论40讲 信息论介绍 世界上任何一个探索者都需要清楚三件事 我们现在的位置 我们的目标 以及通向目标的道路 哲学是一门生活的艺术 它帮助我们认清自己 它回答了第一个问题 至于每一个人的目标 我相信大家比我更清
  • 阿里云短信服务使用

    说明 这是用go语言实现的 xff0c 但说实话 xff0c 其实没啥影响 xff0c 不管什么语言都是这个套路 xff0c 所以无论你是学什么语言或者是用什么语言的都好 xff0c 看看总不亏 22年7 14下午14 xff1a 38 x
  • 数据分析思维扫盲

    知识来源 xff1a 接地气学堂1 前言 行文之初衷 xff0c 建立知识树 xff0c 因而不易速读 xff0c 请君悉知 宜为工具书 xff0c 按索引取之 独学而无友 xff0c 必孤陋寡闻 xff0c 请君赐教 xff0c 不吝感激
  • 高阶用户运营体系搭建

    这里写目录标题 第1章 理解用户运营本质1 什么是 用户运营 xff1f 2 一个 用户运营 重点关注什么 xff1f 3 怎么做好用户运营 xff1f 4 高阶用户运营体系搭建5 大规模用户运营体系的3大子系统6 用户留存的归因 活跃差模
  • 商品管理-运营指挥室 看板

    商品管理可视化项目 项目目标 梳理商品管理的整体业务流程 xff0c 调研数据的使用情况 xff0c 建立影响业务的 xff1a 销量 留存 sku数 断码等维度指标 xff0c 建立智能数据监控体系 工作范围 销量看板留存看板在售SKC看
  • Excel 的进阶学习

    文章目录 Excel 的进阶学习1 常用的 Excel 函数及用途1 关联匹配类2 清洗处理类3 逻辑运算类4 计算统计类5 时间序列类 2 基础1 快捷键2 数据组错误信息基本认识计算操作符 3 数据图展示 3 实战分析注意 Excel
  • SQL

    数据库 基本概念 1 xff0e 数据 定义 xff1a 描述事物的符号序列 xff0c 数据 xff08 Data xff09 是数据库中存储的基本对象 数据的种类 xff1a 数字 文字 图形 图像声音及其他特殊符号 数据举例 xff1
  • MatLab-simulink组件(模块)中文名大全

    MATLAB 矩阵实验室 7 0 1 Simulink 仿真 6 1 Aerospace Blockset 太空模块 1 6 1 Bioinformatics Toolbox 生物信息工具箱 1 1 1 CDMA Reference Blo
  • 控制理论总结

    经典控制理论 xff1b 现代控制理论线性控制理论 xff1b 非线性控制理论最优控制 xff1b 预测控制 xff1b 鲁棒控制数字控制系统 xff1b 连续控制系统随动系统 xff1b 自动控制系统的分类 一 按给定信号的形式不同 xf
  • H无穷控制

    H无穷优化控制问题可归纳为 xff1a 求出一个使系统内部稳定的控制器K s xff0c 使闭环传函Tzw的无穷范数极小 LQG的弱点 xff1a 对控制的一个主要挑战使多变量控制系统设计 xff0c 因为MIMO系统的传函是一个矩阵 LQ
  • 增益调度控制

    增益调度方法在良性 Well Behaved 非线性系统中应用的比较普遍 xff0c 所谓良性非线性系统是指系统的行为特性能由一系列选定的局部线性化模型充分描述 增益调度控制方法的一个显著优点就是它能够充分利用现有的成熟的线性控制理论为非线
  • UORB讲解

    Pixhawk 飞控系统是基于ARM的四轴以上飞行器的飞行控制器 xff0c 它的前身是PX4 IMU xff0c Pixhawk 把之前的IMU进行了完整的重构 xff0c 最新版本是2 4 3 而对应的Pixhawk 1 x版本与2 x