mavlink解析

之前看了mavlink協議,網上關於mavlink的資料不多。本系列共三篇,這是第一篇。本文大概總結了下對mavlink協議的理解。以下如不說明都是說mavlink v1.0版本。


首先附上mavlink的各個消息的簡介https://pixhawk.ethz.ch/mavlink/(這裏的內容很多,建議大概瞭解mavlink後再去瀏覽),mavlink協議介紹http://qgroundcontrol.org/mavlink/start


先簡單介紹下mavlink協議。Mavlink協議最早由 蘇黎世聯邦理工學院 計算機視覺與幾何實驗組 的 Lorenz Meier於2009年發佈,並遵循LGPL開源協議。Mavlink協議是在串口通訊基礎上的一種更高層的開源通訊協議,主要應用在微型飛行器(micro aerial vehicle)的通訊上。Mavlink是爲小型飛行器和地面站(或者其他飛行器)通訊時常常用到的那些數據制定一種發送和接收的規則並加入了校驗(checksum)功能。


【1】下面開始說介紹mavlink所發送的數據結構。Mavlink傳輸時的基本單位是消息幀。


如圖所示,每個消息幀都是上述的結構,除了灰色外,其他的格子都代表了一個字節的數據。

紅色的是起始標誌位(stx),在v1.0版本中以“FE”作爲起始標誌。這個標誌位在mavlink消息幀接收端進行消息解碼時有用處。

第二個格子代表的是灰色部分(payload,稱作有效載荷,要用的數據在有效載荷裏面)的字節長度(len),範圍從0到255之間。在mavlink消息幀接收端可以用它和實際收到的有效載荷的長度比較,以驗證有效載荷的長度是否正確。

第三個格子代表的是本次消息幀的序號(seq),每次發完一個消息,這個字節的內容會加1,加到255後會從0重新開始。這個序號用於mavlink消息幀接收端計算消息丟失比例用的,相當於是信號強度。

第四個格子代表了發送本條消息幀的設備的系統編號(sys),使用PIXHAWK刷PX4固件時默認的系統編號爲1,用於mavlink消息幀接收端識別是哪個設備發來的消息。

第五個格子代表了發送本條消息幀的設備的單元編號(comp),使用PIXHAWK刷PX4固件時默認的單元編號爲50,用於mavlink消息幀接收端識別是設備的哪個單元發來的消息(暫時沒什麼用) 。

第六個格子代表了有效載荷中消息包的編號(msg),注意它和序號是不同的,這個字節很重要,mavlink消息幀接收端要根據這個編號來確定有效載荷裏到底放了什麼消息包並根據編號選擇對應的方式來處理有效載荷裏的信息包。

最後兩個字節是16位校驗位,ckb是高八位,cka是低八位。校驗碼由crc16算法得到,算法將整個消息(從起始位開始到有效載荷結束,還要額外加上個MAVLINK_CRC_EXTRA字節)進行crc16計算,得出一個16位的校驗碼。之前提到的每種有效載荷裏信息包(由消息包編號來表明是哪種消息包)會對應一個MAVLINK_CRC_EXTRA,這個 MAVLINK_CRC_EXTRA 是由生成mavlink代碼的xml文件生成的,加入這個額外的東西是爲了當飛行器和地面站使用不同版本的mavlink協議時,雙方計算得到的校驗碼會不同,這樣不同版本間的mavlink協議就不會在一起正常工作,避免了由於不同版本間通訊時帶來的重大潛在問題。

爲了方便敘述,消息包將稱作包,包所代表的信息稱作消息。上圖中的sys將稱爲sysid,comp將稱爲compid,msg將稱爲msgid。

官方的介紹如下圖:




上文中已經提到了在mavlink消息幀裏最重要的兩個東西,一個是msgid;一個是payload,前者是payload中內容的編號,後者則存放了消息。消息有許多種類型,在官網的網頁中中以藍色的“#”加數字的方式來表示消息的編號如 “#0”(這樣的表示方法應該是爲了方便在網頁中查找相應編號消息的定義)。在官網介紹網頁裏往下拉,大概拉到二分之一的位置處,開始出現“MAVLink Messages”的介紹,往下看是各種消息的數據組成說明。下面將以幾個消息爲例,講解mavlink消息。


先以 #0 消息爲例,這個消息叫心跳包(heartbeat)。它一般用來表明發出該消息的設備是活躍的,飛行器和地面站都會發出這個信號(一般以1Hz發送),地面站和飛行器會根據是否及時收到了心跳包來判斷是否和飛行器或地面站失去了聯繫。

                        


