1. 程式人生 > >px4原始碼學習六--uORB模組研究

px4原始碼學習六--uORB模組研究

UORB模組研讀

uORB函式解析:

uORB模組(Micro Object Request Broker,微物件請求代理器)

uORB是Pixhawk系統中關鍵的一個模組,肩負了資料傳輸任務。所有感測器,資料傳輸任務,GPS,PPM訊號從晶片獲取後通過uORB進行傳輸,到各個模組計算處理。(可以理解為資料中心倉庫)

uORB是跨程序的IPC通訊模組。實際多個程序開啟同一裝置檔案,通過此檔案節點進行資料互動和共享。

程序間通過命名(匯流排)交換訊息成為topic

一個topic包含一種訊息型別/資料型別。

每個程序可以訂閱/釋出topic,一個程序可以訂閱多個主題,但一條匯流排始終只能有一條訊息。

公告topic

extern int orb_advertise(const struct orb_metadata *meta,const void *data)

meta:uORB元物件,可以看topic的ID,通過ORB_ID賦值。

data:指向一個被初始化,釋出者要釋出的資料儲存變數指標。

釋出更新

extern int orb_publish(const struct orb_metadata *meta, int handle, const void *data)

handle:orb_advertise函式返回的控制代碼

data:指向釋出資料的製作

訂閱topic

要求的滿足條件:

  • 呼叫ORB_DEFINE()或ORB_DEFINE_OPTIONAL()巨集(在訂閱標頭檔案中包含他們)
  • 釋出到主題的資料結構定義

extern int orb_subscribe(const struct orb_metadata *meta)

取消訂閱

extern int orb_unsubscribe(int handle)

拷貝資料

訂閱者不能引用從ORB中儲存資料或其他訂閱共享的資料,只能拷貝到臨界緩衝區

extern int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)

檢查更新

extern int orb_check(int handle, bool *updataed)

釋出時間戳

extern int orb_stat(int handle,uint64_t *time)

Note:

  1. 必須要先orb_advertise()+orb_subscribe()後,才能使用orb_copy()
  2. 所有主題在Firmware/Build_px4fmu-v2_default/Src_Modules/uORB/Topics
  3. 也可以到Firmware/Msg下找到

uORB原理解析:
uorb實現原理

實現方式:

  1. 首先模組入口是uORBMain.cpp,這裡會建立唯一的MasterNode例項,負責內部的裝置節點的控制
  2. MasterNode簡潔:繼承自CDev,是一個字元裝置,linux中裝置都是檔案,所以會在對應路徑下生成檔案,主要對Cdev的ioctl函式進行多型實現,類內部維護一個Nodemap的資料結構(後面有詳細簡紹),所有的DeviceNode都會加入其中管理。
  3. 當外部呼叫uORB的方法時候,實際間接呼叫了uORB::Manger類的方法,也可以理解做Manger是外部函式的管理入口,MasterNode是內部節點的管理器。
  4. 外部函式呼叫uORB時候都會通過uORB::Manger::get_instance()這個函式獲取Manger的例項指標。這個函式是靜態函式,根據判斷名稱空間中_Instance指標是否為空,確保全nameespace中只有這麼一個Manger例項。
  5. 實際節點,DeviceNode,繼承自CDev,每一個字元裝置都會生成對應的檔案。這個類過載了open,close,read,wirte,ioctl,還有其他一些函式。
  6. 當外部呼叫orb_advertise時,Manger會做如下的事情:
    1. 以釋出者的身份呼叫node_open函式,最後返回對應檔案的檔案描述符fd。先確認一下是否這個主題對應例項的序號已經被髮布過,如果是,會重新發布一下,否則(對應檔案不存在,也就是這個主題對應ID的DeviceNode不存在),會呼叫node_advertise函式進行DeviceNode例項的建立。
    2. (呼叫node_open函式)
    3. 通過fd呼叫ioctl函式,返回DeiceNode物件的指標。
    4. 釋出初始化的資料
  7. 當外部呼叫orb_publish時
    1. 根據傳入的DeviceNode的指標,呼叫這個類的靜態的釋出資料的方法。就是將資料寫入DeviceNode物件的屬性中。
  8. 當外部呼叫orb_Subsctibe時,
    1. 使用Manger類的node_open函式,返回對應主題對應例項的檔案描述符,其他資訊都可以通過呼叫被DeviceNode多型實現過的ioctl函式獲得。
    2. 取消訂閱就將檔案描述符關閉就可以了。

uORB

