1. 程式人生 > >pixhawk自學筆記之uorb學習總結

pixhawk自學筆記之uorb學習總結

注:這是看過好多文章總結出來的,轉載了較多人的部落格,希望有知道原出處的人把地址留下,我貼上來。在此謝謝各位前輩的總結。(我會在後續筆記中貼出在我自己的程式中對於uorb的使用)

程序與應用程式(感測器應用程式傳送感測器資料到姿態過濾應用程式)之間的通訊是pixhawk軟體架構的重要組成部分,程序(即所謂的節點)通過命名的匯流排交換訊息稱之為“主題”,在pixhawk中,一個主題僅包含一種訊息型別,例如:vehicle_attitude主題傳輸包含姿態結構(翻滾,俯仰和偏航估算)。節點可以在匯流排,主題上釋出一跳訊息或者訂閱匯流排,主題。通訊雙方之間並不知道在與誰通訊,可以存在多個釋出或一條訊息有多個訂閱者。這種設計模式可以防止鎖定的問題。

pixhawk的釋出訂閱機制是通過“微物件請求代理”(uORB)來實現的。

快速入門:

在深入細節之前,以下是一對簡單,完整的釋出/訂閱模型。釋出者釋出一條名為“random——integer”的主題並用隨機整數更新該主題。訂閱者檢查並列印這些更新。

topic.h
/* declare the topic */
ORB_DECLARE(random_integer);/* define the data structure that will be published where subscribers can see it */
struct random_integer_data {int r;};

publisher.c
#include <topic.h>/* create topic metadata */
ORB_DEFINE(random_integer);/* file handle that will be used for publishing */
static int topic_handle;
int init(){/* generate the initial data for first publication */
	struct random_integer_data rd = { .r = random(), };/* advertise the topic and make the initial publication */
	topic_handle = orb_advertise(ORB_ID(random_integer), &rd);
}
int update_topic(){/* generate a new random number for publication */
	struct random_integer_data rd = { .r = random(), };/* publish the new data structure */
	orb_publish(ORB_ID(random_integer), topic_handle, &rd);
}

subscriber.c
#include <topic.h>/* file handle that will be used for subscribing */
static int topic_handle;
int init(){/* subscribe to the topic */
	topic_handle = orb_subscribe(ORB_ID(random_integer));
}
void check_topic(){
	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);
		}
}

釋出:

    釋出分為三個獨立但又相關的行為;確定主題,公告主題和釋出主題更新。

確定主題:

    pixhawk系統為提供部件之前的通用介面定義了許多標準主題,如果釋出者想使用標準主題和相關的資料結構不需要做額外的工作。

自定義主題

    要定義一個自定義主題,釋出者需要提供給訂閱者一個頭檔案(參考上面的topic.h),在這個標頭檔案中必須有:

        1.用主題的名稱做作為引數呼叫ORB_DECLARE()巨集來定義一個例項

        2.定義一個結構體,用來描述將要用來發布的資料結構

主題的名稱應該要具有描述性,pixhawk的管理使用下劃線來分割主題名稱為獨立的部分並且首選更通用的術語表示元件的名稱。

    例如:raw sensor data釋出在sensors_raw主題

除了標頭檔案,釋出者必須要具有使用ORB_DEFINE()巨集在原始碼中定義一個例項,當韌體被構建時,他將被編譯並且連結到韌體。

可選主題:

        如果一個主題通過一個軟體元件來發布,那麼它屬於可選主題,並且可能不會存在於釋出後的韌體,這種情況下,標頭檔案也可以改用ORB_DECLARE_OPTIONAL()巨集來替代,以這種方式宣告主題,釋出者不需要專門來處理什麼。但在下面討論的也有額外要處理的情況,當處理可選主題時訂閱者必須要注意。

公告主題:

        在資料被髮布到一個主題前,它必須被公告,釋出者可以使用下面的API來公告一個新的主題。

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

    公告也可以釋出初始化資料到主題,meta引數是傳遞給API的一個指標,指向由ORB_DEFINE()巨集定義好的資料,通常使用ORB_ID()巨集來根據主題名稱獲取該指標。請注意,雖然主題更新可以從中斷處理函式釋出,公告主題必須在常規的執行緒上下文中執行。

多個釋出:

        只有一個釋出者可以具有釋出一次一個主題,但是該主題手柄可以被關閉,因為是檔案描述符,可以通過close()函式關閉。 

釋出更新:

        一旦公告了一個主題,公告主題後返回的控制代碼可使用下面的API來發布主題更新。

    extern int orb_publish(const struct orb_metadata *meta, int handle, const void *data);
UORB不換衝多個更新,當用戶檢查一個主題,他們將只能看到最新的更新。

