在PX4中,uorb是用于无人机模块间通信的协议机制。
这篇博客对于uorb编程api的一些基本函数介绍的很好 https://blog.youkuaiyun.com/freeape/article/details/46880637
px4官网中提供了一个uorb自定义的教程,并不是十分完善,我接下来进行更加详细的阐述。
一、uorb机制
首先,我们可以将uorb的通信机制了解一下。它的设计理念很有趣,它可以实现不同模块中的数据快速通讯,并且以异步通讯为基本原则,也就是说在通讯过程中发送者只负责发送数据,而并不关心数据由谁接收,也不关心接收者是否能将所有的数据都接收到;而对于接收者来说并不关心数据是由谁发送的,也不关心在接收过程中是否将所有数据都接收到。
uORB在在数据发布与接收过程中并不保证发送者的所有数据都可以被接收者收到,而只保证接收者在想要接收时能收到最新的数据。而发送与接收的分离可以使飞程中各个模块相互独立,互不干扰。实际上一个uORB可以由多个发送者发布,也可以被多个接收者接收,也就是说他们之间是多对多的关系。发布者以一定频率更新发布数据到uorb平台上,不关心谁来接收。订阅者可以随时来获取数据。
网上有一个不错的小例子加以理解:https://baijiahao.baidu.com/s?id=1613003999209452823&wfr=spider&for=pc
有一个教室编号208,里面的黑板上可以写上一些文字内容,有一个同学名叫小强,他每隔1个小时就会来到208教室,先将黑板上原来的文字擦除,然后在黑板上写下一段新文字,之后离开208教室。而另外有一个同学叫小朋,他每隔3个小时就会来到208教室,将黑板上的文字抄写到自己的笔记本上,然后离开。我们可以用下列图例来说明一下这个过程:

我们可以看到,小强每次发布数据之后就会离开208教室,至于有没有人或是谁来读取他留下的文字,小强自己并不关心,也不再乎自己发布的数据是否有人收到了。而对于小朋来说,他每隔3小时来读取一次数据,至于这些数据是谁发布的他也不关心。他每隔3小时来读黑板上的文字时,其实小强已经在黑板上留言3次了,前两次的文字已经被小强擦除了,小朋看到的永远是小强留下最新的内容。
二、常用函数
转载: https://blog.youkuaiyun.com/freeape/article/details/46880637
1. int poll(struct pollfd fds[], nfds_t nfds, int timeout)
功能:监控文件描述符(多个);
说明:timemout=0,poll()函数立即返回而不阻塞;timeout=INFTIM(-1),poll()会一直阻塞下去,直到检测到return > 0;
参数:
fds:struct pollfd结构类型的数组;
nfds:用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:poll()函数会阻塞timeout所指定的毫秒时间长度之后返回;
-1:poll函数调用失败;同时会自动设置全局变量errno;
2.int orb_subscribe(const struct orb_metadata *meta)
功能:订阅主题(topic);
说明:即使订阅的主题没有被公告,但是也能订阅成功;但是在这种情况下,却得不到数据,直到主题被公告;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
返回值:
错误则返回ERROR;成功则返回一个可以读取数据、更新话题的句柄;如果待订阅的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
int fd = orb_subscribe(ORB_ID(topicName));
3.int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
功能:从订阅的主题中获取数据并将数据保存到buffer中;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
handle:订阅主题返回的句柄;
buffer:从主题中获取的数据;
返回值:
返回OK表示获取数据成功,错误返回ERROR;否则则有根据的去设置errno;
eg:
struct sensor_combined_s raw;
orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
4.orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
功能:公告发布者的主题;
说明:在发布主题之前是必须的;否则订阅者虽然能订阅,但是得不到数据;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
data:指向一个已被初始化,发布者要发布的数据存储变量的指针;
返回值:错误则返回ERROR;成功则返回一个可以发布主题的句柄;如果待发布的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
struct vehicle_attitude_s att;
memset(&att, 0, sizeof(att));
int att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att);
5.int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
功能:发布新数据到主题;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
handle:orb_advertise函数返回的句柄;
data:指向待发布数据的指针;
返回值:OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);
6.int orb_set_interval(int handle, unsigned interval)
功能:设置订阅的最小时间间隔;
说明:如果设置了,则在这间隔内发布的数据将订阅不到;需要注意的是,设置后,第一次的数据订阅还是由起初设置的频率来获取,
参数:
handle:orb_subscribe函数返回的句柄;
interval:间隔时间,单位ms;
返回值:OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
orb_set_interval(sensor_sub_fd, 1000);
7.orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)
功能:设备/驱动器的多个实例实现公告,利用此函数可以注册多个类似的驱动程序;
说明:例如在飞行器中有多个相同的传感器,那他们的数据类型则类似,不必要注册几个不同的话题;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
data:指向一个已被初始化,发布者要发布的数据存储变量的指针;
instance:整型指针,指向实例的ID(从0开始);
priority:实例的优先级。如果用户订阅多个实例,优先级的设定可以使用户使用优先级高的最优数据源;
返回值:
错误则返回ERROR;成功则返回一个可以发布主题的句柄;如果待发布的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
struct orb_test t;
t.val = 0;
int instance0;
orb_advert_t pfd0 = orb_advertise_multi(ORB_ID(orb_multitest), &t, &instance0, ORB_PRIO_MAX);
8.int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance)
功能:订阅主题(topic);
说明:通过实例的ID索引来确定是主题的哪个实例;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
instance:主题实例ID;实例ID=0与orb_subscribe()实现相同;
返回值:
错误则返回ERROR;成功则返回一个可以读取数据、更新话题的句柄;如果待订阅的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
int sfd1 = orb_subscribe_multi(ORB_ID(orb_multitest), 1);
9.int orb_unsubscribe(int handle)
功能:取消订阅主题;
参数: handle:主题句柄;
返回值: OK表示成功;
错误返回ERROR;否则则有根据的去设置errno;
eg: ret = orb_unsubscribe(handle);
10.int orb_check(int handle, bool *updated)
功能:订阅者可以用来检查一个主题在发布者上一次更新数据后,有没有订阅者调用过ob_copy来接收、处理过;
说明:如果主题在在被公告前就有人订阅,那么这个API将返回“not-updated”直到主题被公告。可以不用poll,只用这个函数实现数据的获取。
参数:
handle:主题句柄;
updated:如果当最后一次更新的数据被获取了,检测到并设置updated为ture;
返回值:
OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
if (PX4_OK != orb_check(sfd, &updated)) {
return printf("check(1) failed");
}
if (updated) {
return printf("spurious updated flag");
}
//or
bool updated;
struct random_integer_data rd;
/* check to see whether the topic has updated since the last time we read it */
orb_check(topic_handle, &updated);
if (updated) {
/* make a local copy of the updated data structure */
orb_copy(ORB_ID(random_integer), topic_handle, &rd);
printf("Random integer is now %d\n", rd.r);
}
11.int orb_stat(int handle, uint64_t *time)
功能:订阅者可以用来检查一个主题最后的发布时间;
参数:
handle:主题句柄;
time:存放主题最后发布的时间;0表示该主题没有发布或公告;
返回值:
OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
ret = orb_stat(handle,time);
12.int orb_exists(const struct orb_metadata *meta, int instance)
功能:检测一个主题是否存在;
参数:
meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
instance:ORB 实例ID;
返回值:
OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
ret = orb_exists(ORB_ID(vehicle_attitude),0);
13.int orb_priority(int handle, int *priority)
功能:获取主题优先级别;
参数:
handle:主题句柄;
priority:存放获取的优先级别;
返回值:
OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
ret = orb_priority(handle,&priority);
三、自定义 UORB
1.源码阅读
源码所在位置:Firmware\src\modules\uORB 使用语言:c++