這個檔案中所有的方法的實現方式都是呼叫ORB::Manger的方法,只是一個擴充套件介面的包裝檔案

因為這個沒有在任何名稱空間或者類中,這裡將具體的ORB::Manger操作封裝,使得其他類可以呼叫

orb_advertise(*meta, *data) orb_advert_t

引數列表含義:主題名稱,初始資料

作為釋出者做廣播,如果正常,返回一個可以用來發布這個主題的控制代碼

orb_advertise(*meta, *data, *instance, priority) orb_advert_t

引數列表含義:主題名稱,初始資料,例項ID的指標,例項優先順序

一個主題下可以有多個例項,每個例項根據handle區分,最多5個

orb_pubshlist_auto(*meta, *handle, *data, *instance, priority) int

引數列表含義:同上

根據handle是否有效釋出資料

orb_publish(*meta, handle, *data) int

引數列表含義:主題名稱,主題的例項控制代碼,釋出的資料

資料釋出是原子操作,任何等待更新的訂閱者都會被通知。其他沒有等待的可以使用orb_check/orb_stat函式檢查更新

orb_subscribe(*meta) int

引數列表含義: 主題名稱

返回檔案描述符,可以使用poll函式等待主題更新,就像使用topic_read/orb_check/orb_stat一樣

即使這個主題沒有釋出訂閱也可能成功,這種情況下,poll,複製它的值什麼的操作都會失敗,直到這個主題被髮布未知。

如果系統不知道這樣一個主題,那麼會訂閱失敗

orb_subscribe_muti(*meta, instance) int

引數列表含義: 主題名稱, 例項ID

orb_unsubscribe(handle) int

引數列表含義:例項控制代碼

orb_copy(*meta, handle, *buffer) int

引數列表含義:主題名稱,例項控制代碼,緩衝區

這是唯一個可以重置內部主題是否更新標誌的函式。一旦使用poll或者orb_check返回可用更新,必須使用這個方法更新引數

orb_check(handle, *updated) int

引數列表含義:例項的控制代碼,是否被更新的標誌

檢查在上次orb_copy後這個主題是否被重新發布過

更新以每個控制代碼為基礎進行跟蹤; 

這個呼叫將一直返回true直到使用相同的控制代碼呼叫orb_copy。

由於stat和copy之間的競爭視窗可能導致錯過更新,所以此介面應優先於呼叫orb_stat。

orb_stat(handle, *time) int

返回上一次更新主題的時間

orb_exists(*meta, instance) int

檢查主題是否存在

orb_group_count(*meta) int

返回主題的例項數量

orb_priority(handle, *priority) int

返回控制代碼的優先順序

orb_set_interval(handle, interval) int

設定最小的更新間隙

uORBCommon

ORBSet

  • insert(*node_node) void
  • find(*node_name) bool
  • erase(*node_name) bool
  • unlinkNext(Node*) void

實現方式與ORBMap一毛一樣,唯一的區別就是這個類維護的佇列中的node節點是隻有節點名字,沒有節點裝置。

ORBMap

  • insert(*node_name, *node) void
  • find(*node_name) bool
  • *get(*node) uORB::DeviceNode
  • unlinkNext(*a) void

類的實現流程:(太簡單了吧,過分)

類中維護一個佇列,使用物件top,end倆個指標標識佇列,

當有新的資料結點,加入到end後面,查詢、獲取是從頭到尾順序查詢佇列,使用strcmp()比較node_name

uORBDevices_nuttx

namespace uORB
{
class DeviceNode;  //具體的節點
class DeviceMaster; //節點管理
}

DeviceNode

繼承device::CDev(一切字元裝置的基類)

DeviceNode(*meta, *name, *path, priority)

open(struct file *filp) int virtual

裝置節點開啟:算是虛擬的裝置檔案雖然繼承與CDev類

根據檔案資訊,讀取許可權:(filp記錄了這次開啟檔案的資訊,比如許可權,PID等資料)

如果檔案寫入許可權,認為是釋出者,裝置節點屬性發布者設為當前PID,使用CDev::open開啟(但實際這個類也沒幹什麼,可以忽略這一句),

如果是讀取許可權,認為是訂閱者,申請SubscriberData給filp->f_priv,記錄這個訂閱物件fd所私有的資料,並且告訴物件屬性又一個訂閱者訂閱了你

close(*filp) int virtual

裝置節點關閉:

如果是釋出者呼叫的關閉,將屬性發布者置為0

如果是訂閱者呼叫,獲取filp中的SubscriberData,從呼叫列表中刪除,移除內部呼叫者

