PX4/Pixhawk---uORB深入理解和应用
The Instructions of uORB
『PX4/Pixhawk』???『軟件體系結(jié)構(gòu)』?『uORB』?『主題發(fā)布』?『主題訂閱』
1 簡介
1.1 PX4/Pixhawk的軟件體系結(jié)構(gòu)
?PX4/Pixhawk的軟件體系結(jié)構(gòu)主要被分為四個層次,這可以讓我們更好的理解PX4/Pixhawk的軟件架構(gòu)和運(yùn)作:
- 應(yīng)用程序的API:這個接口提供給應(yīng)用程序開發(fā)人員,此API旨在盡可能的精簡、扁平及隱藏其復(fù)雜性。
- 應(yīng)用程序框架: 這是為操作基礎(chǔ)飛行控制的默認(rèn)程序集(節(jié)點)。
- 庫: 這一層包含了所有的系統(tǒng)庫和基本交通控制的函數(shù)。
- 操作系統(tǒng): 最后一層提供硬件驅(qū)動程序,網(wǎng)絡(luò),UAVCAN和故障安全系統(tǒng)。
?
??uORB(Micro Object Request Broker,微對象請求代理器)是PX4/Pixhawk系統(tǒng)中非常重要且關(guān)鍵的一個模塊,它肩負(fù)了整個系統(tǒng)的數(shù)據(jù)傳輸任務(wù),所有的傳感器數(shù)據(jù)、GPS、PPM信號等都要從芯片獲取后通過uORB進(jìn)行傳輸?shù)礁鱾€模塊進(jìn)行計算處理。實際上uORB是一套跨「進(jìn)程」 的IPC通訊模塊。在Pixhawk中, 所有的功能被獨立以進(jìn)程模塊為單位進(jìn)行實現(xiàn)并工作。而進(jìn)程間的數(shù)據(jù)交互就由為重要,必須要能夠符合實時、有序的特點。?
??Pixhawk使用的是NuttX實時ARM系統(tǒng),uORB實際上是多個進(jìn)程打開同一個設(shè)備文件,進(jìn)程間通過此文件節(jié)點進(jìn)行數(shù)據(jù)交互和共享。進(jìn)程通過命名的「總線」交換的消息稱之為「主題」(topic),在Pixhawk 中,一個主題僅包含一種消息類型,通俗點就是數(shù)據(jù)類型。每個進(jìn)程可以「訂閱」或者「發(fā)布」主題,可以存在多個發(fā)布者,或者一個進(jìn)程可以訂閱多個主題,但是一條總線上始終只有一條消息。
1.2 PX4/Pixhawk應(yīng)用程序框架
??應(yīng)用層中操作基礎(chǔ)飛行的應(yīng)用之間都是隔離的,這樣提供了一種安保模式,以確?;A(chǔ)操作獨立的高級別系統(tǒng)狀態(tài)的穩(wěn)定性。而溝通它們的就是uORB。
2 uORB文件夾說明
2.1 uORB文件夾結(jié)構(gòu)
2.2 文件/目錄說明
topics : 系統(tǒng)通用接口定義的標(biāo)準(zhǔn)主題,比如電池電量轉(zhuǎn)態(tài)、GPS的位置參數(shù)等?
module.mk : uORB模塊makefile文件?
objects_common.cpp: 通用接口標(biāo)準(zhǔn)主題定義集合,如添加新主題在這里定義?
ORBMap.hpp : 對象請求器節(jié)點鏈表管理(驅(qū)動節(jié)點)?
ORBSet.hpp : 對象請求器節(jié)點管理(非驅(qū)動節(jié)點)?
Publication.cpp : 在不同的發(fā)布中遍歷使用?
Publication.hpp : 在不同的發(fā)布中遍歷使用?
Subscription.cpp : 在不同的訂閱中遍歷使用?
Subscription.hpp : 在不同的訂閱中遍歷使用?
uORB.cpp : uORB的實現(xiàn)?
uORB.h : uORB頭文件?
uORBCommon.hpp : uORB公共部分變量定義實現(xiàn)?
uORBCommunicator.hpp : 遠(yuǎn)程訂閱的接口實現(xiàn),實現(xiàn)了對不同的通信通道管理,如添加/移除訂閱者,可以基于TCP/IP或fastRPC;傳遞給通信鏈路的實現(xiàn),以提供在信道上接收消息的回調(diào)。?
uORBDevices.hpp :?
uORBDevices_nuttx.cpp : 節(jié)點操作,close,open,read,write?
uORBDevices_nuttx.hpp :?
uORBDevices_posix.cpp :?
uORBDevices_posix.hpp :?
uORBMain.cpp : uORB入口?
uORBManager.hpp : uORB功能函數(shù)實現(xiàn)頭文件?
uORBManager_nuttx.cpp : uORB功能函數(shù)實現(xiàn)(Nuttx)?
uORBManager_posix.cpp : uORB功能函數(shù)實現(xiàn)(Posix)?
uORBTest_UnitTest.cpp : uORB測試?
uORBTest_UnitTest.hpp : uORB測試頭文件,包括主題定義和聲明等?
uORBUtiles.cpp :?
uORBUtiles.hpp :
3 常用函數(shù)功能解析
int poll(struct pollfd fds[], nfds_t nfds, int timeout)
功能:監(jiān)控文件描述符(多個); 說明:timemout=0,poll()函數(shù)立即返回而不阻塞;timeout=INFTIM(-1),poll()會一直阻塞下去,直到檢測到return > 0; 參數(shù):fds:struct pollfd結(jié)構(gòu)類型的數(shù)組;nfds:用于標(biāo)記數(shù)組fds中的結(jié)構(gòu)體元素的總數(shù)量;timeout:是poll函數(shù)調(diào)用阻塞的時間,單位:毫秒; 返回值:>0:數(shù)組fds中準(zhǔn)備好讀、寫或出錯狀態(tài)的那些socket描述符的總數(shù)量;==0:poll()函數(shù)會阻塞timeout所指定的毫秒時間長度之后返回;-1:poll函數(shù)調(diào)用失敗;同時會自動設(shè)置全局變量errno;int orb_subscribe(const struct orb_metadata *meta)
功能:訂閱主題(topic); 說明:即使訂閱的主題沒有被公告,但是也能訂閱成功;但是在這種情況下,卻得不到數(shù)據(jù),直到主題被公告; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值; 返回值:錯誤則返回ERROR;成功則返回一個可以讀取數(shù)據(jù)、更新話題的句柄;如果待訂閱的主題沒有定義或聲明則會返回-1,然后會將errno賦值為ENOENT; eg:int fd = orb_subscribe(ORB_ID(topicName));int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
功能:從訂閱的主題中獲取數(shù)據(jù)并將數(shù)據(jù)保存到buffer中; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;handle:訂閱主題返回的句柄;buffer:從主題中獲取的數(shù)據(jù); 返回值:返回OK表示獲取數(shù)據(jù)成功,錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:struct 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)
功能:公告發(fā)布者的主題; 說明:在發(fā)布主題之前是必須的;否則訂閱者雖然能訂閱,但是得不到數(shù)據(jù); 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;data:指向一個已被初始化,發(fā)布者要發(fā)布的數(shù)據(jù)存儲變量的指針; 返回值:錯誤則返回ERROR;成功則返回一個可以發(fā)布主題的句柄;如果待發(fā)布的主題沒有定義或聲明則會返回-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);int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
功能:發(fā)布新數(shù)據(jù)到主題; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;handle:orb_advertise函數(shù)返回的句柄;data:指向待發(fā)布數(shù)據(jù)的指針; 返回值:OK表示成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg: orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);int orb_set_interval(int handle, unsigned interval)
功能:設(shè)置訂閱的最小時間間隔; 說明:如果設(shè)置了,則在這間隔內(nèi)發(fā)布的數(shù)據(jù)將訂閱不到;需要注意的是,設(shè)置后,第一次的數(shù)據(jù)訂閱還是由起初設(shè)置的頻率來獲取, 參數(shù):handle:orb_subscribe函數(shù)返回的句柄;interval:間隔時間,單位ms; 返回值:OK表示成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:orb_set_interval(sensor_sub_fd, 1000);orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)
功能:設(shè)備/驅(qū)動器的多個實例實現(xiàn)公告,利用此函數(shù)可以注冊多個類似的驅(qū)動程序; 說明:例如在飛行器中有多個相同的傳感器,那他們的數(shù)據(jù)類型則類似,不必要注冊幾個不同的話題; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;data:指向一個已被初始化,發(fā)布者要發(fā)布的數(shù)據(jù)存儲變量的指針;instance:整型指針,指向?qū)嵗腎D(從0開始);priority:實例的優(yōu)先級。如果用戶訂閱多個實例,優(yōu)先級的設(shè)定可以使用戶使用優(yōu)先級高的最優(yōu)數(shù)據(jù)源; 返回值:錯誤則返回ERROR;成功則返回一個可以發(fā)布主題的句柄;如果待發(fā)布的主題沒有定義或聲明則會返回-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);int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance)
功能:訂閱主題(topic); 說明:通過實例的ID索引來確定是主題的哪個實例; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;instance:主題實例ID;實例ID=0與orb_subscribe()實現(xiàn)相同; 返回值:錯誤則返回ERROR;成功則返回一個可以讀取數(shù)據(jù)、更新話題的句柄;如果待訂閱的主題沒有定義或聲明則會返回-1,然后會將errno賦值為ENOENT; eg:int sfd1 = orb_subscribe_multi(ORB_ID(orb_multitest), 1);int orb_unsubscribe(int handle)
功能:取消訂閱主題; 參數(shù):handle:主題句柄; 返回值:OK表示成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:ret = orb_unsubscribe(handle);int orb_check(int handle, bool *updated)
功能:訂閱者可以用來檢查一個主題在發(fā)布者上一次更新數(shù)據(jù)后,有沒有訂閱者調(diào)用過ob_copy來接收、處理過; 說明:如果主題在在被公告前就有人訂閱,那么這個API將返回“not-updated”直到主題被公告。可以不用poll,只用這個函數(shù)實現(xiàn)數(shù)據(jù)的獲取。 參數(shù):handle:主題句柄;updated:如果當(dāng)最后一次更新的數(shù)據(jù)被獲取了,檢測到并設(shè)置updated為ture; 返回值:OK表示檢測成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:if (PX4_OK != orb_check(sfd, &updated)) {return printf("check(1) failed");}if (updated) {return printf("spurious updated flag");}//orbool 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);}int orb_stat(int handle, uint64_t *time)
功能:訂閱者可以用來檢查一個主題最后的發(fā)布時間; 參數(shù):handle:主題句柄;time:存放主題最后發(fā)布的時間;0表示該主題沒有發(fā)布或公告; 返回值:OK表示檢測成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:ret = orb_stat(handle,time);int orb_exists(const struct orb_metadata *meta, int instance)
功能:檢測一個主題是否存在; 參數(shù):meta:uORB元對象,可以認(rèn)為是主題id,一般是通過ORB_ID(主題名)來賦值;instance:ORB 實例ID; 返回值:OK表示檢測成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:ret = orb_exists(ORB_ID(vehicle_attitude),0);int orb_priority(int handle, int *priority)
功能:獲取主題優(yōu)先級別; 參數(shù):handle:主題句柄;priority:存放獲取的優(yōu)先級別; 返回值:OK表示檢測成功;錯誤返回ERROR;否則則有根據(jù)的去設(shè)置errno; eg:ret = orb_priority(handle,&priority);4 例程
4.1 例程前準(zhǔn)備工作
-
archives已編譯完成(注:2015/10/6號后改為cmake編譯系統(tǒng),不再需要編譯archives了);
-
添加一個新的模塊
- 在Firmware/src/modules中添加一個新的文件夾,命名為px4_simple_app
- 在px4_simple_app文件夾中創(chuàng)建module.mk文件,并輸入以下內(nèi)容:?
- MODULE_COMMAND = px4_simple_app
- SRCS = px4_simple_app.c
- 在px4_simple_app文件夾中創(chuàng)建px4_simple_app.c文件
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 注冊新添加的應(yīng)用到NuttShell中,并編譯上傳?
- Firmware/makefiles/config_px4fmu-v2_default.mk文件中添加如下內(nèi)容:?
- MODULES += modules/px4_simple_app
- 編譯?
- make clean
- make px4fmu-v2_default
- 上傳到板子中?
- make upload px4fmu-v2_default
- Firmware/makefiles/config_px4fmu-v2_default.mk文件中添加如下內(nèi)容:?
- 在QGC 中的Terminal(終端)中運(yùn)行新應(yīng)用?
- nsh > px4_simple_app
?接下來的代碼修改均是基于此應(yīng)用。
4.2 訂閱主題
?sensor_combined主題是官方提供的通用接口標(biāo)準(zhǔn)主題。
/*** @file px4_simple_app.c* Minimal application example for PX4 autopilot*/#include <nuttx/config.h> #include <unistd.h> #include <stdio.h> #include <poll.h>#include <uORB/uORB.h> #include <uORB/topics/sensor_combined.h>__EXPORT int px4_simple_app_main(int argc, char *argv[]);int px4_simple_app_main(int argc, char *argv[]) {printf("Hello Sky!\n");/*訂閱sensor_combined 主題*/int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));/*一個應(yīng)用可以等待多個主題,在這里只等待一個主題*/struct pollfd fds[] = {{ .fd = sensor_sub_fd, .events = POLLIN },/* 這里可以添加更多的文件描述符;* { .fd = other_sub_fd, .events = POLLIN },*/};int error_counter = 0;while (true) {/*poll函數(shù)調(diào)用阻塞的時間為1s*/int poll_ret = poll(fds, 1, 1000);/*處理poll返回的結(jié)果 */if (poll_ret == 0) {/* 這表示時間溢出了,在1s內(nèi)沒有獲取到發(fā)布者的數(shù)據(jù) */printf("[px4_simple_app] Got no data within a second\n");} else if (poll_ret < 0) {/* 出現(xiàn)問題 */if (error_counter < 10 || error_counter % 50 == 0) {/* use a counter to prevent flooding (and slowing us down) */printf("[px4_simple_app] ERROR return value from poll(): %d\n", poll_ret);}error_counter++;} else {if (fds[0].revents & POLLIN) {/*從文件描述符中獲取訂閱的數(shù)據(jù)*/struct sensor_combined_s raw;/* copy sensors raw data into local buffer */orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);printf("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f\n",(double)raw.accelerometer_m_s2[0],(double)raw.accelerometer_m_s2[1],(double)raw.accelerometer_m_s2[2]);}/* 如果有更多的文件描述符,可以這樣:* if (fds[1..n].revents & POLLIN) {}*/}}return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
4.3 訂閱和發(fā)布主題
?sensor_combined主題是官方提供的通用接口標(biāo)準(zhǔn)主題。?
?vehicle_attitude主題是官方提供的通用接口標(biāo)準(zhǔn)主題。
?程序流程圖如下:
/*** @file px4_simple_app.c* Minimal application example for PX4 autopilot*/#include <nuttx/config.h> #include <unistd.h> #include <stdio.h> #include <poll.h>#include <uORB/uORB.h> #include <uORB/topics/sensor_combined.h> #include <uORB/topics/vehicle_attitude.h>__EXPORT int px4_simple_app_main(int argc, char *argv[]);int px4_simple_app_main(int argc, char *argv[]) {printf("Hello Sky!\n");/* 訂閱 sensor_combined 主題 */int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));orb_set_interval(sensor_sub_fd, 1000);/* 公告 attitude 主題 */struct vehicle_attitude_s att;memset(&att, 0, sizeof(att));int att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att);/*一個應(yīng)用可以等待多個主題,在這里只等待一個主題*/struct pollfd fds[] = {{ .fd = sensor_sub_fd, .events = POLLIN },/* there could be more file descriptors here, in the form like:* { .fd = other_sub_fd, .events = POLLIN },*/};int error_counter = 0;while (true) {/* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */int poll_ret = poll(fds, 1, 1000);/* handle the poll result */if (poll_ret == 0) {/* this means none of our providers is giving us data */printf("[px4_simple_app] Got no data within a second\n");} 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) */printf("[px4_simple_app] ERROR return value from poll(): %d\n", poll_ret);}error_counter++;} else {if (fds[0].revents & POLLIN) {/* obtained data for the first file descriptor */struct sensor_combined_s raw;/* copy sensors raw data into local buffer */orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);printf("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f\n",(double)raw.accelerometer_m_s2[0],(double)raw.accelerometer_m_s2[1],(double)raw.accelerometer_m_s2[2]);/* 賦值 att 并且發(fā)布這些數(shù)據(jù)給其他的應(yīng)用 */att.roll = raw.accelerometer_m_s2[0];att.pitch = raw.accelerometer_m_s2[1];att.yaw = raw.accelerometer_m_s2[2];orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);}/* there could be more file descriptors here, in the form like:* if (fds[1..n].revents & POLLIN) {}*/}}return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
4.4 創(chuàng)建自己的主題
??官方提供的通用接口標(biāo)準(zhǔn)主題都放在了topics文件夾下了。如果要定義我們自己的主題,比如我們新添加了超聲波傳感器,為了將超聲波傳感器的數(shù)據(jù)發(fā)布出去給其他需要的應(yīng)用訂閱,那么久需要創(chuàng)建我們的主題了。
- 主題頭文件(mytopic.h)?
- ORB_DECLARE(myTopicName);//聲明一個主題
- 定義一個存放發(fā)布數(shù)據(jù)的結(jié)構(gòu)體;
- 主題源文件(mytopic.c)?
- ORB_DEFINE(myTopicName);//定義一個主題
- 初始化發(fā)布數(shù)據(jù)
- 公告主題
- 發(fā)布主題數(shù)據(jù)
mytopic.h
/* 聲明自定義主題,名字可以自定義,不過最好具有一定的意義,如下為隨機(jī)產(chǎn)生整數(shù)數(shù)據(jù) */ ORB_DECLARE(random_integer);/* 定義要發(fā)布的數(shù)據(jù)結(jié)構(gòu)體 */ struct random_integer_data {int r; };- 1
- 2
- 3
- 4
- 5
- 6
- 7
mytopic_publish.c
#include <topic.h>/* 定義主題 */ ORB_DEFINE(random_integer);/* 待發(fā)布的主題句柄 */ static int topic_handle;int init() {/* 隨機(jī)產(chǎn)生一個數(shù)初始化數(shù)據(jù)結(jié)構(gòu)體 */struct random_integer_data rd = { .r = random(), };/* 公告主題 */topic_handle = orb_advertise(ORB_ID(random_integer), &rd); }int update_topic() {/* 產(chǎn)生新的數(shù)據(jù) */struct random_integer_data rd = { .r = random(), };/* 發(fā)布主題,更新數(shù)據(jù) */orb_publish(ORB_ID(random_integer), topic_handle, &rd); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
??對于訂閱者來說,就可以參考主題「4.2 訂閱例程」了。不過這里還是提供下簡單處理例程:
mytopic_subscriber.c
#include <topic.h>/* 訂閱主題的句柄*/ static int topic_handle;int init() {/* 訂閱主題 */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);} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
5 參考資料
??http://www.pixhawk.com/start?id=zh/dev/px4_simple_app?
??http://www.pixhawk.com/dev/shared_object_communication?
??http://blog.arm.so/armteg/pixhawk/183-0503.html?
??http://pixhawk.org/start?id=dev/software_architecture?
??http://www.pixhawk.com/dev/add_uorb_topic?s[]=objects&s[]=common
總結(jié)
以上是生活随笔為你收集整理的PX4/Pixhawk---uORB深入理解和应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NuttX 启动流程
- 下一篇: MAX232和PL2303、CH340的