2、自定义主题
在uorb上我们可以创建自己想要发布订阅的主题。
Firmware/msg下新建lin.msg (这个名字你随意取), 内容为 注意 timestamp 这个字段必须添加不然之后的make会报错

注意
1. # 和 topics 之间有空格
2. topics 之后的第一个主题必须与 文件名相同
3.timestamp 这个字段必须添加不然之后的make会报错(具体原因我还在学习)
Firmware/msg中的CMakeLists.txt中添加:lin.msg

使用make命令编译
这里我是想在jmavsim上使用,所以我使用的是
make px4_sitl_default jmavsim
编译后自动会在\Firmware\build\posix_sitl_default\uORB\topics文件夹下自生成相应.h文件(之后可以通过include引用)。

注意:
如果你使用的是其他make命令就到其他相应的文件夹查看.h文件是否生成。
例如:make px4fmu-v2_default 那么你就到 Firmware\build\nuttx_px4fmu-v2_default\uORB\topics上查看
测试
这一步可以开始进行测试自定义的主题是否生效。在Firmware/src/examples文件夹下建立一个文件夹Mytest。按照px4给的示例配置,只是文件名换一换,稍微修改修改(http://dev.px4.io/en/apps/hello_sky.html),并通过include .h文件加入自定义主题,编写代码测试。
测试的代码
#include <px4_config.h>
#include <px4_tasks.h>
#include <px4_posix.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <math.h>
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/lin.h>
//添加自己的头文件
__EXPORT int Mytest_main(int argc, char *argv[]); //主函数声明,必须要写
int Mytest_main(int argc, char *argv[])
{
PX4_INFO("Hello lin!");//PX4_INFO为打印函数,printf也可以
/*定义话题结构*/
struct lin_s test; //这个结构体就是lin.h文件中定义的
/*初始化数据*/
memset(&test, 0, sizeof(test));
/*公告主题*/
/*test_pub 为handle指针*/
orb_advert_t test_pub = orb_advertise(ORB_ID(lin), &test);
/*test数据赋值*/
test.data1 = 200;
/*发布测试数据*/
orb_publish(ORB_ID(lin), test_pub, &test);
/*订阅数据,在copy之前,必须要订阅*/
/*test_sub_fd为handle*/
int test_sub_fd = orb_subscribe(ORB_ID(lin));
struct lin_s data_copy;
/*copy数据*/
orb_copy(ORB_ID(lin), test_sub_fd, &data_copy);
/*打印*/
PX4_WARN("[m] GanTA:\t%8.4f",
(double)data_copy.data1
);
return 0;
}
修改Firmware\cmake\configs\posix_sitl_default.cmake文件,加入你一会要执行的Mytest

输入 make posix jmavsim 打开模拟器
在控制台上运行Mytest 成功输出


本文详细介绍了uORB在PX4无人机系统中的作用及其实现机制,包括异步通信原理、多对多数据交换特性,以及如何通过一系列API进行主题的发布与订阅。文章还提供了自定义uORB主题的步骤,涵盖了源码位置、主题创建、编译测试等关键环节。
1424

被折叠的 条评论
为什么被折叠?