read(*filp, *buffer, buflen) ssize_t virtual

將類中資料_data讀入到buffer中

write(*filp, *buffer, buflen) ssize_t virtual

將buffer的資料讀入到類的_data屬性中

ioctl(*filp, cmd, arg) int virtual

根據cmd的值調整arg的型別,輸出對應資料屬性

publish(*meta, handle, *data) ssize_t static

這是靜態函式,所有例項公用一個

根據傳入的控制代碼(即DeviceNode的指標),將data的資料寫入類的物件屬性_data中

//以下3個函式應該是設計遠端呼叫IChannel介面的,這裡不做討論,因為沒看懂,也沒有示例使用

process_add_subscription(rateInHz) int16_t

process_remove_subscription() int16_t

process_received_message(length, *data) int16_t

將接受的資料寫入_data屬性中

add_internal_subscriber() void

remove_internal_subscriber() void

is_published() bool

//protected:

poll_state(*filp) pollevent_t virtual

判斷主題是否推送給訂閱者,需要推送返回POLLIN,否則0

poll_notify_one(struct pollfd *fds,pollevent_t events )

對於fds來說,如果有更新,呼叫Cdev::poll_notify_one()函式進行更新

appears_updated(SubscriberData *sd) bool

根據sd的資訊,判斷是否有更新(釋出者呼叫這個函式)

update_deferred() void

監控拉的需求

private:
    struct SubscriberData {
        unsigned  generation; /**< last generation the subscriber has seen */
        unsigned  update_interval; /**< if nonzero minimum interval between updates */
        struct hrt_call update_call;  /**< deferred wakeup call if update_period is nonzero */
        void    *poll_priv; /**< saved copy of fds->f_priv while poll is active */
        bool    update_reported; /**< true if we have reported the update via poll/check */
        int   priority; /**< priority of publisher */
    };

    const struct orb_metadata *_meta; /**< object metadata information */
    uint8_t     *_data;   /**< allocated object buffer */
    hrt_abstime   _last_update; /**< time the object was last updated */
    volatile unsigned   _generation;  /**< object generation count */
    pid_t     _publisher; /**< if nonzero, current publisher */
    const int   _priority;  /**< priority of topic */
    bool _published;  /**< has ever data been published */

DeviceMaster

繼承device::CDev,管理裝置節點的類

DeviceMaster(Flavor f)

*GetDeviceNode(*node_name) uORB::DeviceNode

ioctl(*filp, cmd,arg ) int virtual

private:
    Flavor      _flavor;  //是PUBSUB還是OBJ
    static ORBMap _node_map; //所有的DeviceNode裝置都在其中

uORBDevices_posix

nuttx是在px4在nuttx作業系統下執行的,這個應該是裸機執行的。

uORBManager

這個類管理著每一個UORB主題和節點,也實現了UORb的API

繼承自IChannelRxHandler類,類中函式大多都在前面見過,我只大概描述一下實現,如果有必要的話

get_instance() Manager* static

獲取生成的uORBManager例項,靜態的,單個程序中只生成一個例項

orb_advertise(*meta,*data) orb_advert_t

orb_advertise_multi(*meta, *data, *instance, priority) orb_advert_t

根據主題和例項編號獲取檔案描述符,根據檔案描述符獲取當前釋出者的指標並返回釋出者的指標

orb_publish(*meta, handle, *data) int

釋出資料,返回成功與否

實現:

呼叫DevicNode的publish方法,DeviceNode::publish是靜態方法,呼叫handle->write()函式(write會使屬性_generation++,標記資料版本的),將資料寫入檔案中

orb_subscribe(*meta) int

orb_subscribe_multi(*meta, instance) int

訂閱的原理就是開啟以只讀開啟檔案,返回fd

(一個檔案可以被開啟多次,返回的fd都不一樣,fd對應這file *filp這個指標(也就是你在DeivceNode類中引數filp一樣,這個指標的結構體引數比較全),所以可以開啟一次,有多個備份的filp)

orb_unsubscribe(handle) int

orb_copy(*meta, handle, buffer) int

從handle中讀取相應長度的資料到buffer中

orb_check(handle, *updated) int

使用ioctl方法(ioctrl方法有DeviceNode類實現)

這個檢查的辦法是對比filp中維護的SubscriberData資料和物件中屬性generation是否一致,雖然filp有多個,但是檔案(或者說DeviceNode物件只有一個),所以可以進行判斷。(更新版本是在寫入時候generation++)