從圖上可以看出,心跳包由6個數據組成,第一個是佔一個字節的飛行器類型數據(type),這個數據表示了當前發消息的是什麼飛行器,比如四旋翼,固定翼等等。type的取值如何與飛行器類型對應,這要在官方的mavlink消息介紹網頁上找,位於網頁開始出的數據枚舉中。如下圖所示:

                                                                  

這裏只是一部分的類型,第一個是通用飛行器,對應的type數值是0;第二個是固定翼類型,對應的數值是1;第三個對應的是四旋翼,對應的數值是2.這個飛行器類型,其實對於發心跳包的地面站來說可能沒什麼意義(不同飛控對該消息的處理方法不同,至少刷了PX4固件的Pixhawk飛控對地面站發來的心跳包裏的這個參數並不關心,如無特殊說明,之後所說的Pixhawk飛控都是指刷PX4固件的飛控),對於飛行器端來說代表了當前飛行器的類型,地面站可以根據這個參數來判斷飛行器的類型並作出其他的反應。

第二個參數是自駕儀(即通常所說的飛控)類型,比如apm,ppz,Pixhawk等飛控,具體定義查找和之前查找飛行器類型時的方法一樣。同樣的,對於發送心跳包的飛行器來說代表了自己的飛控類性,對地面站發出的心跳包來說意義不大。

第三個參數是基本模式(base mode),是指飛控現在處在哪個基本模式,對於發心跳包的地面站來說沒有意義,對於發送心跳包的飛控來說是有意義的。這個參數要看各個飛控自己的定義方式,mavlink介紹網頁並不會給出具體的模式。在Pixhawk中基本模式可以分爲使用用戶模式(custom mode)還是基本模式(這裏有點繞,其實是就是是否使用用戶模式)。使用用戶模式將在講下個參數時說明,使用基本模式又會分爲自動模式(auto),位置控制模式(posctl)和手動模式(manual)。一般情況下都會使用用戶模式,普通用戶不用關心這個參數。開發者在使用mavlink修改飛行器模式時需要注意基本模式的設置,具體請看PX4代碼,下載地址https://pixhawk.org/firmware/source_code

另外,Pixhawk的模式和apm的有很大的不同,具體請看官網介紹https://pixhawk.org/users/system_modes,裏面還有關於遙控器如何設置模式的教程鏈接https://pixhawk.org/users/system_modes/mode_switch_config。用QGroundControl地面站(以後簡稱QGC,下載地址http://qgroundcontrol.org/downloads)的圖形界面來設置飛行模式的功能很雞肋,建議直接在QGC中讀取飛控參數值,並對遙控器的設置參數進行修改,記得改變參數後只是改變了飛控ram參數,要把參數寫入到rom中才可以。

                                                                     

第四個參數是用戶模式(custom mode),大概說一下Pixhawk的用戶模式。以多軸爲例。它分爲主模式(main mode)和子模式(sub mode),兩種模式組合在一起成爲最終的模式,主模式分爲3種,手動(manual),輔助(assist),自動(auto)。手動模式類似apm的姿態模式。在輔助模式中,又分爲高度控制模式(altctl)和位置控制模式(posctl)兩個子模式,高度控制模式就類似apm的定高模式,油門對應到飛行器高度控制上。位置模式控制飛行器相對地面的速度,油門和高度控制模式一樣,yaw軸控制和手動模式一樣。自動模式裏又分爲3個子模式,任務模式(mission),留待模式(loiter),返航模式(return),任務模式就是執行設定好的航點任務,留待模式就是gps懸停模式,返航模式就是直線返回home點並自動降落。在apm裏這個參數貌似是沒有用的,注意這個數據佔了4個字節,在Pixhawk中,前兩個字節(低位)是保留的,沒有用,第三個字節是主模式,第四個字節是子模式。普通用戶請無視,開發者請注意:官網給出的通過程序設置模式的代碼是錯誤的。如圖,最後一行代碼有誤,應該爲:


  • uint32_t custom_mode = (main_mode<< 16) | (sub_mode << 24);

[color=rgb(34, 34, 34) !important]複製代碼

第五個是系統狀態(system status),查定義就好了,其中的standby狀態在Pixhawk裏就是還沒解鎖的狀態,active狀態就是已經解鎖,準備起飛的狀態。

第六個是mavlink版本(mavlink version),現在是“3”版本。


