ArduPilot RTK 高精度定位实战:GPS 导航、传感器融合与故障排查
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
做农田测绘、精准喷药的朋友大概都有过这种崩溃时刻:定位偏了 5 米,航线全乱,一亩地的活儿等于白干。要把误差从"米级"压到"厘米级",靠的不是换一块更贵的天线,而是一套完整的定位方案——ArduPilot 开源飞控正是为此而生:它内置了从普通 NMEA 解析到 RTK 差分、再到多传感器融合滤波的完整 GPS 导航栈,让无人机稳稳停在你要它停的位置。
想用源码对照阅读的话,可执行:
git clone https://gitcode.com/GitHub_Trending/ar/ardupilot,本文涉及的代码主要集中在libraries/AP_GPS/和libraries/AP_NavEKF3/两个目录。
精度分级速查:你的任务到底需要多准?
先别急着上 RTK,搞清楚自己处在哪一档。打个比方:普通 GPS 像是"看地图走导航",DGPS 是"有人偶尔喊一句你偏了",而 RTK 相当于"有人举着一根标尺贴在你车身上实时比对"——基站和移动站用的是同一套卫星载波相位信号,就像两个人拿着同一把尺子对表,对完之后差值能精确到毫米级。
| 定位方式 | 典型精度 | 收敛/生效时间 | 一句话点评 |
|---|---|---|---|
| 单点定位(SPP) | 2~5 米 | 开机即有 | 入门导航够用,精细作业不够看 |
| 差分 GPS(DGPS) | 1~2 米 | 链路建立后秒级 | 花小钱升半档,适合航测 |
| RTK 浮点解(Float) | 20~50 厘米 | 数秒 | 已经能做很多事,但还不算"锁死" |
| RTK 固定解(Fixed) | 1~2 厘米 | 数秒到数分钟 | 精准农业、测绘的标准配置 |
记住一个口诀:Float 能用,Fixed 才敢干重活。浮点解的精度是"大概齐",固定解的坐标才是可交付的。
拆开 ArduPilot 的 GPS 模块:状态机与模块化架构
ArduPilot 的 GPS 子系统在libraries/AP_GPS/目录下,设计思路是"一个管理器 + 一堆可插拔后端":
- AP_GPS(管理者):负责调度多个 GPS 实例、对外统一提供位置/速度/精度接口;
- GPS_Backend(后端抽象基类):每种通信协议一个具体实现——NMEA、Ublox、SBAS、SBP(如 DroneCAN 上的 GPS)、MSP(如某些 ESC 里的"假 GPS")等;
- RTCM3_Parser:专门解析 RTCM3 差分报文流,把碎片化的字节流重新拼成完整报文(支持 MAVLink 分片重传,最大 720 字节一包)。
硬件层面的启示:GPS 模块和天线走的是独立的串口/引脚,天线质量直接决定信号底子,接线图上预留的 G3&SAFETY、TELEM 等接口就是留给 RTK 数据链的第二链路。
对使用者最重要的,是 ArduPilot 对"当前定位质量"的分级。源码libraries/AP_GPS/AP_GPS_FixType.h里定义了这套状态枚举:
// GPS 定位状态分级(libraries/AP_GPS/AP_GPS_FixType.h) enum class AP_GPS_FixType : uint8_t { NO_GPS = 0, // 压根没检测到 GPS 模块 NONE = 1, // 有模块、有数据,但还没锁定 FIX_2D = 2, // 二维锁定(水平位置) FIX_3D = 3, // 三维锁定(含高度),普通精度 DGPS = 4, // 差分增强,米级 RTK_FLOAT = 5, // RTK 浮点解,分米级 RTK_FIXED = 6, // RTK 固定解,厘米级 —— 我们的目标 STATIC = 7, // 静止固定解(一般用于基站自检) PPP = 8, // 精密单点定位 };把它想象成一条"升级打怪"的路径,接收机状态会沿着这条链爬升:
地面站软件里那个不断刷新的状态条,本质就是在显示这个枚举值。看到RTK_FIXED之前,心里都要留三分谨慎。
厘米级数据是怎么算出来的:EKF 多传感器融合
RTK 只解决了"输入够准",还有半个问题:飞机是动的,传感器还在不停抖。IMU 每毫秒都在报姿态和加速度,气压计每几十毫秒报一次高度,GPS 一秒钟给 10 到 50 个点——把这些节奏不一、各有脾气的信号揉成一个顺滑可信的位姿,就是导航滤波器(EKF)的活。ArduPilot 用的是libraries/AP_NavEKF3/下的三套独立 EKF 实例(互相对质,谁的解更一致就用谁的)。
每个周期里,滤波器干两件事:
- 预测:用 IMU 推算"按照加速度,我此刻应该在哪儿";
- 更新:拿 GPS(含 RTK 修正后的位置/速度)和气压计当"裁判",把推算结果往真实值上拽一拽。
注意虚线那条分支——EKF3 的精华之一是对每个观测做"创新量检验"(源码libraries/AP_NavEKF3/AP_NavEKF3_PosVelFusion.cpp中的 innov/test 逻辑):如果 GPS 报的位置和滤波器预期差得离谱(比如超过 3σ),它不会硬吞,而是判这条数据有问题并降权。这就是为什么 GPS 突然跳变时,飞机姿态通常不会跟着抽风。
还有一个很实用的自适应机制:EKF3 会直接读取 GPS 接收机自报的精度水平来调整融合权重——
// EKF3 融合 GPS 时的观测噪声设置(简化示意,源码见 AP_NavEKF3_PosVelFusion.cpp) if (gpsPosAccuracy > 0.0f) { // 有 RTK 时接收机上报厘米级精度,GPS 权重自动升高 R_OBS[3] = sq(constrain_ftype(gpsPosAccuracy, frontend->_gpsHorizPosNoise, 100.0f)); } else { // 拿不到精度时,退回用 EK3_GPS_POS_X 参数兜底 R_OBS[3] = sq(constrain_ftype(frontend->_gpsHorizPosNoise, 0.1f, 10.0f)); }翻译成人话:RTK 固定解期间,滤波器会"更信 GPS、少信 IMU 积分";信号退化成单点时,自动反过来多依赖 IMU 惯性推算。整个过程全自动,你只需要在参数上给个合理的底。
从零搭一条 RTK 链路:基站、移动站与 EKF 参数一步配好
以最常见的双 Ublox 方案为例(源码libraries/AP_GPS/AP_GPS.h中的类型枚举:GPS_TYPE_UBLOX_RTK_BASE = 17、GPS_TYPE_UBLOX_RTK_ROVER = 18)。假设天线一接基站、天线二接移动站,都挂在 GPS1 实例下:
# ---- 基站实例(固定架设在高处、无遮挡)---- GPS1_TYPE = 17 # UBLOX_RTK_BASE,基站模式 GPS1_AUTO_CONFIG = 2 # 自动配置接收机 GPS1_RATE_MS = 20 # 50Hz 输出,差分数据更密集 # ---- 移动站实例(装在飞行器上)---- GPS2_TYPE = 18 # UBLOX_RTK_ROVER,移动站模式 GPS2_AUTO_CONFIG = 2 GPS2_RATE_MS = 20 # ---- 差分链路:基站把 RTCM3 数据经 TELEM/第二串口送过来 ---- # GPS1_INJECT_TO 用于把 RTCM 报文注入到指定 GPS 实例移动站收到基站流后,接收机内部完成载波相位整周解算,随后 ArduPilot 侧的状态字段就会被填满。这些字段都定义在AP_GPS::GPS_State结构里(libraries/AP_GPS/AP_GPS.h),是你排查 RTK 的第一手证据:
// GPS_State 中与 RTK 相关的核心字段(节选自 AP_GPS.h) uint32_t rtk_time_week_ms; // 最后一次基线解算的 GPS 周内时间 uint32_t rtk_age_ms; // 差分修正的"年龄"(毫秒),0 表示没有修正 uint8_t rtk_num_sats; // 参与 RTK 解算的卫星数 int32_t rtk_baseline_x_mm; // 基线北分量(毫米) int32_t rtk_baseline_y_mm; // 基线东分量(毫米) int32_t rtk_baseline_z_mm; // 基线下分量(毫米) uint32_t rtk_accuracy; // 3D 基线精度估计(接收机相关)rtk_age_ms值得单独说:它告诉你"基站发来的修正现在有多新鲜"。数值持续上涨或不归零,多半是数据链路断了或太慢——这就是很多"RTK 打不固定"的第一现场。
EKF 侧常用调优参数一览:
| 参数 | 作用 | 建议 |
|---|---|---|
EK3_SRC1_ALT = 2 | 高度源选用气压计(2=Baro) | 起飞前必查,RTK 垂直精度好也可视情况用 GPS |
EK3_SRC1_VELXY = 3 | 水平速度源选用 GPS | 常规设置 |
EK3_SRC1_VELZ = 3 | 垂直速度源选用 GPS | 需接收机支持垂直速度输出 |
EK3_GPS_POS_X = 0.3 | GPS 位置噪声下限 | 单点环境可加大到 0.5 以上,给 IMU 更多话语权 |
EK3_GPS_VEL_X = 0.5 | GPS 速度噪声下限 | 与位置噪声同逻辑 |
EK3_GLITCH_RAD = 100 | 位置跳变半径(cm) | 越小越敏感,跳变报警越早 |
调参原则一句话:先保证输入干净(天线、链路),再动滤波器。EKF 参数是"微调",不是"救火"。
RTK 打不固定、定位漂移?先查这 4 处
现场排障最忌瞎改参数。按下面的顺序过一遍,能解决九成的问题:
| 现象 | 最可能的原因 | 处理动作 |
|---|---|---|
| 永远停在 Float,不 Fixed | RTCM 链路丢包/延迟,或基站解算未固定 | 查rtk_age_ms是否持续增长;检查基站架高与遮挡;核对注入端口GPS*_INJECT_TO与物理接线一致 |
| Fixed 但位置整体偏了 | 天线/馈线受干扰,或基站坐标基准不一致 | 挪开金属与电线重新固定;确认两端同一坐标系;重新让基站静态收敛 |
| 位置随机跳变 | 多路径反射、供电不足、IMU 异常 | 天线离机体远一些、加地面板;独立供电;跑一次加速度计/陀螺校准 |
| 飞起来后高度明显飘 | 高度源配错(用了 GPS 高度但 RTK 没固定) | 检查EK3_SRC1_ALT,气压计正常就用 Baro;留意地面效应参数 |
配合做一轮"健康自检",起飞前 30 秒搞定:
// 起飞前 GPS 健康检查清单(逻辑示意) bool gps_health_ok(const GPS_State& s) { if (s.status < AP_GPS_FixType::FIX_3D) return false; // 至少要 3D if (s.num_sats < 8) return false; // 卫星数别太抠 if (s.hdop > 200) return false; // HDOP>2.0(值为放大100倍) if (s.status == AP_GPS_FixType::RTK_FIXED && s.rtk_age_ms > 2000) return false; // RTK 修正超过 2 秒视为过期 return true; }ArduPilot 内部其实早就内置了类似的健康状态位(精度不足、卫星数不够、水平漂移、垂直速度异常等逐项打标,EKF 据此决定 GPS 用不用、怎么用),你在地面站看到的"GPS 异常"提示,背后就是这套检查在自动跑。
写在最后:从厘米级再往前一步
ArduPilot 的 GPS 导航栈还在持续进化,几个值得关注的方向:
- 双频/多频接收:L1+L2/L5 组合能更快解掉整周模糊度,收敛更快、抗遮挡更强;
- 视觉辅助定位:GPS 丢星时靠光流+视觉里程计顶上,室内与城市峡谷不再是禁区;
- 数据驱动调参:用历史飞行数据回归出更优的噪声参数,替代人工拍脑袋;
- 网络化 RTK 服务:走移动网络订阅差分服务,省掉自建基站的运维成本。
工具已经就位:模块化后端让你换接收机不用改架构,EKF 自适应融合让"好信号更被信任、坏信号被隔离"。剩下的功课只有一件——把天线架好、把链路调通、把RTK_FIXED等出来。定位到厘米级那一刻,你会发现原来的"米级世界",真的可以退场了。
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考