orb_stat(handle, *time) int

使用ioctl方法

orb_exists(*meta, instance) int

主題的對應例項是否有

根據引數生成路徑,然後試圖開啟對應檔案

orb_priority(handle, *priority) int

使用ioctl方法

orb_set_interval(handle, interval) int

使用ioctl方法

set_uorb_communicator(*comm_channel) void

get_uorb_communicator() IChannel*

is_remote_subscriber_present(*messageName) bool

private:

node_advertise(*meta, *instance, priority) int

通過呼叫控制裝置MasterNode的ioctl函式(被過載過),讓控制檔案去建立對應的DeviceNode物件(有了物件,對應路徑下就有了檔案)

node_open(Flavor, *meta, *data, advertiser, *instance, priority) int

根據訂閱釋出的屬性開啟對應的裝置檔案,返回檔案描述符

這個函式讓你遮蔽是否存在這樣的裝置檔案,根據你的需求去解決。

如果你是訂閱者,存在,直接返回。不存在,那就建立一個,方法同下。

如果你是釋出者,存在,關閉fd。重新開啟一次,(這時node_advertise呼叫將自動進入下一個空閒條目)不存在,呼叫node_advertise釋出建立一個DeviceNode物件(有了物件自然會有檔案)

實現:

判斷輸入的邏輯合法性

根據訂閱者還是釋出者,選擇對應讀取許可權開啟檔案

如果是釋出者&&開啟成功,說明這個檔案存在

關閉檔案重新呼叫node_advertise釋出再開啟檔案

返回檔案描述符fd

process_add_subscription(*messageName, msgRate) int16_t

處理訂閱的回撥函式,Manger類實現的

實現邏輯:

將主體加入訂閱主題的集合中

根據對應資訊獲取路徑,再從裝置管理器DeviceMaster獲取對應的裝置節點

具體裝置節點處理訂閱頻率

process_remove_subscription(*messageName) int16_t

處理移除訂閱的回撥函式,Manger類實現

實現邏輯:

將主題移除主題集合

獲取對應的裝置節點

具體裝置節點處理移除事件

process_received_message(*message, length, *data) int16_t

處理接受資訊的回撥函式,Manger類實現

實現邏輯:

獲取對應的裝置節點

具體的裝置節點處理接受的資訊

    static Manager *_Instance; //全域性唯一的管理器
    // the communicator channel instance.
    uORBCommunicator::IChannel *_comm_channel;
    ORBSet _remote_subscriber_topics;

uORBUtils

工具類Utils中只有2個同名的靜態函式

int node_mkpath(char* buf, Flavor f, const struct orb_metadata *meta, int *instance)
int node_mkpath(char* buf, Flavor f, const char* orbMsgName)

引數含義:

buf:輸出路徑的字串

f:  標誌,是釋出訂閱還是作為引數(PUBSUB/PARAM)

meta: 通過meta->o_name = orbMsgName

instance: 如果是多個例項,對應相應的,否則預設為0

生成路徑為/(obj?param)/orbMsgName/instance ,應該是節點裝置的檔案路徑

uORBCommunicator

namespace uORBCommunicator
{
 //倆個類都為純虛類,可以認為java中的介面類
class IChannel; // 可以理解為這個是客戶端
class IChannelRxHandler; //這個是伺服器端
}

IChannel

啟用遠端訂閱的介面,介面的實現類需要管理通訊通道,快速RPC/TCP/IP

add_subscription(*messageName, msgRateInHz) int16_t

引數列表含義:主題名稱(全域性唯一),最大訊息更新速率

通知有興趣的遠端實體訂閱訊息的介面

remove_subscription(*messageName) int16_t

通知遠端實體移除訂閱的介面

register_handler(IChannelRxHandler *handler) int16_t

註冊訊息控制代碼

send_message(*messageName, length, *data) int16_t

通過通訊連結傳送資料

IChannelRxHandler

通過通訊鏈路回撥的類

process_add_subscription(*messageName, msgRateInHz) int16_t

處理從遠端接受新增的介面函式

process_remove_subscription(*messageName) int16_t

處理遠端移除訂閱的介面

process_received_message(*messageName, length, *data) int16_t

處理遠端傳送資料的類

相關推薦

px4原始碼學習--uORB模組研究

UORB模組研讀 uORB函式解析: uORB模組(Micro Object Request Broker,微物件請求代理器) uORB是Pixhawk系統中關鍵的一個模組,肩負了資料傳輸任務。所有感測器,資料傳輸任務,GPS,PPM訊號從晶片獲取後通過u

