uorb縮寫(xiě)是什么意思,uorb的全稱(chēng)及含義,uorb全稱(chēng)意思大全
uorb縮寫(xiě)是什么意思
UORB英文含義
1、UORB的英文全稱(chēng):Ubiquitous Object Request Broker | 中文意思:───無(wú)處不在的對象請求**
2、UORB的英文全稱(chēng):Upper Ocklawaha River Basin | 中文意思:───克瓦哈河上游流域
arduino如何輸出一個(gè)數組如位置坐標(x,y).不是賦值而是輸出。
可以試一下把println(x,y);改成:
print("("); print(x); print(","); print(y); println(")");
把數組拆分輸出
arduino如何輸出一個(gè)數組如位置坐標(x,y).不是賦值而是輸出。
PX4Firmware
經(jīng)常有人將Pixhawk、PX4、APM還有ArduPilot弄混。這里首先還是簡(jiǎn)要說(shuō)明一下:
Pixhawk是飛控硬件平臺,PX4和ArduPilot都是開(kāi)源的可以燒寫(xiě)到Pixhawk飛控中的自駕儀軟件,PX4稱(chēng)為原生固件,專(zhuān)為Pixhawk打造。APM(Ardupilot Mega)早期也是一款自駕儀硬件,到APM3.0版本,這款基于A(yíng)rduino Mega的自駕儀已經(jīng)走到了它的終點(diǎn)。ArduPilot早期是APM自駕儀的固件,Pixhawk作為APM的升級版,也兼容ArduPilot固件,APM自駕儀卒了之后,ArduPilot現在全面支持Pixhawk,現在大家親切的稱(chēng)ArduPilot固件為APM。
Pixhawk
APM 2.5
PX4
ArduPilot
筆者一直使用的是Pixhawk飛控,研究PX4Firmware。用Source Insight 看代碼是極好的。
PX4固件主要是用C++語(yǔ)言編寫(xiě),真是學(xué)好C++,走遍天下都不怕。使用了NuttX實(shí)時(shí)操作系統,整體軟件架構不可謂不龐大。
不知道如何開(kāi)頭,復述一個(gè)人工智能和機器人領(lǐng)域著(zhù)名的莫拉維克悖論:和傳統假設不同,對計算機而言,實(shí)現邏輯推理等人類(lèi)高級智慧只需要相對很少的計算能力,而實(shí)現感知、運動(dòng)等低等級智慧卻需要巨大的計算資源。
且從系統說(shuō)起吧。
地面站配置的文件應該在芯片flash中,格式化SD卡同時(shí)擦除芯片后配置信息依然存在。
RTFSC
Pixhawk整體邏輯大致為:
commander和navigator產(chǎn)生期望位置
position_estimator估計當前位置
通過(guò)pos_ctrl產(chǎn)生期望姿態(tài)
attitude_estimator估計當前姿態(tài)
通過(guò)att_estimator產(chǎn)生PWM數值
最后通過(guò)mixer和motor_driver控制電機
一直都還只是停留在底層,什么時(shí)候能感受一下ETHz這幫人的成果呢,這才是pixhawk啊。
啟動(dòng)函數
Pixhawk是沒(méi)有main函數的,飛控上電后,會(huì )自動(dòng)執行Firmware/ROMFS/px4fmu_common/init.d文件夾下的rcS 啟動(dòng)腳本(startup script)。這個(gè)腳本位于被編譯到固件中的 ROM文件系統中。這個(gè)腳本檢測可用的硬件, 加載硬件驅動(dòng),并且根據你的設置啟動(dòng)系統正常運行所需的有 app(任務(wù)軟件 ,包括位置和姿態(tài)估計,控制遙測等)。所有屬于自啟動(dòng)程序的腳本文件可以在init.d文件夾中找到。
uORB是Pixhawk系統中非常重要且關(guān)鍵的一個(gè)模塊,它肩負了整數據傳輸任務(wù),所有的感器、 數據傳輸任務(wù)、 GPS、PPM信號等都要從芯片獲取后通過(guò)uORB進(jìn)行傳輸到各個(gè)模塊進(jìn)行計算處理。
uORB 的入口點(diǎn)是 uorb_main函數,在這里它檢查 uORB的啟動(dòng)參數來(lái)完成對應的功能, uORB支持 start/test/status這 3條啟動(dòng)參數,在 PX4的rcS啟動(dòng)腳本中,使用start參數來(lái)進(jìn)行初始化,其他 2個(gè)參數分別用來(lái)進(jìn)行uORB功能的自檢和列出 uORB的當前狀態(tài)。
在rcS中使用 start參數啟動(dòng)uORB后,uORB會(huì )創(chuàng )建并初始化它的設備實(shí)例,其中的實(shí)現大部分都在CDev基類(lèi)完成。
rcS啟動(dòng)順序
extern “C” __EXPORT int main(int agrc, char *agrv[ ]);
argc和argv是main函數的形參,它們是程序的“命令行參數”。agrc(argument count的縮寫(xiě),意思是參數個(gè)數),argv(argument vector的縮寫(xiě),意思是參數向量),它是一個(gè)*char指針數組,數組中每一個(gè)元素指向命令行中的一個(gè)字符串。
main函數是操作系統調用的,實(shí)參只能由操作系統給出。在操作命令狀態(tài)下,實(shí)參是和執行文件的命令一起給出的。例如在DOS、UNIX或Linux等系統的操作命令狀態(tài)下,在命令行中包括了命令名和需要傳給main函數的參數。
命令行的一般形式為:
命令名 參數1 參數2 …… 參數n
命令名和各參數之間用空格分隔。命令名是可執行文件名(此文件包含main函數)。
在rcS執行的時(shí)候,比如attitude_estimator_q_main start
那么agrc就等于2,agrv[0]就是attitude_estimator_q_main這個(gè)字符串,argv[1]就是start。
所以要判斷agrv[1]是start還是stop。
就像你在dos命令行里輸入attitude_estimator_q start,自然就給agrc和agrv[]賦值。NuttX系統下的模塊的主函數名字都是以”_main”開(kāi)始的,但是調用的時(shí)候不加“_main”。
不管了,就地舉個(gè)栗子,還是attitude_estimator_q_main.cpp這個(gè)文件。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495extern "C" _EXPORT int attitude_estimator_q_main(int argc, char *argv[]); …… int attitude_estimator_q_main(int argc, char *argv[]) { if (argc < 1) { warnx("usage: attitude_estimator_q {start|stop|status}"); return 1; } if (!strcmp(argv[1], "start")) { if (attitude_estimator_q::instance != nullptr) { warnx("already running"); return 1; } attitude_estimator_q::instance = new AttitudeEstimatorQ; if (attitude_estimator_q::instance == nullptr) { warnx("alloc failed"); return 1; } if (OK != attitude_estimator_q::instance->start()) { delete attitude_estimator_q::instance; attitude_estimator_q::instance = nullptr; warnx("start failed"); return 1; } return 0; } if (!strcmp(argv[1], "stop")) { if (attitude_estimator_q::instance == nullptr) { warnx("not running"); return 1; } delete attitude_estimator_q::instance; attitude_estimator_q::instance = nullptr; return 0; } if (!strcmp(argv[1], "status")) { if (attitude_estimator_q::instance) { attitude_estimator_q::instance->print(); warnx("running"); return 0; } else { warnx("not running"); return 1; } } warnx("unrecognized command"); return 1; }在一系列頭文件之后,這里extern “C”告訴編譯器在編譯attitude_estimator_q_main這個(gè)函數時(shí)按照C的規則去翻譯相關(guān)的函數名而不是C++的; __EXPORT 表示將函數名輸出到鏈接器(Linker)。
然后跳轉到函數的定義部分int attitude_estimator_q_main(int argc, char *argv[]),判斷系統給出的命令行的參數,一系列的判斷,C++在大型項目上的優(yōu)勢這里有沒(méi)有發(fā)揮出來(lái)!總之你要的是start就對了。
和attitude_estimator_q相似,一個(gè)正常的應用程序啟動(dòng)如下圖所示,直接task_main( ) 吧:
坐標系
慣性導航的基礎是精確定義一系列的笛卡兒參考坐標系,每一個(gè)坐標系都是正交的右手坐標系或軸系。
對地球上進(jìn)行的導航,所定義的坐標系要將慣導系統的測量值與地球的主要方向聯(lián)系起來(lái)。也就是說(shuō),當在近地面導航時(shí),該坐標系具有實(shí)際意義。因此,習慣上將原點(diǎn)位于地球中心、相對于恒星固定的坐標系定義為慣性參考坐標系,下圖給出了用于陸地導航的固連于地球的參考坐標系和當地地理導航坐標系以及慣性參考坐標系。
地球坐標系(e系)。原點(diǎn)位于地球中心,坐標軸與地球固連,軸向定義為Oxe,Oye,Oze 。其中,Oze 沿地球極軸方向,Oxe軸沿格林尼泊子午面和地球赤道平面的交線(xiàn)。地球坐標系相對于慣性坐標系繞Ozi 軸以角速度Ω轉動(dòng)。
導航坐標系(n系)。是一種當地地理坐標系,原點(diǎn)位于導航系統所處的位置P點(diǎn),坐標軸指向北、東和當地垂線(xiàn)方向(向下)。導航坐標系相對于地球固連坐標系的旋轉角速率wen取決于P 點(diǎn)相對于地球的運動(dòng),通常稱(chēng)為轉移速率。
載體坐標系(b系)。一個(gè)正交坐標系,軸向分別沿安裝有導航系統的運載體的橫滾軸、俯仰軸和偏航軸。
在PX4中,
Local position setpoint in NED frame → 導航坐標系(以起飛點(diǎn)home為原點(diǎn)) 北東地 xyz
Global position in WGS84 coordinates → 世界大地坐標系(原點(diǎn)位于地球質(zhì)心)
NED earth-fixed frame →個(gè)人覺(jué)得是GPS投影到地面的坐標系,原點(diǎn)?
NED body-fixed frame →機體坐標系,x軸正方向為機頭,z軸正方向下
這里有兩個(gè)函數不得不提:
123456789101112131415161718192021222324252627282930313233343536373839404142434445//將地理學(xué)坐標系(geographic coordinate system)中的點(diǎn)(球)投影到本地方位等距平面(XOY)中 int map_projection_project(const struct map_projection_reference_s *ref, double lat, double lon, float *x,float *y) { if (!map_projection_initialized(ref)) { return -1; } double lat_rad = lat * M_DEG_TO_RAD; // 度 -> 弧度 A/57.295 double lon_rad = lon * M_DEG_TO_RAD; // GPS數據角度單位為弧度 double sin_lat = sin(lat_rad); //程序中三角運算使用的是弧度 double cos_lat = cos(lat_rad); double cos_d_lon = cos(lon_rad - ref->lon_rad); double arg = ref->sin_lat * sin_lat + ref->cos_lat * cos_lat * cos_d_lon; if (arg > 1.0) { arg = 1.0; } else if (arg < -1.0) { arg = -1.0; //限幅 } double c = acos(arg); double k = (fabs(c) < DBL_EPSILON) ? 1.0 : (c / sin(c));// c為正數 *x = k * (ref->cos_lat * sin_lat - ref->sin_lat * cos_lat * cos_d_lon) * CONSTANTS_RADIUS_OF_EARTH; *y = k * cos_lat * sin(lon_rad - ref->lon_rad) * CONSTANTS_RADIUS_OF_EARTH; return 0; }將球面坐標轉化為平面坐標的過(guò)程便稱(chēng)為投影。這里將經(jīng)緯度轉換成地坐標系xy值,也就是說(shuō)是基于GPS的位置自動(dòng)控制 。
采用的是等距方位投影的方法(Azimuthal Equidistant Projection)。
方位投影既不是等面積也不是保形的。讓φ1和λ0作為投影中心的緯度和經(jīng)度,則變換方程由下式給出
x=k′cos?sin(λ?λ0)
y=k′[cos?1sinsin?1cos?sin(λ?λ0)]
這里
k′=csinc
并且
cosc=sin?1sin?+cos?1cos?cos(λ?λ0)]
c在這里代表距中心的角距離(一定點(diǎn)到兩物體之間所量度的夾角)。公式的逆表達如下:
=sin?1(coscsin?1+ysinccos?1c)
以及
λ=?λ0+tan?1(xsincccos?1cosc?ysin?1sinc),λ0+tan?1(?xy),λ0+tan?1(xy),for?1≠±90°for?1=90°for?1=-90°
到中心的角距離由下式給出:
c=x2+y2√
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657//將本地方位等距平面中的點(diǎn)投影到地理學(xué)坐標系 int map_projection_global_reproject(float x, float y, double *lat, double *lon) { return map_projection_reproject(&mp_ref, x, y, lat, lon); } __EXPORT int map_projection_reproject(const struct map_projection_reference_s *ref, float x, float y, double *lat, double *lon) { if (!map_projection_initialized(ref)) { return -1; } double x_rad = x / CONSTANTS_RADIUS_OF_EARTH; // 地球半徑 double y_rad = y / CONSTANTS_RADIUS_OF_EARTH; double c = sqrtf(x_rad * x_rad + y_rad * y_rad); double sin_c = sin(c); double cos_c = cos(c); double lat_rad; double lon_rad; if (fabs(c) > DBL_EPSILON) { lat_rad = asin(cos_c * ref->sin_lat + (x_rad * sin_c * ref->cos_lat) / c); lon_rad = (ref->lon_rad + atan2(y_rad * sin_c, c * ref->cos_lat * cos_c - x_rad * ref->sin_lat * sin_c)); } else { lat_rad = ref->lat_rad; lon_rad = ref->lon_rad; } *lat = lat_rad * 180.0 / M_PI; // 弧度 -> 度 *lon = lon_rad * 180.0 / M_PI; return 0; }先map_projection_reproject( )再map_projection_project( )。這種方式將位置轉換為經(jīng)緯度和高度, 然后用位置估計參數來(lái)更新經(jīng)緯度和高度,接著(zhù)轉換回位置參考點(diǎn),屬于GPS數據轉換的方式。
poll
int poll(struct pollfd fds[], nfds_t nfds, int timeout)
功能:監控文件描述符(多個(gè));
說(shuō)明:timemout=0,poll()函數立即返回而不阻塞;timeout=INFTIM(-1),poll()會(huì )一直阻塞下去,直到檢測到return >0;
參數:
fds:struct pollfd結構類(lèi)型的數組;
nfds:用于標記數組fds中的結構體元素的總數量;
timeout:是poll函數調用阻塞的時(shí)間,單位:毫秒;
返回值:
>0:數組fds中準備好讀、寫(xiě)或出錯狀態(tài)的那些socket描述符的總數量;
==0:poll()函數會(huì )阻塞timeout所指定的毫秒時(shí)間長(cháng)度之后返回;
-1:poll函數調用失敗;同時(shí)會(huì )自動(dòng)設置全局變量errno;
poll( )函數用于監測多個(gè)等待事件,若事件未發(fā)生,進(jìn)程睡眠,放棄CPU控制權。若監測的任何一個(gè)事件發(fā)生,poll函數將喚醒睡眠的進(jìn)程,并判斷是什么等待事件發(fā)生,并執行相應的操作。poll( )函數退出后,struct polldf變量的所有值被清零,需要重新設置。
uORB
uORB(Micro Object Request Broker,微對象請求**器)是PX4/Pixhawk系統中非常重要且關(guān)鍵的一個(gè)模塊,它肩負了整個(gè)系統的數據傳輸任務(wù),所有的傳感器數據、GPS、PPM信號等都要從芯片獲取后通過(guò)uORB進(jìn)行傳輸到各個(gè)模塊進(jìn)行計算處理。實(shí)際上uORB是一套跨「進(jìn)程」 的IPC通訊模塊。在Pixhawk中, 所有的功能被獨立以進(jìn)程模塊為單位進(jìn)行實(shí)現并工作。而進(jìn)程間的數據交互就由為重要,必須要能夠符合實(shí)時(shí)、有序的特點(diǎn)。
Pixhawk使用的是NuttX實(shí)時(shí)ARM系統,uORB實(shí)際上是多個(gè)進(jìn)程打開(kāi)同一個(gè)設備文件,進(jìn)程間通過(guò)此文件節點(diǎn)進(jìn)行數據交互和共享。進(jìn)程通過(guò)命名的「總線(xiàn)」交換的消息稱(chēng)之為「主題」(topic),在Pixhawk 中,一個(gè)主題僅包含一種消息類(lèi)型,通俗點(diǎn)就是數據類(lèi)型。每個(gè)進(jìn)程可以「訂閱」或者「發(fā)布」主題,可以存在多個(gè)發(fā)布者,或者一個(gè)進(jìn)程可以訂閱多個(gè)主題,但是一條總線(xiàn)上始終只有一條消息。
應用層中操作基礎飛行的應用之間都是隔離的,這樣提供了一種安保模式,以確?;A操作獨立的高級別系統狀態(tài)的穩定性。而溝通它們的就是uORB。
源碼中關(guān)于uORB有幾個(gè)常見(jiàn)的函數:
公告主題
在數據被發(fā)布到一個(gè)主題前,它必須被公告,發(fā)布者可以使用下面的API來(lái)公告一個(gè)新的主題
extern int orb_advertise(const struct orb_metadata *meta, const void *data);
參數:
meta:uORB元對象,可以認為是主題id,一般是通過(guò)ORB_ID(主題名)來(lái)賦值;
data:指向一個(gè)已被初始化,發(fā)布者要發(fā)布的數據存儲變量的指針;
返回值:
錯誤則返回ERROR;成功則返回一個(gè)可以發(fā)布主題的句柄;如果待發(fā)布的主題沒(méi)有定義或聲明則會(huì )返回-1,然后會(huì )將errno賦值為ENOENT;
公告也可以發(fā)布初始化數據到主題,meta參數是傳遞給API的一個(gè)指針,指向由ORB_DEFINE()宏定義好的數據,通常使用ORB_ID()宏來(lái)根據主題名稱(chēng)獲取該指針。請注意,雖然主題更新可以從中斷處理函數發(fā)布,公告主題必須在常規的線(xiàn)程上下文中執行。
發(fā)布更新
一旦公告了一個(gè)主題,公告主題后返回的句柄可使用下面的API來(lái)發(fā)布主題更新。
extern int orb_publish(const struct orb_metadata *meta, int handle, const void *data);
參數:
meta:uORB元對象,可以認為是主題id,一般是通過(guò)ORB_ID(主題名)來(lái)賦值;
handle:orb_advertise函數返回的句柄;
data:指向待發(fā)布數據的指針;
返回值:
OK表示成功;錯誤返回ERROR;否則則有根據的去設置errno;
uORB不會(huì )緩存多個(gè)更新,當用戶(hù)檢查一個(gè)主題,他們將只能看到最新的更新。
訂閱主題
訂閱主題的要求如下:
調用ORB_DEFINE()或ORB_DEFINE_OPTIONAL()宏(在訂閱者的頭文件中包含他們) 發(fā)布到主題的數據結構定義(通常與發(fā)布者使用同一頭文件)
如果滿(mǎn)足上面的條件后,訂閱者可以使用下面的api來(lái)訂閱一個(gè)主題:
extern int orb_subscribe(const struct orb_metadata *meta);
參數:
meta:uORB元對象,可以認為是主題id,一般是通過(guò)ORB_ID(主題名)來(lái)賦值;
返回值:
錯誤則返回ERROR;成功則返回一個(gè)可以讀取數據、更新話(huà)題的句柄;如果待訂閱的主題沒(méi)有定義或聲明則會(huì )返回-1,然后會(huì )將errno賦值為ENOENT;
即使訂閱的主題沒(méi)有被公告,但是也能訂閱成功;但是在這種情況下,卻得不到數據,直到主題被公告;如果可選主題不存在于固件之中,訂閱到可選的主題將會(huì )失敗,但其他主題即便發(fā)布者沒(méi)有進(jìn)行公告也會(huì )訂閱成功,這樣可大大降低系統對啟動(dòng)順序的安排。
取消訂閱
要取消訂閱一個(gè)主題,可以用下面的API
extern int orb_unsubscribe(int handle);
拷貝數據
訂閱者不能引用ORB中存儲的數據或其他訂閱共享的數據,而是在訂閱者請求時(shí)從ORB拷貝數據到訂閱者的臨時(shí)緩沖區。副本拷貝的方式可以避免鎖定ORB的問(wèn)題,并保持兩者之間(發(fā)布者,訂閱者)的API接口簡(jiǎn)單。它也允許訂閱者在必要的時(shí)候直接修改拷貝副本的數據供自己使用。從訂閱的主題中獲取數據并將數據保存到buffer中。
當訂閱者想要把主題中的最新數據拷貝一份全新的副本,可以使用:
extern int orb_copy(const struct orb_metadata *meta, int handle, void *buffer);
參數:
meta:uORB元對象,可以認為是主題id,一般是通過(guò)ORB_ID(主題名)來(lái)賦值;
handle:訂閱主題返回的句柄;
buffer:從主題中獲取的數據;
返回值:
返回OK表示獲取數據成功,錯誤返回ERROR;否則則有根據的去設置errno;
拷貝是以原子操作進(jìn)行的,所以可以保證獲取到發(fā)布者最新的數據。
檢查更新
訂閱者可以使用下面的API來(lái)檢查一個(gè)主題在發(fā)布者最后更新后,有沒(méi)有人調用過(guò)orb_copy來(lái)接收,處理:
extern int orb_check(int handle, bool *updated);
語(yǔ)句,很多時(shí)候Source Insight無(wú)法跳轉到
版權聲明: 本站僅提供信息存儲空間服務(wù),旨在傳遞更多信息,不擁有所有權,不承擔相關(guān)法律責任,不代表本網(wǎng)贊同其觀(guān)點(diǎn)和對其真實(shí)性負責。如因作品內容、版權和其它問(wèn)題需要同本網(wǎng)聯(lián)系的,請發(fā)送郵件至 舉報,一經(jīng)查實(shí),本站將立刻刪除。