第四章 嵌入式平台搭建:STM32/PX4/ArduPilot平台选择、RTKLIB移植、EKF状态估计器配置、传感器融合框架搭建

各位同学,欢迎来到第四章。这一章,咱们要真刀真枪地干硬件和底层软件了。

说实话,很多做无人机飞控的朋友,一听到「嵌入式平台搭建」就头大。觉得不就是选个板子、刷个固件嘛?其实不然。你想想看,RTK厘米级定位要落地,底层平台选不对,后面全是坑。我当年在做一个农业植保机的项目时,就因为平台选型没想清楚,导致RTK数据融合进去后,EKF直接发散,飞机在天上画八字……嗯,那场面,至今难忘。

所以这一章,我会把三个主流平台——STM32、PX4、ArduPilot——掰开了讲。告诉你哪个场景该用哪个,RTKLIB怎么移植进去,EKF怎么配,传感器融合框架怎么搭。全是实战经验,没有废话。

4.1 平台选型:STM32、PX4、ArduPilot,到底选哪个?

这个问题,几乎每个做RTK融合的人都会问。我的回答很简单:看你的产品定位和团队基因

咱们先看一张对比表,心里有个底:

平台 核心优势 RTK融合难度 适合场景
STM32裸机/FreeRTOS 完全自主可控,资源占用极低 高(需手写EKF) 定制化飞控、工业级产品
PX4 (NuttX) 生态成熟,EKF2内置,支持RTK即插即用 中(需配置参数) 科研、快速原型验证
ArduPilot (ChibiOS) 代码可读性好,传感器驱动丰富 中低(有现成RTK插件) 教育、开源社区项目

我个人习惯:如果是做量产产品,我会选STM32+FreeRTOS。为什么?因为PX4和ArduPilot虽然方便,但里面有很多你不需要的模块,占Flash、占RAM,而且出了问题你很难改底层。但如果你是在做科研或者快速验证,PX4的EKF2模块直接支持RTCM数据输入,省事很多。

小提示: 如果你团队里没有能啃EKF源码的人,老老实实选PX4。别问我怎么知道的——我曾经硬着头皮在STM32上手写EKF,写了三个月,最后发现PX4的EKF2已经开源了……那种感觉,就像你爬到了山顶,发现人家是坐缆车上去的。

4.2 RTKLIB移植:从PC到嵌入式

RTKLIB是RTK定位的灵魂。但它的原始代码是跑在PC上的,要移植到STM32这类MCU上,有几个坎必须过。

第一步:裁剪RTKLIB

RTKLIB的源码里,有很多你不需要的东西。比如图形界面、文件读写、TCP通信。我们只需要核心解算引擎。我建议只保留以下模块:

  • rtkcmn.c — 公共函数
  • rtkpos.c — 定位解算
  • rtknav.c — 导航滤波
  • rtkobs.c — 观测数据处理
  • rtkcmn.h — 头文件

第二步:内存管理改造

RTKLIB在PC上喜欢用malloc动态分配内存。但在STM32上,尤其是实时性要求高的场景,动态分配是大忌。我建议全部改成静态数组,或者使用FreeRTOS的pvPortMalloc

看一段我实际改过的代码:

// 原版RTKLIB代码
double *obs = (double *)malloc(sizeof(double) * MAXOBS);

// 移植到STM32后的代码
#define MAX_OBS_STATIC 32
static double obs_buf[MAX_OBS_STATIC];
double *obs = obs_buf;

你看,就这么简单。但很多新手会忽略这一步,导致程序跑着跑着就HardFault了。

注意: RTKLIB的浮点运算量很大。如果你用STM32F4系列,建议开启FPU(硬件浮点单元),否则解算一次RTK位置可能要几百毫秒,根本没法用。我曾在STM32F407上测试过,不开FPU,单次解算耗时约800ms;开了之后,直接降到15ms。

4.3 EKF状态估计器配置

EKF(扩展卡尔曼滤波)是RTK融合的核心。说白了,它就是把GPS/RTK的位置、IMU的加速度和角速度、磁力计的航向,全部揉在一起,输出一个最靠谱的姿态和位置。

EKF的状态向量,我一般这样定义:

