精华内容
下载资源
问答
  • uorb
    2018-09-18 14:03:05
    uORB(Micro Object Request Broker,微对象请求代理器)是PX4/Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。实际上uORB是一套跨「进程」 的IPC通讯模块。在Pixhawk中, 所有的功能被独立以进程模块为单位进行实现并工作。而进程间的数据交互就由为重要,必须要能够符合实时、有序的特点。 
    uORB实际上是多个进程打开同一个设备文件,进程间通过此文件节点进行数据交互和共享。
    进程通过命名的「总线」交换的消息称之为「主题」(topic),在Pixhawk 中,一个主题仅包含一种消息类型,通俗点就是数据类型。
    每个进程可以「订阅」或者「发布」主题,可以存在多个发布者,或者一个进程可以订阅多个主题,但是一条总线上始终只有一条消息。
     应用层中操作基础飞行的应用之间都是隔离的,这样提供了一种安保模式,以确保基础操作独立的高级别系统状态的稳定性。而沟通它们的就是uORB。
    Topic:很多主题数据,姿态 位置
    Msg:姿态数据结构体message
    

    **程序函数:**int orb_subscribe(const struct orb_metadata *meta)
    功能:订阅主题(topic);
    参数:
    meta:uORB元对象,可以认为是主题id;
    返回值:正确返回一个句柄,句柄是你自己定义的,初始化为-1,如int my_topic_sub=-1;
    int my_topic_sub = orb_subscribe(ORB_ID(my_topic));

    int orb_set_interval(int handle, unsigned interval)
    功能:设置订阅的最小时间间隔;
    int poll(struct pollfd fds[], nfds_t nfds, int timeout)
    功能:监控文件描述符(多个);
    fds:struct pollfd结构类型的数组,进程可以同时等待很多个主题数据,当有数据更新时,判断一下是谁
    nfds:用于标记数组fds中的结构体元素的总数量;
    timeout:是poll函数调用阻塞的时间,单位:毫秒;
    返回值:

    0:阻塞时间内拿到了数据;
    ==0:抱歉时间都用完了,也没能拿到数据;
    -1:poll函数调用失败;

    int orb_check(int handle, bool *updated)
    功能:检查一个主题数据有没有更新,更新了updated会被设置为true。但一旦调用ob_copy来接收、处理过后,updated会被自动设置为false,所以这种方式数据只能被第一个人检测到,后面人再检测到时updated已经是false。

    int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
    功能:从订阅的主题中获取数据并将数据保存到buffer中。meta消息的ID,handle句柄,哪个句柄,你订阅时返回的句柄,从消息中拷贝数据放到buffer中。buffer是自己消息的数据结构类型自己定义的。例:sensor_combined_s raw ;orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);

    orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
    功能:先公告主题,相当于注册,只要一次就行了。meta公告的消息ID,data公告的原始数据。
    说明:在发布主题之前是必须的;否则订阅者虽然能订阅,但是得不到数据;

    int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
    功能:公告之后就可以发布主题数据。meta公告的消息ID,handle公告返回的句柄,这个句柄初始化为null,data发布的数据,要发布什么数据,自己填充。

    int orb_unsubscribe(int handle)

    功能:取消订阅主题;
    参数:
    handle:主题句柄;
    返回值:
    OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
    eg:
    ret = orb_unsubscribe(handle);

    数据传递函数:
    1、
    void *memset(void *s, int c, size_t n)
    作用是在一段内存块中填充某个给定的值,将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 c 替换并返回 s
    例子:
    memset(&actuator_controls, 0, sizeof(actuator_controls));
    表示将actuator_controls的各位置0

    2、
    FAR void *memcpy(FAR void *dest, FAR const void *src, size_t n)
    {
    FAR unsigned char pout = (FAR unsigned char)dest;
    FAR unsigned char pin = (FAR unsigned char)src;
    while (n– > 0) *pout++ = *pin++;
    return dest;
    }
    从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
    例子:
    memcpy(val, v, param_size(param));
    表示将v中的前n个值复制到val中

    3、
    uintptr_t param_find(const char *name) //unsigned int pointer
    参数:
    ​ *name 指针名
    功能:
    ​ 返回指针指向的值
    例子:
    param_t _param_system_id = param_find(“MAV_SYS_ID”);
    PARAM_DEFINE_INT32(MAV_SYS_ID, 1);
    表示将MAV_SYS_ID所代表的值1赋给_param_system_id

    4、
    int param_get(param_t param, void *val)
    参数:
    ​ param 源参数;
    ​ val 目标参数指针
    功能:获取param的地址并赋给val
    例子:
    param_get(_param_system_id, &(status.system_id));
    表示将_param_system_id的地址写入status.system_id中

    更多相关内容
  • 可以在找到uORB消息定义,该定义代表在此软件包中找到的ROS2消息的反义部分。 这些消息定义是如何生成的? 当存储库中的uORB消息定义发生更改时,CI / CD管道会自动生成并将更新的ROS消息定义推送到该存储库。 ROS...
  • UORB测试.rar

    2020-05-19 15:44:15
    PIX4飞控二次开发,关于自定义UORB的实现;UORB测试代码,含有订阅、发布两个部分,msg定义有对应流程,有什么问题请留言.
  • UORB讲解

    千次阅读 2020-08-16 10:54:52
    uORB是Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。 uORB 的架构简述 uORB全称为micro ...

    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调用时检查完可用更新后,再次检查定时器即可。

    展开全文
  • 基于PX4开源软件框架简明简介和PX4模块设计之二:uORB消息代理, 了解了更多关于中间件uORB消息代理作为PX4系统内部消息传递的重要性。关于新增消息或者消息主题,可以分为PX4代码库内部新增(通常是PX4开源组织在...

    基于PX4开源软件框架简明简介PX4模块设计之二:uORB消息代理, 了解了更多关于中间件uORB消息代理作为PX4系统内部消息传递的重要性。

    关于新增消息或者消息主题,可以分为PX4代码库内部新增(通常是PX4开源组织在进行维护)和外部新增两种方式。

    这里自定义uORB消息主要侧重的是外部新增主题消息,后续将会结合一个示例进行相关应用的Demo。

    1. 新增自定义uORB消息步骤

    1. 新增与PX4-Autopilot平行目录PX4-ExternalModule;
    2. 建立PX4-ExternalModule下相应的msg、src目录;
    3. 在PX4-ExternalModule的msg目录下新建.msg文件,参考PX4-Autopilot下msg目录下的相关文件;
    1 uint64 timestamp	# time since system start (microseconds)
    2 int8 hello          # index of hello variable
    3 
    4 # TOPICS ext_hello_world
    
    1. 在PX4-ExternalModule的msg目录下新建CMakeLists.txt文件;
    2. 在CMakeList.txt文件中添加.msg文件;
    34 set(config_msg_list_external
    35    ext_hello_world.msg
    36    PARENT_SCOPE
    37    )
    
    1. 在make的时候,框架代码会自动根据上述源文件生成对应的两个C/C++文件;
    PX4-Autopilot\build\px4_sitl_default\uORB\topics\ext_hello_world.h
    PX4-Autopilot\build\px4_sitl_default\msg\topics_sources\ext_hello_world.cpp
    

    注:自此已经完成新增自定义uORB消息ext_hello_world。

    2. 应用ext_hello_world消息示例

    定义消息的目的主要是为了不同的任务之间进行通信,这里写了一个Demo:两个任务,一个任务publish消息,一个任务subscribe消息;

    1. 在Demo代码里面包含头文件;
    #include <uORB/topics/ext_hello_world.h>
    
    1. 使用ORB_ID(ext_hello_world)唯一定位消息主题;
    2. 使用px4_task_spawn_cmd函数建立两个任务;
    3. 使用uORB消息处理接口函数Demo应用;
    /****************************************************************************
     *
     *   Copyright (c) 2012-2019 PX4 Development Team. All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in
     *    the documentation and/or other materials provided with the
     *    distribution.
     * 3. Neither the name PX4 nor the names of its contributors may be
     *    used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     * POSSIBILITY OF SUCH DAMAGE.
     *
     ****************************************************************************/
    
    /**
     * @file px4_custom_uorb.c
     * extern custom uorb example for PX4 autopilot
     *
     * @author Example User <lida-mail@163.com>
     */
    
    #include <px4_platform_common/px4_config.h>
    #include <px4_platform_common/tasks.h>
    #include <px4_platform_common/posix.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <poll.h>
    #include <string.h>
    #include <math.h>
    #include <sys/types.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>
    
    
    #include <uORB/uORB.h>
    #include <uORB/topics/ext_hello_world.h>
    
    #define TASK_NAME_CUSTOM_UORB_SUBSCRIBE  "orb subscribe"
    #define TASK_NAME_CUSTOM_UORB_PUBLISH    "orb publish"
    
    #define TASK_SUBSCRIBE_NUM                8
    #define TASK_PUBLISH_NUM                  5
    
    __EXPORT int px4_custom_uorb_main(int argc, char *argv[]);
    
    int px4_custom_uorb_subscribe(int argc, char *argv[])
    {
    	/* listen */
    	int hello_fd = orb_subscribe(ORB_ID(ext_hello_world));
    	/* limit the update rate to 5 Hz */
    	orb_set_interval(hello_fd, 200);
    	/* one could wait for multiple topics with this technique, just using one here */
    	px4_pollfd_struct_t fds[] = {
    		{ .fd = hello_fd,   .events = POLLIN },
    		/* there could be more file descriptors here, in the form like:
    		 * { .fd = other_sub_fd,   .events = POLLIN },
    		 */
    	};
    
    	int error_counter = 0;
    
    	for (int i = 0; i < TASK_SUBSCRIBE_NUM; i++) {
    		/* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
    		int poll_ret = px4_poll(fds, 1, 5000);
    
    		/* handle the poll result */
    		if (poll_ret == 0) {
    			/* this means none of our providers is giving us data */
    			PX4_ERR("Got no data within a second");
    
    		} else if (poll_ret < 0) {
    			/* this is seriously bad - should be an emergency */
    			if (error_counter < 10 || error_counter % 50 == 0) {
    				/* use a counter to prevent flooding (and slowing us down) */
    				PX4_ERR("ERROR return value from poll(): %d", poll_ret);
    			}
    
    			error_counter++;
    
    		} else {
    
    			if (fds[0].revents & POLLIN) {
    				/* obtained data for the first file descriptor */
    				struct ext_hello_world_s tmp;
    				/* copy sensors raw data into local buffer */
    				orb_copy(ORB_ID(ext_hello_world), hello_fd, &tmp);
    				PX4_INFO("Hello world(subscribe):\t%d", tmp.hello);
    			}
    			
    			/* there could be more file descriptors here, in the form like:
    			 * if (fds[1..n].revents & POLLIN) {}
    			 */
    		}
    	}
    	
    	PX4_INFO("px4_custom_uorb_subscribe exiting");
    	
    	return 0;
    }
    
    int px4_custom_uorb_publish(int argc, char *argv[])
    {
    	/* advertise */
    	struct ext_hello_world_s hello_world;
    	memset(&hello_world, 0, sizeof(hello_world));
    	orb_advert_t hello_world_pub = orb_advertise(ORB_ID(ext_hello_world), &hello_world);
    	
    	for (int i = 0; i < TASK_PUBLISH_NUM; i++) {
    		
    		/* Wait for subscriber */
    		sleep(5);
    
    		/* set att and publish this information for other apps
    		 the following does not have any meaning, it's just an example
    		*/
    		hello_world.hello = i + 1;
    
    		PX4_INFO("Hello world(puslish):\t%d", hello_world.hello);
    		orb_publish(ORB_ID(ext_hello_world), hello_world_pub, &hello_world);
    	}
    	
    	PX4_INFO("px4_custom_uorb_publish exiting");
    	
    	return 0;
    }
    
    int px4_custom_uorb_main(int argc, char *argv[])
    {
    	int task_subscribe, task_publish;
    
    	task_publish = px4_task_spawn_cmd(TASK_NAME_CUSTOM_UORB_PUBLISH, SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 2000, px4_custom_uorb_publish, argv);
    	PX4_INFO("creating %s task_publish = %d", TASK_NAME_CUSTOM_UORB_PUBLISH, task_publish);
    	
    	sleep(1);
    		
    	task_subscribe = px4_task_spawn_cmd(TASK_NAME_CUSTOM_UORB_SUBSCRIBE, SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 2000, px4_custom_uorb_subscribe, argv);
    	PX4_INFO("creating %s task_subscribe = %d", TASK_NAME_CUSTOM_UORB_SUBSCRIBE, task_subscribe);
    	
    	while(px4_task_is_running(TASK_NAME_CUSTOM_UORB_SUBSCRIBE) || px4_task_is_running(TASK_NAME_CUSTOM_UORB_PUBLISH)){
    		sleep(1);
    	}
    
    	PX4_INFO("px4_custom_uorb_main exiting");
    
    	return 0;
    }
    
    

    整个DEMO结构如下所示:

    目录结构
     ├── PX4-Autopilot
     │      └── build/px4_sitl_default
     │          ├── external_modules
     │          │    └── examples
     │          │         └── px4_custom_uorb/libexamples__px4_custom_uorb.a
     │          ├── uORB
     │          │    └── topics/ext_hello_world.h
     │          └── msg
     │               └── ttopics_sources/ext_hello_world.cpp
     └── PX4-ExternalModule
            ├── msg
            │   ├── CMakeLists.txt
            │   └── ext_hello_world.msg
            └── src
                ├── CMakeLists.txt
                └── examples
                    └── px4_custom_uorb
                        ├── CMakeLists.txt
                        ├── Kconfig
                        └── px4_custom_uorb.c
    

    完整的PX4-ExternalModule代码,请链接下载。

    3. 编译执行结果

    基于SITL仿真环境,在PX4-Autopilot目录下,进行编译;

    $ make px4_sitl EXTERNAL_MODULES_LOCATION=../PX4-ExternalModule/
    

    然后,执行仿真环境;

    $ make px4_sitl jmavsim
    

    最后,测试下效果。

    pxh> px4_custom_uorb 
    INFO  [px4_custom_uorb] creating orb publish task_publish = 13
    INFO  [px4_custom_uorb] creating orb subscribe task_subscribe = 14
    INFO  [px4_custom_uorb] Hello world(subscribe):	0
    INFO  [px4_custom_uorb] Hello world(puslish):	1
    INFO  [px4_custom_uorb] Hello world(subscribe):	1
    INFO  [px4_custom_uorb] Hello world(puslish):	2
    INFO  [px4_custom_uorb] Hello world(subscribe):	2
    INFO  [px4_custom_uorb] Hello world(puslish):	3
    INFO  [px4_custom_uorb] Hello world(subscribe):	3
    INFO  [px4_custom_uorb] Hello world(puslish):	4
    INFO  [px4_custom_uorb] Hello world(subscribe):	4
    INFO  [px4_custom_uorb] Hello world(puslish):	5
    INFO  [px4_custom_uorb] px4_custom_uorb_publish exiting
    INFO  [px4_custom_uorb] Hello world(subscribe):	5
    ERROR [px4_custom_uorb] Got no data within a second
    ERROR [px4_custom_uorb] Got no data within a second
    INFO  [px4_custom_uorb] px4_custom_uorb_subscribe exiting
    INFO  [px4_custom_uorb] px4_custom_uorb_main exiting
    

    4. 参考资料

    【1】uorb messaging
    【2】out of tree uorb message definitions

    展开全文
  • uORB(Micro Object Request Broker,微对象请求代理器)是PX4中非常重要且关键的一个模块,通过异步publish/subscribe的消息传递方式,用于各个模块之间的数据交互。这个使我想到了11年的时候,接触的CORBA, Common ...

    uORB(Micro Object Request Broker,微对象请求代理器)是PX4中非常重要且关键的一个模块,通过异步publish/subscribe的消息传递方式,用于各个模块之间的数据交互。

    这个使我想到了11年的时候,接触的CORBA, Common Object Request Broker。度娘了一圈(包括Google),发现这个uORB是PX4的特殊模块,也没有被抽象出来(比如:Nuttx,MAVLink,Coap,Mqtt等能独立抽象出来)。

    In distributed computing, an object request broker (ORB) is a middleware which allows program calls to be made from one computer to another via a computer network, providing location transparency through remote procedure calls. ORBs promote interoperability of distributed object systems, enabling such systems to be built by piecing together objects from different vendors, while different parts communicate with each other via the ORB.

    其实对象模型更多考虑是解耦(松耦合模块设计),当然这个和我们前面PX4开源软件框架简明简介的框架设计原则是非常吻合的。

    既然这样先对这个模块进行下了解,关于内部实现细节和模块性能这方面我们暂放一边。

    1. uORB模块接口

    接口文件定义:

    platforms\common\uORB\uORB.h
    

    服务模型

    1.1. uORB服务接口

    int uorb_start(void); //启动服务
    
    int uorb_status(void); //服务状态查询,命令行打印状态
    
    int uorb_top(char **topic_filter, int num_filters); //uORB动态情况查询,类似top
    

    1.2. uORB消息注册/去注册接口

    orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data); //注册uORB主题(缓存1个消息)
    
    orb_advert_t orb_advertise_queue(const struct orb_metadata *meta, const void *data, unsigned int queue_size); //注册uORB主题(缓存queue_size个消息)
    
    orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance); //注册多实例uORB主题(每个实例缓存1个消息)
    
    orb_advert_t orb_advertise_multi_queue(const struct orb_metadata *meta, const void *data, int *instance, unsigned int queue_size);  //注册多实例uORB主题(每个实例缓存queue_size个消息)
    
    int orb_unadvertise(orb_advert_t handle); //去注册uORB主题
    

    1.3. uORB消息发布接口

    int	orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data); //发布uORB消息
    
    int orb_publish_auto(const struct orb_metadata *meta, orb_advert_t *handle, const void *data, int *instance); //整合注册uORB主题+发布uORB消息的函数(支持多实例)
    

    1.4. uORB消息订阅/去订阅接口

    int	orb_subscribe(const struct orb_metadata *meta); //订阅uORB主题
    
    int	orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance); //订阅uORB主题(针对特定主题)
    
    int	orb_unsubscribe(int handle); //去订阅uORB主题
    

    1.5. uORB辅助功能接口

    int	orb_copy(const struct orb_metadata *meta, int handle, void *buffer); //获取uORB主题消息数据
    
    int	orb_check(int handle, bool *updated); //用来检查一个主题在发布者上一次更新数据后,有没有订阅者调用过ob_copy来接收、处理过
    
    int	orb_exists(const struct orb_metadata *meta, int instance); //检测一个主题是否存在
    
    int	orb_group_count(const struct orb_metadata *meta); //检查有多少个实例
    
    int	orb_set_interval(int handle, unsigned interval); //设置订阅的最小时间间隔。如果设置了,则在这间隔内发布的数据将订阅不到;需要注意的是,设置后,第一次的数据订阅还是由最初设置的频率来获取。
    
    int	orb_get_interval(int handle, unsigned *interval); //获取订阅的最小时间间隔
    
    char *orb_get_c_type(unsigned char short_type); //ctype类型检查
    
    void orb_print_message_internal(const struct orb_metadata *meta, const void *data, bool print_topic_name); //内部debug函数
    

    2. Hello World with uORB

    PX4是提供了一个完整的飞控解决方案。什么叫完整,不仅仅是结果,还会提供过程,甚至是为什么。所以这个【Hello World with uORB】的示例代码,已经有了。

    -不仅开源,还专业,哈哈!!!

    注1:完整代码详见链接
    注2:关于自定义消息这块内容,后面我们会找机会介绍,先了解这个模块和简单应用。

    2.1. px4_simple_app工程结构

    这个就是完整的Hello World任务工程。为什么这里叫任务工程(是我自己随便叫的),主要原因:

    1. PX4代码主要是跑在两种OS上(Nuttx or Unix), Nuttx任务对应linux线程;
    2. PX4代码在Nuttx上主要是启动多个任务
    3. PX4代码在Unix上主要是启动一个进程下的多个线程(对应Nuttx任务)
    4. PX4代码采用CMakeLists.txt和MODULE的方式来对应用层进行打包
    src\examples\px4_simple_app\
     ├──> px4_simple_app.c
     ├──> CMakeLists.txt
     └──> Kconfig
    

    2.2. px4_simple_app之Hello World

    这里是Hello World代码,很熟悉吧!类似C语言Linux应用程序之Helloworld入门

    54 __EXPORT int px4_simple_app_main(int argc, char *argv[]);
    55 
    56 int px4_simple_app_main(int argc, char *argv[])
    57 {
    58 	PX4_INFO("Hello Sky!");
    

    2.3. px4_simple_app之uORB

    示例代码中使用了以下API

    1. orb_subscribe
    2. orb_set_interval
    3. orb_advertise
    4. orb_copy
    5. orb_publish
    60 	/* subscribe to vehicle_acceleration topic */
    61 	int sensor_sub_fd = orb_subscribe(ORB_ID(vehicle_acceleration));
    62 	/* limit the update rate to 5 Hz */
    63 	orb_set_interval(sensor_sub_fd, 200);
    64 
    65 	/* advertise attitude topic */
    66 	struct vehicle_attitude_s att;
    67 	memset(&att, 0, sizeof(att));
    68 	orb_advert_t att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
    69 
    70 	/* one could wait for multiple topics with this technique, just using one here */
    71 	px4_pollfd_struct_t fds[] = {
    72 		{ .fd = sensor_sub_fd,   .events = POLLIN },
    73 		/* there could be more file descriptors here, in the form like:
    74 		 * { .fd = other_sub_fd,   .events = POLLIN },
    75 		 */
    76 	};
    77 
    78 	int error_counter = 0;
    79 
    80 	for (int i = 0; i < 5; i++) {
    81 		/* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
    82 		int poll_ret = px4_poll(fds, 1, 1000);
    83 
    84 		/* handle the poll result */
    85 		if (poll_ret == 0) {
    86 			/* this means none of our providers is giving us data */
    87 			PX4_ERR("Got no data within a second");
    88 
    89 		} else if (poll_ret < 0) {
    90 			/* this is seriously bad - should be an emergency */
    91 			if (error_counter < 10 || error_counter % 50 == 0) {
    92 				/* use a counter to prevent flooding (and slowing us down) */
    93 				PX4_ERR("ERROR return value from poll(): %d", poll_ret);
    94 			}
    95 
    96 			error_counter++;
    97 
    98 		} else {
    99 
    100			if (fds[0].revents & POLLIN) {
    101				/* obtained data for the first file descriptor */
    102				struct vehicle_acceleration_s accel;
    103				/* copy sensors raw data into local buffer */
    104				orb_copy(ORB_ID(vehicle_acceleration), sensor_sub_fd, &accel);
    105				PX4_INFO("Accelerometer:\t%8.4f\t%8.4f\t%8.4f",
    106					 (double)accel.xyz[0],
    107					 (double)accel.xyz[1],
    108					 (double)accel.xyz[2]);
    109
    110				/* set att and publish this information for other apps
    111				 the following does not have any meaning, it's just an example
    112				*/
    113				att.q[0] = accel.xyz[0];
    114				att.q[1] = accel.xyz[1];
    115				att.q[2] = accel.xyz[2];
    116
    117				orb_publish(ORB_ID(vehicle_attitude), att_pub, &att);
    118			}
    119
    120			/* there could be more file descriptors here, in the form like:
    121			 * if (fds[1..n].revents & POLLIN) {}
    122			 */
    123		}
    124	}
    125
    126	PX4_INFO("exiting");
    

    2.4. px4_simple_app之CMakeList.txt

    px4_add_module() 方法根据模块描述生成静态库。

    • MODULE块是模块的唯一固件名称(按照惯例,模块名称的前缀是src之后的父路径)
    • MAIN块列出了模块的入口点,它将命令注册到 NuttX,以便可以从 PX4 shell 或 SITL 控制台调用它。
    33 px4_add_module(
    34 	MODULE examples__px4_simple_app
    35 	MAIN px4_simple_app
    36	SRCS
    37 		px4_simple_app.c
    38 	DEPENDS
    39 	)
    

    2.5. px4_simple_app之Kconfig

    Kconfig文件里面定义了:

    1. 任务的名称: px4_simple_app
    2. Kconfig符号:EXAMPLES_PX4_SIMPLE_APP
    3. 默认选择:n
    4. 该任务的描述
    1 menuconfig EXAMPLES_PX4_SIMPLE_APP
    2 	bool "px4_simple_app"
    3 	default n
    4 	---help---
    5 		Enable support for px4_simple_app
    

    3. 测试应用(SITL)

    这里就用SITL来做下测试效果。
    注:自己的KakuteF7还有BetaFlight,目前懒得动bootloader。

    由于SITL代码编译和有硬件目标板的Kconfig配置有些差异,无法使用命令:

    $ make px4_sitl menuconfig
    

    我们查下build工程下的boardconfig文件,看下这个文件是否已经选中编译我们的这个任务模块(449行):

    436 #
    437 # examples
    438 #
    439 CONFIG_EXAMPLES_DYN_HELLO=y
    440 CONFIG_EXAMPLES_FAKE_GPS=y
    441 # CONFIG_EXAMPLES_FAKE_GYRO is not set
    442 CONFIG_EXAMPLES_FAKE_IMU=y
    443 CONFIG_EXAMPLES_FAKE_MAGNETOMETER=y
    444 CONFIG_EXAMPLES_FIXEDWING_CONTROL=y
    445 CONFIG_EXAMPLES_HELLO=y
    446 # CONFIG_EXAMPLES_HWTEST is not set
    447 # CONFIG_EXAMPLES_MATLAB_CSV_SERIAL is not set
    448 CONFIG_EXAMPLES_PX4_MAVLINK_DEBUG=y
    449 CONFIG_EXAMPLES_PX4_SIMPLE_APP=y
    450 CONFIG_EXAMPLES_ROVER_STEERING_CONTROL=y
    451 CONFIG_EXAMPLES_UUV_EXAMPLE_APP=y
    452 CONFIG_EXAMPLES_WORK_ITEM=y
    453 # end of examples
    

    整体构建下代码,并运行仿真环境:

    $ make px4_sitl jmavsim
    

    在仿真pxh命令提示下,help看下内部支持的命令是否含有px4_simple_app

    pxh> help
    Builtin Commands:
      actuator_test
      airship_att_control
      airspeed_selector
      attitude_estimator_q
      battery_simulator
      camera_feedback
      camera_trigger
      cdev_test
      commander
      commander_tests
      control_allocator
      controllib_test
      dataman
      dyn
      ekf2
      ex_fixedwing_control
      failure
      fake_gps
      fake_imu
      fake_magnetometer
      flight_mode_manager
      fw_att_control
      fw_autotune_attitude_control
      fw_pos_control_l1
      gimbal
      gps
      gyro_calibration
      gyro_fft
      hello
      hrt_test
      land_detector
      landing_target_estimator
      led_control
      list_files
      list_tasks
      listener
      load_mon
      local_position_estimator
      logger
      mag_bias_estimator
      manual_control
      mavlink
      mavlink_tests
      mc_att_control
      mc_autotune_attitude_control
      mc_hover_thrust_estimator
      mc_pos_control
      mc_rate_control
      mixer
      motor_test
      navigator
      param
      perf
      pwm
      pwm_out_sim
      px4_mavlink_debug
      px4_simple_app
      rc_tests
      rc_update
      replay
      rover_pos_control
      rover_steering_control
      rpm_simulator
      sd_bench
      send_event
      sensor_baro_sim
      sensor_gps_sim
      sensor_mag_sim
      sensors
      shutdown
      sih
      simulator
      sleep
      system_time
      temperature_compensation
      tests
      tone_alarm
      tune_control
      uorb
      uorb_tests
      uuv_att_control
      uuv_example_app
      uuv_pos_control
      ver
      vtol_att_control
      work_item_example
      work_queue
      wqueue_test
    
    

    在仿真pxh命令提示下运行:

    pxh> px4_simple_app
    INFO  [px4_simple_app] Hello Sky!
    INFO  [px4_simple_app] Accelerometer:	  0.0373	 -0.0432	 -9.7909
    INFO  [px4_simple_app] Accelerometer:	  0.0652	 -0.0592	 -9.7908
    INFO  [px4_simple_app] Accelerometer:	  0.0738	 -0.0068	 -9.7628
    INFO  [px4_simple_app] Accelerometer:	  0.0282	 -0.0097	 -9.8224
    INFO  [px4_simple_app] Accelerometer:	  0.0637	 -0.0293	 -9.7856
    INFO  [px4_simple_app] exiting
    

    不过这里有一点可能看不到了,因为orb_publish(ORB_ID(vehicle_attitude), att_pub, &att);混在在很多仿真模拟数据里面了。有兴趣可以研改下代码,将这个ACC的值放大到很大,显示一个差异。

    具体QGroundControl的验证方法可以参考:PX4开发环境搭建–模拟器编译及QGroundControl & RC遥控模拟配置

    4. 参考资料

    【1】uORB Messaging
    【2】First Application Tutorial (Hello Sky)

    展开全文
  • rtthread移植实现uorb

    2020-08-25 01:19:02
      uORB(Micro Object Request Broker,微对象请求代理器)是PX4/Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到...
  • uORB(Micro Object Request Broker,微对象请求代理器)是PX4/Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个...
  • PX4二次开发——uorb订阅 一、写在前面 ​ 我们写了一个一个功能的模块,这些模块并不是独立的。模块之间是有数据传递的,这样才能组合到一起实现飞行控制的目的。那么解决模块之间的数据传递的方式就是通过uorb订阅...
  • 详细讲解uORB机制的原理与应用
  • 1 简介 1.1 PX4/Pixhawk的软件体系结构 PX4/Pixhawk的软件体系结构主要被分为四个层次,这可以让我们更好的理解PX4/Pixhawk的软件架构和运作: 应用程序的API:这个接口提供给应用程序开发...uORB(Micro Object Requ
  • Nuttx下移植uorb笔记

    千次阅读 2018-07-28 16:07:39
    Nuttx下移植uorb笔记 之前接触过ros下的消息机制(生产者/消费者)模型,第一感觉是灵活好用,但是在资源有限的嵌入式环境里面,邮箱、消息…显得就有点不那么灵活,后来开发飞控逐渐了解到了nuttx以及uorb发现了...
  • 关于ORB_ID这个宏的含义: 应用程序如果用到一个主题,比如px4的官方用例px4_simple_app.c中,一般都需要include 2个文件: #include #include uORB.h是在platforms/common/uORB目录中,是编译前就存在的,是uORB...
  • UORB 理解与总结

    千次阅读 2019-04-02 21:27:31
    UORB是用来在各个进程之间进行通信的,分为如下几个步骤: 发布数据 1,首先公告主题: orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data) 相当于注册,只能有一次 2,发布数据: int...
  • 在PX4中,uorb是用于无人机模块间通信的协议机制。 这篇博客对于uorb编程api的一些基本函数介绍的很好https://blog.csdn.net/freeape/article/details/46880637 px4官网中提供了一个uorb自定义的教程,并不是十分...
  • 一天精通无人机第 19 讲 中级篇系列:uORB原理与使用 原创 怪蛙 编程外星人 2018-09-26 飞控程序中内部消息传递采用的是异步消息机制uORB。它的设计理念很有趣,它可以实现不同模块中的数据快速通讯,并且以...
  • PX4-Autopilot/msg/ 这个目录下定义了所有uORB消息的格式
  • uORB和MAVLink通讯例程

    2021-08-26 09:39:58
    uORB uORB 是一种异步 publish()/subscribe() 的消息传递 API,用于进程或者线程间通信(IPC)。 添加新的Topic(主题) 在msg/目录下创建一个新的.msg文件,并将文件名添加到msg/CMakeLists.txt中。 若在代码中要...
  • 那就要说到uORB了,uORB 是一个异步/消息传递 API,用于线程间/进程间通信。接下来就具体讲讲uORBuORB的原理和优点 原理: 简单点来讲,uORB的机制就是多个进程打开同一个设备文件,进程间通过此文件节点进行...
  • Ubuntu16.04下PX4环境快速搭建及uORB通信机制 勤奋比天赋更重要 2017-09-02 22:22:01 6257 收藏 10 分类专栏: PX4研究笔记 版权 Ubuntu16.04下的环境搭建 之前搭建PX4环境常常编译不通,cmake,gcc,以及交叉...
  • 参考自: PX4/Pixhawk—uORB深入理解和应用. 修改部分内容和格式 1 int poll(struct pollfd fds[], nfds_t nfds, int timeout) /** * @brief 监控文件描述符(多个); timemout=0,poll()函数立即返回而不阻塞...
  • distance uint32 tof_phase uint32 tof_amp uint16 tof_mode 在Firmware/msg/CMakeLists.txt中添加一行 test_tof.msg 在Firmware目录下编译 make px4_fmu-v5_default 在Firmware/build/px4_fmu-v5_default/uORB/...
  • PX4:【uORB通讯机制】

    2020-06-28 14:26:44
    uORB: (Micro Object Request Broker ) [PX4进程间的通讯机制:多对多的信息发布与订阅方式] 发布消息: 1. 公告 advertise: 相当于初始化,在发布消息之前需要对主题(topic)进行公告,一个topic只公告一次 ...
  • PX4-3-uORB

    2021-09-11 20:40:59
    uORB(Micro Object Request Broker,微对象请求代理器)是PX4中非常重要且关键的一个模块,用于各个模块之间的数据交互。实际上uORB是一套跨「进程」 的IPC通讯模块。在PX4中, 所有的功能被独立以进程模块为单位进行...
  • Pixhawk--uORB深入学习

    2018-07-19 14:22:21
    注:本文转载自 博主:FreeApe 博客:《PX4/Pixhawk---uORB深入理解和应用》 The Instructions of uORB 『PX4/Pixhawk』   『软件体系结构』 『uORB』 『主题发布』 『主题订阅』 1 简介 1.1 PX4/...
  • 在前一篇笔记中使用uORB完成消息传递,实现了一个简单示例程序,本文将对uORB进行系统学习。 uORB是一种异步发布(publish)/订阅(subscribe)机制的消息API,该机制用于在线程/进程之间通信。uORB在其他程序启动之前...
  • 之前我们已经谈到系统框架,之前谈到了定制自己功能的两部:添加模块和修改信息流。   鉴于信息流的重要性,我们在这里重新复习整理一下:看完之后你应该做到三点: ... uORB(Micro Object Reques...
  • 前面主要给出和。这里主要来看下uORB消息管理模块是如何对uORB消息进行管理的。
  • 此时,我想起了PX4的uORB(Micro Object Request Broker,微对象请求代理器)。     uORB本质上是一种发布订阅模式,什么是发布订阅模式?发布订阅模式:假设存在老师T(teacher),黑板B(blackboard),同学S(student)....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 825
精华内容 330
关键字:

uorb