其餘的消息也是類似的結構,各個數據的定義可以查看mavlink官方網頁的說明,這些說明一般在網頁的前面部分。具體說明以飛控爲準,mavlink僅提供基本的定義。


有幾個相對特殊和容易混淆的消息再特別說明下:

#76消息(command long),該消息是發送長命令,一般是地面站發送給飛控命令用的。該消息組成如下圖。目標系統(命令的接收方,就是目標系統編號sysid),目標單元(命令的接收單元,就是目標單元編號compid)。command數據是這條命令的編號,用於區別不同的命令。confirmation數據,筆者還不是很明白,大概是是否需要收到命令後回覆確認信號的意思。接下去有七個參數,這些參數是執行這條命令所需要告訴飛控的,許多命令都用不到七個參數,多餘的參數清0就可以了。


Pixhawk支持的命令有許多種(但不是所有mavlink命令都支持)。要看mavlink提供了哪些命令請在介紹mavlink的官網查詢mav_cmd,在網頁的中上部分。比如:第176號命令 MAV_CMD_DO_SET_MODE。這條命令用於改變飛行器的飛行模式,第一個參數就是設置飛控的base_mode,第二個是設置custom_mode。想要通過這條命令正確設置pixhawk的模式需要查看PX4代碼,mavlink對參數的描述不夠具體。


現在應該對介紹mavlink官網的佈局有所瞭解了吧。網頁前面主要講了各類數據的取值和含義,比如飛控類型(mav_autopilot),飛行器類型(mav_type)等,其中mav_cmd是比較特殊和重要的一種數據。網頁的後半部分主要講了mavlink消息的種類和數據組成,這裏會用到各種數據,具體數據定義的可以回到前半部分去找。但是mavlink是個通用的通訊協議,不同的飛控支對mavlink支持方式不一樣,一般都只支持一部分mavlink消息,還會自己擴展一些mavlink協議所沒有定義的消息(pixhawk和apm都是如此),具體都以飛控代碼爲準。


大概說說地面站和飛控的通訊流程,由於沒看過地面站的代碼,所以很可能有誤,還望發評論指正!一般飛控在連接上地面站後都會主動向地面站發送心跳包,飛行器姿態,系統狀態,遙控器信號等組成的數據流。各個數據都會以一定的頻率發送,比如心跳包一般是1Hz,姿態信息會快些,pixhawk用數傳連接QGC時的姿態數據發送頻率在7-8Hz左右。一般地面站會在剛連接上飛控時發送命令,請求飛控傳回所有參數(QGC就是這樣),飛控根據自己的情況判斷是否接受地面站的請求,並根據不同的命令執行相應的操作(有些命令需要飛控回覆地面站確認信號)。之後地面站根據用戶的操作會發送相應的mavlink消息給飛控,比如設置航點,改寫飛控參數等。據說數傳是半雙工的(在同一時刻只能選擇發送或者選擇接受數據,不能同時收發數據),地面站和飛控之間如何避免數據衝突(即雙方同時向對方發送消息)的機制筆者並不清楚,希望能拋磚引玉。


                                        

可以看到,裏面有多個文件夾和幾個頭文件。pixhawk,ardupilotmega(apm),matrixpilot這類的文件夾裏都是各個飛控自己定義的mavlink消息類型,原始的mavlink消息放在common文件夾裏面(大部分消息都在common文件夾中)。checksum.h中存放的是計算校驗碼的代碼。 mavlink_helper.h裏面是將各個消息包補充完整(調用checksum.h中的函數計算校驗碼並補上消息幀的頭,比如sysid和compid等)成爲mavlink消息幀再發送。最主要的功能集中在這兩個文件夾中。mavlink_conversions.h裏是dcm,歐拉角,四元數三種姿態表示方法之間的轉換代碼。

下面以發送心跳包(heartbeat)爲例,說明下如何使用mavlink頭文件來發送心跳包。首先打開common文件夾中的 mavlink_msg_heartbeat.h 頭文件。這個頭文件可以分爲兩部分,一部分用來打包、發送heartbeat消息,另一部分用來接收到heartbeat消息時解碼消息。heartbeat.h定義了heartbeat消息對應的數據類型:


  • typedef struct __mavlink_heartbeat_t
  • {
  • uint32_t custom_mode; ///< A bitfield for use for autopilot-specific flags.
  • uint8_t type; ///< Type of the MAV (quadrotor, helicopter, etc., up to 15 types, defined in MAV_TYPE ENUM)
  • uint8_t autopilot; ///< Autopilot type / class. defined in MAV_AUTOPILOT ENUM
  • uint8_t base_mode; ///< System mode bitfield, see MAV_MODE_FLAG ENUM in mavlink/include/mavlink_types.h
  • uint8_t system_status; ///< System status flag, see MAV_STATE ENUM
  • uint8_t mavlink_version; ///< MAVLink version, not writable by user, gets added by protocol because of magic data type: uint8_t_mavlink_version
  • } mavlink_heartbeat_t;

