ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

无人机Remote ID发射模块配置:从MAVLink到OpenDroneID

无人机Remote ID发射模块配置:从MAVLink到OpenDroneID 1. RID发射模块配置前的整体思路做无人机运行识别Remote ID简称RID这块说实话是个挺新的领域。这两年国内外监管都在推远程识别无人机要“看得见、认得出”空中交通管理才能跟地面交通管理一样有章法。我这个项目目前进展到了第三个阶段——发射模块的参数配置前面已经把硬件方案跑通了模块能通电、能发信号但距离真正合规可用还差关键的参数调校这一步。先把我这套项目背景交代清楚方便后面聊参数的时候大家能对上号。我用的飞控是Pixhawk系列跑的是PX4或者ArduPilot固件RID发射模块采用的是基于OpenDroneID协议的开源方案硬件是ESP32芯片加上配套的射频前端通过串口跟飞控通信。之所以选这套方案一是因为OpenDroneID是目前行业里比较通行的开源协议栈二是因为ESP32成本低、资料多、调试方便一个人做项目不至于被硬件折腾到崩溃。为什么参数配置这一步特别关键因为RID模块发射的数据如果不准确、不完整轻则地面站收不到有效信息重则直接违反监管要求测试都过不了。打个比方你把一台收音机频率调偏了广播照样响但全是杂音RID模块参数配置不对就是这个效果——信号发出去了接收端却解析不出来正确的内容。参数配置前需要明确的几个核心问题RID模块要广播哪些信息根据OpenDroneID规范主要包括飞行器ID、位置、高度、速度、航向、电池电量、飞控状态等。数据从哪里来这些信息不是模块自己编的而是从飞控的MAVLink消息里实时读取的。参数配置的本质是什么就是告诉RID模块“从哪条MAVLink消息里取哪个字段以什么格式填进OpenDroneID的哪个报文段”同时配置射频发射的相关参数。这套逻辑想明白了后面所有参数配置都是有迹可循的而不是靠猜。我见过不少人在这一步翻车上来就乱改参数改完模块彻底沉默连日志都不打排查起来非常痛苦。下面这张流程图不需要我直接用文字把信息流讲清楚飞控位置、速度等原始数据→ MAVLink消息封装成标准协议→ RID发射模块串口接收→解析→重新编码为OpenDroneID报文→ 射频发射Wi-Fi Beacon/蓝牙广播→ 手机App/地面站接收显示。2. 核心参数逐项拆解2.1 飞行器标识信息配置飞行器ID是RID报文里最基础也最关键的一项相当于无人机的“车牌号”。OpenDroneID支持几种ID类型最常见的是ICAO地址International Civil Aviation Organization国际民用航空组织分配的24位地址也有厂家自定义的序列号形式。我这边配置的是ICAO地址。获取方式很简单PX4飞控的参数里就有一个MAV_SYS_ID但要注意这个ID不等于ICAO地址。ICAO地址是在OpenDroneID的Basic ID消息里单独配置的通常是模块的配置文件里写死的静态值。配置文件里对应的部分大概是这样的// RID模块配置示例 #define RID_ICAO_ADDRESS 0xA1B2C3 // 24位ICAO地址十六进制 #define RID_ID_TYPE RID_ID_ICAO #define RID_AIRCRAFT_TYPE RID_TYPE_MULTIROTOR // 多旋翼这里有个容易踩的坑ICAO地址不能随便填。虽然它是十六进制的数值但实际使用中建议从厂家分配的地址段里选或者用飞控的序列号派生一个固定值。我在调试阶段图省事填了个0xDEADBEEF这是32位超了模块直接报参数错误折腾了半天才发现是地址位数不对ICAO地址必须是24位也就是6位十六进制数。飞行器类型字段也不容忽视。OpenDroneID规范里定义了固定翼、多旋翼、直升机等多种类型。这个字段直接决定了地面接收端显示出来的图标和分类信息。我一开始没改这个参数默认值是固定翼地面站显示出来的飞机图标跟实际的四旋翼完全对不上乍一看还以为是别人家的飞机飞过来了。2.2 位置与速度信息来源配置RID报文里最核心的动态信息就是经纬度、高度、速度、航向。这些数据的源头在飞控的GPS模块和IMU经过飞控解算后通过MAVLink的GLOBAL_POSITION_INT消息向外发送。配置层面要做的有两件事一是告诉模块去监听哪条MAVLink消息二是设置好数据转换的规则。GLOBAL_POSITION_INT消息的关键字段如下字段名类型单位说明latint321e-7度纬度注意是缩放后的整数lonint321e-7度经度同样是缩放后的整数altint32毫米海拔高度椭球高relative_altint32毫米相对起飞点高度vxint16cm/s北向速度分量vyint16cm/s东向速度分量vzint16cm/s垂直速度分量向下为正hdguint16百分度航向角0~35999看到这个表你就能明白为什么参数配置这么重要了。RID模块要把这些字段转换成OpenDroneID规范要求的格式转换关系不对地面站显示的位置能偏出几条街去。我实际配置的转换关系是这样的// 经纬度转换int32(1e-7度)转int32(1e-7度)规范一致直接透传 rid_msg.latitude global_pos_int.lat; rid_msg.longitude global_pos_int.lon; // 高度毫米转米OpenDroneID要求单位是米 rid_msg.height global_pos_int.alt / 1000.0f; // 速度cm/s转m/s同时可以算出地速大小 float vx global_pos_int.vx / 100.0f; float vy global_pos_int.vy / 100.0f; float speed sqrtf(vx * vx vy * vy); rid_msg.speed speed; // 航向百分度转为度 rid_msg.direction global_pos_int.hdg / 100.0f;这里有几个细节要注意首先是经纬度的单位问题。MAVLink里纬度是以1e-7度为单位的整数OpenDroneID规范里Location消息的经纬度也是以1e-7度为单位的整数。单位恰好一致可以直接透传但如果你中间做了浮点转换一定要控制精度别用double转float的时候丢掉有效位否则定位误差一下就出来了。其次是高度。飞控发出来的是毫米为单位的整数OpenDroneID要求接收端拿到的高度是米。这个转换不能只在显示层做要在模块固件里做掉否则不同接收端处理方式不一致有的显示正常有的显示异常。再次是速度矢量。MAVLink里的vx/vy是北东坐标系下的分量OpenDroneID的Speed字段是标量速度值。直接用勾股定理合成即可但要注意负号问题——vx和vy都有方向别在合成时丢符号。2.3 电量与状态信息配置RID规范里还要求广播电池电量和飞控状态信息。这部分数据的源头是MAVLink的BATTERY_STATUS消息和SYS_STATUS消息。BATTERY_STATUS里有一个battery_remaining字段单位是百分比0~100直接映射到OpenDroneID的电池字段即可。SYS_STATUS里则是飞控的整体健康状态包括传感器是否正常、电压是否稳定等。配置的时候重点关注两点一是电量数据要选对消息源。有些飞控姿态模式下也会广播BATTERY_STATUS但数值是模拟的。我测试时用USB供电飞控上报的电池百分比一直在100%看起来一切正常但装到真机上才发现电量显示完全不动排查了很久才发现是配置里选错了消息源读的是USB电源状态而不是电池状态。二是电量字段的更新频率。RID发射模块通常是周期性广播比如每1秒发一次。如果电量消息更新频率低模块发出去的还是上一次的值这在飞行中会造成电量显示滞后。实际测试下来5Hz的BATTERY_STATUS更新配合1Hz的RID广播效果是够用的。// 电量转换直接透传百分比值 rid_msg.battery_percent battery_status.battery_remaining; // 飞控状态标志位映射 if (sys_status.onboard_control_sensors_health MAV_SYS_STATUS_SENSOR_GPS) { rid_msg.operator_status | RID_OPERATOR_STATUS_GPS_FIX; }2.4 发射功率与频段配置RID模块的射频参数是整个配置环节里“物理感”最强的部分。OpenDroneID规范支持两种物理层Wi-Fi Beacon和蓝牙5长距离广播Bluetooth 5 Long Range。我用的ESP32芯片两者都支持默认跑的是Wi-Fi Beacon模式也就是在2.4GHz频段上周期性地发送Beacon帧。发射功率配置直接影响覆盖范围和合规性。ESP32的发射功率范围是-10dBm到20.5dBm步进0.5dBm。我实际测试的经验是场景建议发射功率备注室内地面测试8dBm减少干扰够用就行室外目视飞行12dBm兼顾功耗与覆盖远距离/复杂环境18dBm注意功耗上升明显有人可能会问为什么不全开满功率因为RID发射模块多半是挂在无人机上的功耗直接吃电池。满功率发射ESP32的电流能到300mA以上一颗500mAh的3.3V小电池撑不了太久。而RID广播的覆盖距离要求通常是几百米到1公里实际上12dBm在开阔环境下已经能覆盖800米以上没必要为了“更远”去牺牲续航。配置方式在ESP32的固件里直接调用API// 设置发射功率单位dBm esp_wifi_set_max_tx_power(12); // 12dBm这里有个坑ESP32的Wi-Fi Beacon模式在部分固件版本里不支持自定义信道默认在1信道上。如果你的飞行区域有其他Wi-Fi设备也在1信道工作干扰会非常大。实测中RID模块旁边的手机连Wi-Fi热点RID数据的接收成功率能掉一半以上。解决思路有两个一是强制定频到干扰少的信道需要改ESP32的Wi-Fi驱动二是在物理上尽量把RID模块天线和数传天线分开布置减少同频干扰。2.5 MAVLink消息流配置RID模块要想拿到飞控的数据前提是飞控愿意把这些数据通过串口发出来。这就涉及到飞控端的MAVLink消息流配置。在PX4上设置串口的波特率和消息流通常是用mavlink命令# 设置/dev/ttyS1串口波特率921600 mavlink stream -d /dev/ttyS1 -r 921600 # 启动GLOBAL_POSITION_INT消息流10Hz mavlink stream -d /dev/ttyS1 -s GLOBAL_POSITION_INT -r 10 # 启动BATTERY_STATUS消息流5Hz mavlink stream -d /dev/ttyS1 -s BATTERY_STATUS -r 5 # 启动SYS_STATUS消息流2Hz mavlink stream -d /dev/ttyS1 -s SYS_STATUS -r 2在ArduPilot上对应的配置是通过SERIALx_PROTOCOL和SRx_参数完成的// ArduPilot串口配置示例 SERIAL2_PROTOCOL 2 // MAVLink2 SERIAL2_BAUD 921 // 921600波特率 SR2_EXTRA1 5 // 额外消息集包含位置等关键信息 SR2_POSITION 5 // 位置消息频率这块配置最让人头疼的问题是消息ID冲突和数据流拥堵。ESP32的串口接收如果是用硬件串口还好如果用软件模拟串口波特率高了容易丢字节。我一开始用的是115200波特率模块收到数据后解析偶尔出错后来提高到57600才稳定。115200反而太慢消息流一多就排队实时性变差。实测结论是57600波特率是ESP32软件串口稳定性的甜点值。有个小技巧分享一下调试的时候可以把MAVLink消息流同时接到QGroundControl地面站上先确认飞控端配置有没有问题再去查模块端解析。毕竟串口数据这玩意儿肉眼看不见有一个可视化工具在旁边对照排查效率能翻倍。3. 参数配置实操流程3.1 模块接线与初始化参数配置之前先把物理链路确认好。RID模块和飞控之间的接线是串口对接一般TXD接RXD、RXD接TXD、GND共地千万别把电源线接反。ESP32模块供电是3.3V但很多ESP32开发板上带了5V转3.3V的稳压芯片可以直接从飞控的5V输出取电。这时候要注意共地不然串口通信信号电平参考点不一致数据全是乱码。我用的连接方式飞控TELEM2端口或自定义串口 5V → ESP32 VIN如果开发板带稳压 TX → ESP32 RXD注意交叉 RX → ESP32 TXD GND → ESP32 GND接线完毕上电后用串口工具直接连ESP32的调试串口查看启动日志。正常启动时日志里会出现类似下面的信息[I][esp32_rid.c:120] RID Module Init OK [I][esp32_rid.c:135] MAVLink channel opened, baudrate57600 [I][esp32_rid.c:152] Waiting for flight controller heartbeat...如果一直卡在“Waiting for flight controller heartbeat”说明MAVLink通信没建立起来。排查顺序是先看波特率是否一致再看TXD/RXD有没有接反最后确认飞控串口是否已经使能。3.2 编译固件并写入参数RID发射模块的固件参数配置我是在源码级别完成的。OpenDroneID的ESP32参考实现里有一个config.h或者rid_config.h文件集中定义了所有可调参数。一个典型的配置头文件长这样#ifndef RID_CONFIG_H #define RID_CONFIG_H // 飞行器标识 #define RID_ICAO_ADDRESS 0xA1B2C3 #define RID_ID_TYPE RID_ID_ICAO #define RID_AIRCRAFT_TYPE RID_TYPE_MULTIROTOR // 串口与MAVLink #define RID_SERIAL_BAUDRATE 57600 #define RID_SERIAL_TX_PIN 17 #define RID_SERIAL_RX_PIN 16 // MAVLink消息流 #define MAVLINK_STREAM_RATE 10 // Hz全局消息更新率 // 射频配置 #define RID_WIFI_TX_POWER 12 // dBm #define RID_WIFI_CHANNEL 1 // Wi-Fi信道 #define RID_BROADCAST_INTERVAL 1000 // 广播间隔单位ms // 位置精度阈值单位米 #define RID_MIN_H_ACCURACY 5.0f #define RID_MIN_V_ACCURACY 8.0f #endif编译写入的完整流程是安装ESP-IDF开发环境ESP32的官方SDK或者用Arduino IDE加上ESP32开发板支持。拉取OpenDroneID的ESP32参考代码主要关注main目录下的rid.c、mavlink_parser.c、config.h。修改config.h里的宏定义填上你实际的飞控串口引脚、波特率、ICAO地址等参数。执行编译idf.py set-target esp32 idf.py menuconfig # 设置串口、Flash大小等 idf.py build idf.py -p /dev/ttyUSB0 flash monitor如果是用Arduino IDE直接改好头文件后点击上传即可流程简单很多。但Arduino IDE的缺点是编译优化选项不够灵活生成固件体积偏大对ESP32这种Flash只有4MB的芯片来说不是大问题但代码调试起来不如IDF精细。3.3 消息流联调与数据验证参数写入之后最关键的一步是联调验证。光看模块日志说“初始化成功”不够你得确认从飞控读到的数据确实是活数据而且在实时更新。我惯用的验证手法是打开模块的调试串口用下面这条命令持续输出RID报文的解析结果# 在ESP32的监控终端里 rid debug on然后观察输出。正常情况下控制台会周期性地打印[INFO] RID Broadcast #123 ID: 0xA1B2C3 Lat: 31.2304000 Lon: 121.4737000 Alt: 12.5m Speed: 0.0m/s Direction: 95.0deg Battery: 87% GPS: FIX如果参数配置有误日志里会出现对应的异常提示比如GPS数据无效GPS: NO FIX、经纬度数值异常0.0000000、ID字段错误ID: UNKNOWN等。数据层的验证搞定了还要做一次应用层的验证——用手机上的OpenDroneID接收App扫描。Android端有一个叫OpenDroneID RemoteID的测试AppiOS上也能找到类似的。打开App后如果模块广播正常App的地图界面上会显示一个标记点开后可以看到所有RID报文里的信息字段。这里有个高频问题App搜不到模块。原因不外乎几种一是模块发射功率太低二是信道不对三是App本身不支持Wi-Fi Beacon模式。我推荐先用官方测试App确认接收再换第三方App交叉验证。毕竟有的接收App实现不够规范收不到不等于模块发射有问题。3.4 地面站交叉验证除了手机App还可以用Mission Planner或QGroundControl这类地面站来做交叉验证。新版地面站对Remote ID有基础支持能显示附近的RID广播信号。验证的步骤是启动RID模块确保它在广播状态。打开地面站在飞行地图界面找到“远程ID”或“Remote ID”的显示开关。观察地图上是否出现一个独立的飞机图标不飞也显示因为RID广播不依赖飞控连接。这一步的意义在于验证RID模块的广播不依赖飞控的在线状态。只要模块通了电启动了广播逻辑即使没有飞控数据它也应该发送包含基本信息如ID、生产厂家等的RID报文区别只是位置信息可能为空或者沿用最后一次的有效值。我在测试时发现过一个有意思的现象RID模块用USB单独供电不接飞控时手机App能搜到它的广播但位置信息显示为N/A。接了飞控之后位置信息正常。这说明模块的广播流程和数据获取流程是解耦的一旦飞控数据断流模块不会傻傻地停发广播而是继续发“残缺”但有效的RID报文。这对合规性很重要因为即便飞控出了故障空管方仍然能知道“有一架无人机在这个区域”。4. 参数配置高频问题排查4.1 常见问题速查表这一节把我在调试过程中遇到的典型问题整理成速查表希望能帮大家少走弯路。问题现象可能原因排查与解决模块无法启动电源供电不足/接反用万用表量3.3V和5V确认电压正常再上电串口无日志输出TX/RX接反、波特率不一致先用USB-TTL直连模块调试串口确认日志正常收不到心跳包飞控串口未使能/协议不对确认飞控端SERIALx_PROTOCOL2MAVLink2位置数据为0GPS未定位/消息流未配置检查飞控GPS状态确认GLOBAL_POSITION_INT消息流已打开位置数据漂移经纬度单位转换错误检查是否做了多余的缩放1e-7度整数应直接透传App搜不到模块功率太低/信道干扰调高发射功率改信道用官方App排除兼容性问题电池显示不更新消息源选错/更新率太低确认BATTERY_STATUS消息正常提高消息更新率速度显示为零勾股合成时丢了符号/飞控数据本身为零静态测试时先确认GPS速度是否稳定输出4.2 诡异Bug排查实录挑两个印象深刻的case跟大家分享。第一个case模块在室内能收到RID广播一到室外就搜不到。排查了发射功率、天线方向、信道干扰都没有明显改善。最后用频谱仪扫了一圈发现是模块所在机架的碳纤维板对天线形成了屏蔽。碳纤维是导电材料对2.4GHz信号的衰减非常严重。解决办法是调整天线位置让天线伸出机架边缘不要紧贴着碳板。这个问题的教训是RID模块的天线布置不能只看电气指标还要看物理安装位置。碳纤维机架、金属支架、电池挡板都会影响辐射效率。低成本的做法是先跑一趟近距离测试确认模块本身没问题再做整机条件下的远距离测试定位是模块问题还是安装问题。第二个case模块日志显示RID广播一切正常但收到的位置和飞控实际位置差了几十米。排查过程比较曲折最后定位到是固件里用了double转float的隐式转换经纬度的小数精度丢了。前面聊过经纬度在MAVLink和OpenDroneID里都是以1e-7度为单位的整数。我当时为了调试方便在代码里先把整型转成了double单位度再转成float塞进RID报文。float只有约7位有效十进制数字经纬度这种动辄上百的值转换后精度只剩下几米到几十米正好对上了测试中发现的误差1e-7度对应的精度大概是一厘米double到float的转换损失了太多位。解决办法很简单整数直接透传不要经过浮点中转。这也算是我踩过最冤枉的一个坑——问题不是出在协议上而是出在自己写的“便民转换”代码上。4.3 配置保存与持久化RID模块的参数配置在开发阶段是编译进固件的每次改参数都要重新编译烧录效率很低。项目做到后期对频繁调参的场景很不友好。我后来在项目里加了一个简易的持久化方案把config.h里的关键参数做成可以在运行时通过串口指令修改的变量存入ESP32的NVSNon-Volatile Storage非易失性存储分区。操作界面类似这样rid set id 0xA1B2C4 rid set power 14 rid set channel 6 rid get current id: 0xA1B2C4 tx_power: 14dBm channel: 6 rid save config saved to NVS实现思路不复杂ESP-IDF自带了NVS库几行代码就能读写。启动时先从NVS读取配置读不到就用编译期的默认值。这样一个模块做多架飞机的适配时不需要重新编译固件串口敲几句命令就完事了。这个功能在实际项目里帮了大忙。之前调试一架飞机要烧录一次固件串口线插拔几十次现在一条命令解决。特别是做多机编队测试的时候每架飞机的ID必须唯一以前要编译N次现在直接现场配置。5. 参数配置完成后的验证清单RID模块参数配置完成后建议按下面这份清单做一次系统性的验证避免漏项模块通电后能在10秒内输出第一条RID广播手机App能搜索到模块显示的ID与配置一致位置信息与飞控当前位置一致误差在5米内速度、航向与飞控地面站显示一致电池电量与飞控上报值一致模块单独供电不接飞控时仍能广播RID报文ID字段有效飞行状态下RID广播不中断接收正常模块电流在规格范围内发热正常距离测试开阔环境下接收距离不低于500米每项验证通过后再进行下一项全部通过才算配置到位。我个人的经验是前5项是“数据正确性”验证后4项是“鲁棒性”验证。很多人只做前5项就收工了结果真机上电后才发现功耗超标、天线遮挡严重。多花十分钟把后4项也跑一遍后面能省一大堆返工的时间。6. 参数配置的合规性与扩展思考RID模块最终是要服务于合规飞行的参数配置时一定要对标监管要求。就我目前了解的情况几个关键参数是最容易被验收环节卡住的一是ID的真实性。RID报文里的ICAO地址或序列号必须是这台无人机真实对应的标识不能随意填一个测试值就出厂。二是位置信息的精度。监管要求的位置误差通常在几米级别参数转换时丢掉精度这种事不允许发生。三是广播的持续性和完整性。飞行全过程中RID报文不能中断该有的字段必须携带。从方案扩展的角度看RID发射模块的参数配置可以继续往几个方向深挖。比如做主备双模块冗余一个模块负责Wi-Fi Beacon广播另一个负责蓝牙广播这样不同类型的接收设备都能收到信号。再比如把RID模块采集到的飞行数据回传给地面控制端实现更细粒度的飞行监控而不是只依赖地面站的数传链路。我最近在试的一个小扩展是把RID模块的数据同时转发到机载的日志系统这样每次飞行的RID广播历史都能记录、可追溯。相当于给无人机装了一个“黑匣子”对事故分析和合规审计都很有价值。实现起来也不复杂模块在广播的同时把同样的数据通过另一个串口写到SD卡或者传给树莓派。关于参数配置这事我的体会是它看起来只是改几个数字、敲几行命令其实背后是对整个RID链路——从飞控数据源到射频发射——的全面理解。参数本身不复杂复杂的是搞清楚每个参数在整条链路里扮演什么角色、影响什么结果。你只有真正理解了经纬度为什么不能走float中转才能理解为什么RID模块出货前必须做整机验证而不是单板验证。最后分享一个小技巧调试RID模块时如果条件允许尽量准备两个接收设备同时收。一个手机App做直观确认一个带日志记录功能的地面站做数据留存。两条腿走路调试效率会高很多。真出问题的时候日志数据能帮你回溯到底哪个环节出了错不用全靠猜。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进