// 22维状态向量
// [0:2]   — 位置 (NED坐标系, 米)
// [3:5]   — 速度 (NED坐标系, 米/秒)
// [6:8]   — 姿态四元数
// [9:11]  — 加速度计偏置
// [12:14] — 陀螺仪偏置
// [15:17] — 地磁偏置
// [18:20] — 风速 (如果你做室外飞行)
// [21]    — 气压计高度偏移

配置EKF时,有几个关键参数必须调:

  • 过程噪声协方差矩阵Q:这个值设大了,EKF会太相信IMU,导致位置漂移;设小了,又跟不上动态变化。我一般先设一个经验值,然后通过实际飞行日志反推。
  • 观测噪声协方差矩阵R:RTK的观测噪声,我通常设0.02米(水平)和0.05米(垂直)。注意,RTK的垂直精度天生比水平差,这个要区分开。
  • 初始化协方差矩阵P:第一次上电时,P矩阵要设大一点,让EKF快速收敛。我习惯设成单位矩阵乘以100。
核心经验: 我在项目中遇到过一个问题——RTK信号偶尔会跳变,导致EKF发散。后来我在EKF里加了一个「残差检测」逻辑:如果RTK观测残差超过3倍标准差,就暂时不用这个观测值,只用IMU预测。这个技巧,救了我好几次。

4.4 传感器融合框架搭建

框架搭得好,后面写代码就像搭积木。我推荐一个分层架构,大家可以直接复用:

// 伪代码:传感器融合主循环
while(1) {
    // 1. 数据采集层
    imu_data = read_imu();          // 200Hz
    gps_data = read_rtk_uart();     // 10Hz (RTCM数据)
    mag_data = read_magnetometer(); // 100Hz
    
    // 2. 时间同步层
    // 注意:IMU和GPS的时间戳必须对齐!
    sync_data = time_sync(imu_data, gps_data);
    
    // 3. EKF预测步 (IMU驱动)
    ekf_predict(sync_data.imu, dt);
    
    // 4. EKF更新步 (观测驱动)
    if (new_gps_available) {
        ekf_update_gps(sync_data.gps);
    }
    if (new_mag_available) {
        ekf_update_mag(sync_data.mag);
    }
    
    // 5. 输出层
    output_position(ekf_get_position());
    output_attitude(ekf_get_attitude());
}

这个框架,说白了就是「谁有数据谁说话」。IMU负责高频预测,GPS/RTK负责低频修正。磁力计只在航向漂移时拉一把。

关于传感器融合,我再强调三点:

  1. 时间戳是命根子:IMU和GPS的时间戳必须统一。我习惯用STM32的定时器捕获GPS的PPS脉冲,然后给IMU数据打上硬件时间戳。软件时间戳?误差太大,别用。
  2. 数据质量要打分:不是所有GPS数据都能进EKF。我会根据RTK的定位状态(单点解、浮点解、固定解)给每个观测值一个权重。固定解的权重最高,单点解直接扔掉。
  3. 别忘了磁力计校准:硬铁和软铁校准不做,EKF的航向估计会偏到姥姥家。我一般在飞控上电后,让用户拿着飞机转几个「8」字,自动完成校准。
一个小技巧: 调试EKF时,别只看最终位置。把IMU的加速度残差、GPS的位置残差都打印出来。如果残差突然变大,说明某个传感器出问题了。我曾经靠这个技巧,发现了一个批次IMU的零偏超标问题。

本章知识体系总览

下面这张图,是我用SVG画的本章核心逻辑。你可以把它当成一个「导航图」,随时回来对照:

嵌入式平台搭建与RTK融合核心框架 平台选型 STM32 / PX4 / ArduPilot 根据场景选择 RTKLIB移植 裁剪源码 内存管理改造 EKF配置 状态向量定义 Q/R矩阵调参 传感器融合框架 数据采集 → 时间同步 → EKF预测/更新 → 输出 IMU(200Hz) + RTK(10Hz) + 磁力计(100Hz) 厘米级定位输出 位置 + 姿态 + 速度 注:箭头方向表示数据流与依赖关系

这张图把本章的四个核心模块串起来了。你从平台选型开始,然后移植RTKLIB,接着配置EKF,最后搭融合框架。每一步都踩实了,厘米级定位自然就出来了。

好了,第四章的内容就到这里。记住,嵌入式开发没有捷径,但有了正确的框架和参数,你能少走很多弯路。下一章,我们会深入RTCM电文的解析与注入,到时候见。


专注资料整理