px4原始碼學習五--固定翼位置控制模組

fw_pos_control模組 class landingslope 為固定翼著陸的角度變化模組 calulateSlopeValues() void private 更新H1,H0,d1,根據log(H0/H1)的比例調整 d1 / d1+ del

PX4原始碼學習一--Pix和APM的區別

pixhawk是硬體平臺, PX4是pixhawk的原生韌體,專門為pixhawk開發的 APM(Ardupilot Mega)也是硬體 Ardupilot是APM的韌體,所以稱ArduPilot韌體也叫APM 後來APM硬體效能不太夠,所以APM韌體也

Python原始碼學習(四)-builtins模組的初始化

Module的初始化是從系統預定義的PyModuleDef開始的 typedef struct PyModuleDef{ PyModuleDef_Base m_base; const char* m_name; const char* m_doc; Py_

px4原始碼學習三--px4原始碼結構分析

px4原始碼結構分析 Px4原始碼目錄 cmake: 是存放的 Cmake 編輯指令碼資料夾, 其中 Cmake/Configs 是存放的不同硬體的編譯指令碼, nuttx_mindpx-v2_default 是 PIXHAWK 這個硬體的編輯

RocketMQ NameServer模組 原始碼學習

RocketMQ namesrv 模組中 用到了 MixAll 類, 其中有一個 properties2Object(Properties, Object) 通用方法,把properties 轉換成 簡單的 POJO object。 你可以進一步擴充套件:從.properties

nginx 原始碼學習筆記(二十一)—— event 模組(二) ——事件驅動核心ngx_process_events_and_timers

首先繼續回憶下,之前子執行緒執行操作裡面有一個未涉及的內容ngx_process_events_and_timers,今天我們就來研究下這個函式。 本篇文章來自於:http://blog.csdn.net/lengzijian/article/details/7601730 先來看一下第十九

Python學習筆記(十模組

模組是Python程式架構的一個核心概念   模組就好比是工具包,要想使用這個工具包中的工具,就需要匯入import這個模組 每一個以副檔名py結尾的Python原始碼檔案都是一個模組 在模組中定義的全域性變數、函式都是模組能夠提供給外界直接使用的工具 &nb

Pyhton學習筆記模組

為了編寫可維護的程式碼,我們把函式分組,分別放到不同的資料夾裡,在python中,一個.py檔案就稱之為一個模組(Module) 使用模組有什麼好處呢:一是:大大提高了程式碼的可維護性,當一個模組編寫完成以後,就可以在其他地方引用這些模組;二是:使用模組避免函

python3語言學習筆記(模組+輸入輸出)

引入模組 import 模組名     使用時:模組名.函式名(引數) from 模組名 import 函式名 使用時:函式名(引數) import 模組名.* 使用時:函式名(引數) 格式化字串 方式一:使用str.format() 方式二:使用%格式化字串

tensorflow學習筆記——keras模組

本文基於tensorflow官網教程(https://tensorflow.google.cn/guide/keras),機器環境為ubuntu14.04LTS+tensorflow1.8.01.Keras模組配置        Keras模組是tensorflow提供的一個

深度學習結合SLAM的研究思路/成果整理之(一)使用深度學習方法替換SLAM中的模組

整理了部分近兩年深度學習結合SLAM的一些研究成果(參考知乎帖子https://www.zhihu.com/question/66006923 和泡泡機器人公眾號,附上論文連結和已找到的原始碼/資料集連結,大多簡單看了一下摘要。僅為自己學習所用,確實翻譯得很爛…

vue的原始碼學習——6.非同步元件

1 介紹        版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。   

vue的原始碼學習——5.元件註冊

1 介紹        版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。   

vue的原始碼學習——3.patch

1. 介紹        版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  &nbs

vue的原始碼學習——2.createComponent虛擬DOM元件

1. 介紹         版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  &nb

vue的原始碼學習——1.元件化(介紹)

1. 介紹         版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  &nb

libevent原始碼學習研究(libevent-0.1)

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">想學習研究libevent怎麼設計的,學習它的思想,學習它的設計,奈何自

px4原生原始碼學習-(2)--實時作業系統篇

/**************************************************************************************************************   po上我使用到的硬體和開發環境   px4硬體

spring原始碼學習 元素處理過程

概述: </property-placeholder> 主要用來讀取配置資訊替換Bean中的${}佔位符, 常用配置如下: <context:property-placeholder ignore-resource-not-found="