訂閱者:

        訂閱主題的要求如下:

                1.呼叫ORB_DEFINE()或ORB_DEFINE_OPTIONAL()巨集(在訂閱者的標頭檔案中包含他們)

                2.釋出到主題的資料結構定義(通常與釋出者使用同一標頭檔案)

        如果滿足上面的條件後,訂閱者可以使用下面的api來訂閱一個主題:

    extern int orb_subscribe(const struct orb_metadata *meta);

 如果可選主題不存在於韌體之中,訂閱到可選的主題將會失敗,但其他主題即便釋出者沒有進行公告也會訂閱成功,這樣可大大降低系統對啟動順序的安排。

        這裡沒有專門來限制一個任務的最大訂閱數。

        要取消訂閱一個主題,可以用下面的API:

    extern int orb_unsubscribe(int handle);

拷貝資料到主題:

        訂閱者不能引用ORB中儲存的資料或其他訂閱共享的資料,而是在訂閱者請求時從ORB拷貝資料到訂閱者的臨時緩衝區。副本拷貝的方式可以避免鎖定ORB的問題,並保持兩者之間(釋出者,訂閱者)的API介面簡單。它也允許訂閱者在必要的時候直接修改拷貝副本的資料供自己使用。

        當訂閱者想要把主題中的最新資料拷貝一份全新的副本,可以使用:

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

 拷貝是以原子操作進行的,所以可以保證獲取到釋出者最新的資料。

檢查更新:

        訂閱者可以使用下面的API來檢查一個主題在釋出者最後更新後,有沒有人呼叫過orb_copy來接收,處理:

    extern int orb_check(int handle, bool *updated);      

   如果主題在被公告前就有人訂閱,那麼這個API將返回“not-updated”直到主題被公告。

釋出時間戳:

        訂閱者可以使用下面的API來檢查一個主題最後釋出的時間。

    extern int orb_stat(int handle, uint64_t *time);
需要注意的是,要小心的使用這個呼叫,因為不能保證再呼叫返回後不久主題就不會被髮布(呼叫返回後不久,主題可能馬上又被髮布,導致最後更新時間錯誤)

uORB的管理羅輯是通過建立執行緒後臺執行方式實現。

uORB深入探索

        uORB是pixhawk系統中非常重要的一個模組,它肩負了整個系統的資料傳輸任務,所有的感測器資料,GPS,ppm訊號等都要從晶片獲取後通過uORB進行傳輸到各個模組進行計算處理。

        1.uORB的架構簡述:

         uORB是一套跨程序的IPC通訊模組。在pixhawk中,所有的功能被獨立以程序模組為單位進行實現並工作。而進城間的資料互動尤為重要,必須要能夠符號實時,有序的特點。

        pixhawk使用nuttx實時ARM系統,而uORB對於nuttx而言,它僅僅是一個普通的檔案裝置物件,這個裝置支援open,close,read,write,ioctl以及poll機制。通過這些介面的實現,uORB提供了一套“點對多”的跨程序廣播通訊機制。“點”指的是通訊訊息的“源”,“多”指的是一個源可以有多個使用者來接受,處理。而源和使用者的關係在於,源不需要去考慮使用者是否課餘i收到某條被廣播的訊息或什麼時候收到這條訊息。它只是需要單純的把要廣播的資料推送到uORB的訊息總線上,對於使用者而言,源推送了多少次的小心也不重要,重要的是取回最新的這條訊息。

        2.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會建立並初始化它的裝置例項,其中的實現大部分都在CDev基類完成。這個過程類似於Linux裝置驅動中的Probe函式,通過init呼叫完成裝置的建立,節點註冊以及派遣例程的設定等。

        原始碼解讀:(最新版本的uORB)

        uORB資料夾說明

        1.uORB資料夾結構

2.檔案/目錄說明

            objects_common.cpp:通用介面標準主題定義集合,如新增新主題就在這裡定義。

            uORBMap.hpp:物件請求節點連結串列管理(驅動節點)

            uORBSet.hpp:物件請求節點連結串列管理(非驅動節點)

           Publication.cpp/ Publication.hpp:在不同的釋出中遍歷使用

           Subscription.cpp/ Subscription.hpp:在不同的釋出中遍歷使用

            uORB.cpp:uORB的實現

            uORB.h:uORB的標頭檔案

            uORBCommon.hpp:uORB公共部分變數定義實現

            uORBCommunicator.hpp:遠端訂閱的介面實現,實現了對不同的通訊通道管理,如新增、移除訂閱者,可以基於TCP/IP或者fastRPC;傳遞給通訊鏈路的實現,以提供在通道上接收資訊的回撥。

            uORBDevices_nuttx.cpp:節點操作,close,open,read,write等

            uORbMain.cpp:uORB入口

            uORBManager.hpp:uORB功能函式實現的標頭檔案

            uORBManager_nuttx.cpp:uORB功能函式的實現(Nuttx)

           uORBManager_posix.cpp:uORB功能函式的實現(Posix)

           uORBTest_UnitTest.cpp:uORB測試

            uORBTest_UnitTest.hpp:uORB測試標頭檔案,包括主題定義和宣告等