[color=rgb(34, 34, 34) !important]複製代碼

如果mavlink的發送方式可以使用(串口發送,函數接口也兼容),則可以調用 【1】

  • static inline void mavlink_msg_heartbeat_send(mavlink_channel_t chan, uint8_t type, uint8_t autopilot, uint8_t base_mode, uint32_t custom_mode, uint8_t system_status)


[color=rgb(34, 34, 34) !important]複製代碼

其中的chan是channel的縮寫,用於選擇發送的串口或者usb口。type就是飛行器類型,其餘參數不明的可以看看本博客的第一篇文章。

該函數功能是將傳入的各個參數按照對應的格式放到heartbeat消息包中(即打包)

這個函數內部有一句預處理:

  • #if MAVLINK_CRC_EXTRA

[color=rgb(34, 34, 34) !important]複製代碼

是說是否使用額外的crc校驗字符(默認使用),詳情請看第一篇博客中對於兩個校驗字節的說明。

函數中會調用函數【2】


  • _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_HEARTBEAT, buf, MAVLINK_MSG_ID_HEARTBEAT_LEN, MAVLINK_MSG_ID_HEARTBEAT_CRC);

[color=rgb(34, 34, 34) !important]複製代碼
  • MAVLINK_MSG_ID_HEARTBEAT//這個是心跳包消息對應的編號 這裏=0

[color=rgb(34, 34, 34) !important]複製代碼
  • MAVLINK_MSG_ID_HEARTBEAT_LEN//這個是心跳包的長度 注意這個長度僅僅是payload的長度,不包括幀的頭尾。

[color=rgb(34, 34, 34) !important]複製代碼
  • MAVLINK_MSG_ID_HEARTBEAT_CRC//這個是heartbeat消息對應的額外的crc校驗碼 這裏=50

[color=rgb(34, 34, 34) !important]複製代碼

這個函數位於mavlink_helper.h中,用於更新消息幀的編號(seq 每發送一幀加1)並將消息幀的頭和計算校驗碼,使得成爲完整的一個mavlink消息幀。最後調用串口發送函數進行消息幀的發送。


如果只是想將對應的心跳包參數按照心跳包的格式存放好,則可以只調用


  • static inline uint16_t mavlink_msg_heartbeat_pack(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg,
  • uint8_t type, uint8_t autopilot, uint8_t base_mode, uint32_t custom_mode, uint8_t system_status)


[color=rgb(34, 34, 34) !important]複製代碼

將參數打包爲heartbeat消息幀,待之後使用。


解碼消息幀時可以調用mavlink_helper.h中的


  • MAVLINK_HELPER uint8_t mavlink_parse_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status)


[color=rgb(34, 34, 34) !important]複製代碼

它會將收到的字符一個個進行解碼,會檢驗收到的校驗碼是否正確;有效載荷的長度小於最大長度並且和該消息的長度一致。如果一切順利,將會得到解碼到的消息,放在解碼得到的消息幀類型中

  • typedef struct __mavlink_message {
  • uint16_t checksum; ///< sent at end of packet
  • uint8_t magic; ///< protocol magic marker
  • uint8_t len; ///< Length of payload
  • uint8_t seq; ///< Sequence of packet
  • uint8_t sysid; ///< ID of message sender system/aircraft
  • uint8_t compid; ///< ID of the message sender component
  • uint8_t msgid; ///< ID of message in payload
  • uint64_t payload64[(MAVLINK_MAX_PAYLOAD_LEN+MAVLINK_NUM_CHECKSUM_BYTES+7)/8];
  • } mavlink_message_t;

[color=rgb(34, 34, 34) !important]複製代碼

其中的magic是一幀的起始標誌(FE=254),就是mavlink_stx的值。


其餘的mavlink消息也是類似的,舊的mavlink代碼中有些類型的消息類型可能會找不到,使用時要注意接受和發送方使用的mavlink版本是否兼容。common文件夾中的common.h裏面包含了要用到的數據類型和所有消息的頭文件,使用時直接包含進來即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章