1. 项目概述与核心思路拆解
1.1 极速越野组到底比什么
全国大学生智能车竞赛的极速越野组,从第21届开始独立设组,规则核心就一句话:小车在室外大场地跑一圈或者多圈,用时短者胜。场地没有传统室内竞速组的赛道边界线和挡板,取而代之的是GPS路径点、路锥或者喷漆标记,转弯半径大、跑动范围广,车速经常能飙到8m/s以上。相比电磁组和摄像头组,极速越野组最大的区别在于“绝对定位”——车必须知道自己在地图上的真实位置,而不是靠传感器沿路找线。
这一点决定了定位系统的核心地位。你光靠摄像头识别路锥,远距离下根本分不清目标;光靠编码器积分里程计,室外粗糙地面打滑两下位置就飘了。所以从组队第一天就得接受一个现实:GPS和惯导融合定位,不是“锦上添花”,而是这个组别的“生命线”。
我身边不少第一次接触这个组别的同学,上来就急着调PID、改机械,结果车一上赛道就满地乱跑——问题压根不是控制,是位置算错了。所以这篇内容我会从传感器选型、坐标转换、姿态解算、融合滤波到控制执行,把整条链路完整过一遍,重点讲清楚每个环节为什么这么做、踩过的坑在哪里。
1.2 为什么GPS离不开惯导
GPS的日本语叫全球定位系统,它在空旷环境下能给出绝对位置,误差一般能稳定在2米以内,用上RTK甚至能做到厘米级。但它有个致命短板:更新频率低。民用模块普遍5Hz到10Hz,算上系统内部处理和串口传输延迟,真正拿到数据时车已经往前跑了几十厘米;而且GPS是松散的时间序列,相邻两帧之间的位置信息完全空白,车辆转向、打滑、加减速时,纯靠GPS根本来不及反应。
这时候惯导(IMU,惯性测量单元)就派上用场了。IMU内部有陀螺仪和加速度计,能以200Hz、500Hz甚至更高频率输出角速度和加速度。它的特性跟GPS正好相反:短期精度极高,一毫秒内的角度变化和加速度变化都能量到,但长期运行会因为积分漂移而越偏越多——加速度计积分一次得到速度,再积分一次得到位移,任何微小的零偏误差都会被二次积分放大,几秒钟就飞了。
一个给你“绝对位置但不及时”,一个给你“及时变化但会漂移”,两者互补性非常明显。用专业一点的话说,GPS是低频绝对观测,IMU是高频相对推算,融合的本质就是用GPS修正惯导的累计误差,用惯导填补GPS的帧间空白。这也是捷联惯导(Strapdown Inertial Navigation)在智能车竞赛场景下的典型落地方式:IMU以车体为基准直接固定安装,通过姿态矩阵把载体坐标系的加速度变换到导航坐标系,然后积分出速度和位置,再由GPS每100毫秒或200毫秒来“校准”一次。
1.3 融合方案的整体架构
在真正动代码之前,先把整体方案在脑子里过一遍,这点非常关键。我推荐的架构是松耦合+扩展卡尔曼滤波(EKF)方案,也是竞赛圈里最成熟、性价比最高的做法。所谓松耦合,就是GPS和IMU各自独立处理数据,然后在结果层面融合——IMU积分的姿态、速度、位置作为预测,GPS解算出的位置、速度作为观测,丢进卡尔曼滤波器里做最优估计。
整个系统分为五个模块:传感器数据采集模块(GPS串口NMEA协议解析+IMU SPI/IIC读取)、姿态解算模块(把陀螺仪和加速度计融合成可靠的姿态角)、坐标转换模块(把GPS经纬度转成直角坐标)、组合导航滤波模块(EKF融合输出车体的位置、速度、航向角)、路径规划与控制模块(根据融合结果算横向偏差和速度指令)。后面所有章节都是围绕这五个模块展开的。
我见过不少队伍把代码写得特别复杂,一上来就上因子图、紧耦合、RTK,折腾了两个月还没跑通。说实话,极速越野组赛道就那么几百米往返,松耦合EKF完全够用,稳定、可调、好排查问题。先跑起来,再考虑优化,这句话在竞赛里永远是真理。
2. 传感器选型与硬件设计要点
2.1 GPS模块选型与天线设计细节
GPS模块的选择,极速越野组有两个主流方向:一是用带PPK/RTK功能的模块,比如华测、千寻位置方案,厘米级精度但价格高、依赖基站信号;二是用普通民用模块加上良好的融合算法,比如ATGM336H、NEO-M8N/M9N,成本只有几十块钱,配合EKF融合也能做到赛道级别的可用度。对大多数参赛队来说,我更推荐后者——先不说预算,RTK在校园场地基站信号覆盖不稳定,反而会带来额外的失效风险。
这里单独讲一个热搜里反复出现的话题:GPS陶瓷天线设计注意事项。很多队伍第一次画板子,把陶瓷天线直接贴在电机驱动附近,结果丢星严重、定位漂移特别夸张。陶瓷天线对干扰极其敏感,有几点必须注意:
一是天线必须放在整车最高点,周围金属物体尽量少,如果车壳是碳纤维的要特别小心——碳纤维对GPS信号有屏蔽效应,天线底部要有大面积接地铜皮,且与车身支架之间做好电气绝缘。二是陶瓷天线尽量远离大电流走线、电机、降压模块和数字总线,实测下来离电机功率线10厘米以上、离摄像头排线5厘米以上才算安全;如果实在躲不开,在天线下方加一层接地屏蔽片会有明显改善。三是天线馈线要用同轴线,且长度越短越好,不要为了布线方便绕一大圈。
注意:还有一种很容易忽略的干扰源是摄像头模块的MIPI/并口排线,高速翻转的数字信号会直接耦合进天线,表现为“跑直线时GPS偶尔跳变几十米”。排查方法很简单:把所有外设逐个断电,看GPS星数和定位稳定性变化。
2.2 IMU选型与安装固定
IMU选择上,竞赛车常见的型号有MPU6050、ICM20602、BMI088等。MPU6050虽然老,但资料多、调起来快;ICM20602噪声更低、温漂更小,适合高速场景;BMI088更皮实,抗振动性能好,很多无人机飞控在用。我的建议是:预算够就直接上带内置滤波的高端IMU,省下后期调参的功夫;预算有限的话,ICM20602是好选择。
安装方式对惯导数据的影响,经常被低估。IMU必须固定牢靠,用螺丝刚性锁紧在车架重心附近,不能有任何弹性垫片或者双面胶悬空——高速过弯时车架振动会直接成为陀螺仪噪声,双面胶垫着的话,IMU本身还会和车体产生微小的相对位移,产生额外误差。方向要对准车体前向,X轴朝前、Y轴朝左/右、Z轴垂直向上,并且用水平仪或者静止数据确认初始安装角度,软件里再做一次精确的安装角度标定。
2.3 硬件布局与抗干扰
GPS和IMU要尽量靠近车辆几何中心,同时各自满足安装要求——这在实际中往往是矛盾的,GPS要高,IMU要在重心。我的方案是:把GPS天线装在车顶碳板一角,IMU装在电池旁边的主控板上方,然后在软件里做杆臂补偿。所谓杆臂补偿,就是GPS天线所在位置跟IMU位置有几十厘米偏差,车辆旋转时两者测到的速度方向、加速度会有差异,需要根据角速度和杆臂向量做一个速度修正。这个细节在低速时无所谓,但极限速度下过弯,杆臂补偿能减少十几到几十厘米的横向误差。
电调和电机是最大的干扰源,除了天线距离问题,还要保证GPS模块的电源用单独的LDO单独供电,不要跟舵机、电机驱动共用电源。GPS模块纹波敏感,电机换相瞬间的电流尖峰会把GPS信号直接打没,导致周期性丢星。实测用独立LDO+π型滤波后,丢星次数会明显减少。
3. 数据解析与坐标转换
3.1 NMEA协议解析与原始数据判读
GPS模块最常见的数据协议是NMEA-0183,串口输出一串以“$”开头的ASCII文本。对极速越野组来说,最常用的语句是$GNGGA和$GNRMC。$GNGGA里有经纬度、定位质量、卫星数和海拔高度;$GNRMC里有经纬度、地面速度(节)、航向角,以及日期时间。解析并不难,按逗号分段取字段即可,但有几个点要特别注意:
第一是经纬度格式,NMEA里输出的是“度分”格式,比如“3109.45678”代表31度09.45678分,必须除以100取整得到度,余数除以60得到小数度,再相加才是十进制度数。很多新手在这里直接当小数用,位置能偏出上百公里。第二是要检查定位质量指示,GGA里的第6个字段,0表示无效、1表示单点定位、2表示差分定位,无效时数据要丢弃,绝不能拿来更新位置。第三是要做合理性检查,比如卫星数少于4颗不用、水平精度因子(HDOP)大于3不用、速度大于30m/s时直接认为是野值。
3.2 经纬度转平面坐标的实用方法
GPS输出的是WGS-84椭球下的经纬度(单位:度),这是一个球面坐标,但我们的控制算法需要平面直角坐标。投影方法有多种,最常用的是UTM投影和高斯-克吕格投影。UTM在全球范围内使用,按6度带划分,中国地区在49带到51带之间;高斯投影是国内测绘标准,用得也很多。不管用哪种,关键是需要一个固定基准点:开机时把第一个有效GPS点作为原点,后续所有点都相对于这个原点投影,这样小车在地图上的位置就是局部平面坐标,控制代码不需要处理很大的坐标数值。
这里给出一个简化版的等距投影做法,误差在几十公里范围内可以忽略,竞赛场地完全够用:以北向为Y轴、东向为X轴,以原点经纬度为基准,根据地球半径把纬度差乘以111320得到Y方向米数,经度差乘以111320再乘cos(纬度)得到X方向米数。这个方法的精度在场地范围(几百米内)能达到厘米级,而且代码极短、没有带号切换问题。需要更高精度时,可以换成标准UTM代码,网上有成熟的Python和C实现,直接抄就好。
import math R = 6371000.0 # 地球平均半径,单位米 lat0, lon0 = 31.123456, 121.654321 # 原点经纬度 def gps_to_xy(lat, lon): dlat = math.radians(lat - lat0) dlon = math.radians(lon - lon0) x = dlon * R * math.cos(math.radians(lat0)) # 东向,单位米 y = dlat * R # 北向,单位米 return x, y3.3 坐标偏移与高德经纬度问题
热搜词里有“python将gps经纬度转换为高德经纬度”,这个是很多做上位机轨迹显示的同学会遇到的问题。GPS拿到的原始坐标是WGS-84坐标系,而国内地图产品(高德、百度)因为合规原因会加偏移,高德用的是GCJ-02火星坐标系,百度用的是BD-09。如果你要把小车轨迹实时画到高德地图底图上,就需要做WGS-84到GCJ-02的转换,这个转换有公开的近似算法,用Python实现也就几十行。
但对竞赛本身而言,这个转换是不需要的——赛道路径点是组织方用GPS测量得到的,你的车和路径点都在同一个坐标系下,直接算相对距离和偏差完全没问题。只有在“上位机展示给评委看轨迹叠加在地图上”这种场景下,才需要做坐标偏移转换。我建议在开发阶段就把上位机的底图独立处理,不要在车上做任何坐标转换,省掉一组不必要的误差源。
3.4 时间同步与数据对齐方案
GPS和IMU的融合要求两组数据时间对齐,否则融合结果会引入相位滞后。GPS输出频率一般是5Hz或10Hz,IMU输出200Hz甚至更高,两者的时间基准不一致。最稳妥的做法是:让单片机在收到IMU数据时打上本地时间戳,收到GPS解析完成的时刻也打时间戳,然后在卡尔曼滤波更新时,用最近一次IMU的时间作为主时间线,找到离该时刻最近的GPS数据作为观测。如果GPS输出频率要求高,可以考虑用PPS秒脉冲来同步时间基准,但竞赛场景下用“时间戳+最近邻匹配”已经足够。
实操心得:GPS数据从天线接收、模块内部解算到串口输出,有几十到上百毫秒的延迟,而且是变化的。不要认为“读到串口的那一刻车就在这个位置”。我通常在融合之前做一个常数延迟补偿——把GPS观测时间戳往前推100毫秒左右再跟IMU状态做时间对齐,这个参数在调试时用高速直线往返跑标定,效果很明显。
4. 姿态解算与捷联惯导
4.1 姿态表达的三种方式与选择
惯导融合的第一步是得到可靠的姿态,这样才能知道加速度计测到的“沿车体系的加速度”在水平面上的分量到底是多少。姿态表达有三种常见方式:欧拉角、旋转矩阵、四元数。
欧拉角直观,但有万向锁问题,俯仰角接近90度时方程退化,车在竞赛中虽很少垂直,但上坡坡道角度大时依然会有隐患。旋转矩阵没有奇异性,但9个参数有冗余,计算量偏大。工程上最常用的还是四元数——4个参数、无奇异性、计算简单、便于插值,效率高。
三个量之间的关系需要清楚:陀螺仪测到的是载体坐标系相对导航坐标系的角速度,用四元数微分方程可以更新姿态四元数;加速度计测到的是比力(重力+运动加速度),在静止或匀速时可以用来校正陀螺仪的漂移;磁力计也能提供航向参考,但竞赛场地边上的铁栅栏、钢筋楼会导致磁场畸变,我自己是不太信任磁力计的——极速越野组通常只在场地边上跑,方向判别更多靠GPS航向和路径相对关系,磁场参考反而会带来错误修正。
4.2 先做姿态解算还是直接融合
这是很多人纠结的问题。在实际工程中,我建议先做姿态解算,输出稳定的欧拉角(横滚roll、俯仰pitch、航向yaw),然后把这个姿态作为已知量,再做位置速度融合。这一步逻辑上更清晰,调参时也好定位问题。
姿态解算的算法,首选Mahony互补滤波,代码短、效果稳、参数只有一个Kp和Ki。Madgwick效果也行,但计算量稍大。很多教程推的卡尔曼姿态解算在MCU上偏重,而且调参复杂,竞赛场景没必要。Mahony的核心思想是:把加速度计测得的重力方向作为参考,和姿态矩阵推算出的重力方向做叉积,得到误差,用PI控制器修正陀螺仪角速度,再用修正后的角速度更新四元数。
void mahony_update(float gx, float gy, float gz, float ax, float ay, float az, float dt) { float norm, ex, ey, ez; float q0q0 = q0*q0, q0q1 = q0*q1, q0q2 = q0*q2, q0q3 = q0*q3; float q1q1 = q1*q1, q1q2 = q1*q2, q1q3 = q1*q3; float q2q2 = q2*q2, q2q3 = q2*q3, q3q3 = q3*q3; norm = sqrtf(ax*ax + ay*ay + az*az); ax /= norm; ay /= norm; az /= norm; // 由四元数推算的重力方向 float vx = 2*(q1q3 - q0q2); float vy = 2*(q0q1 + q2q3); float vz = q0q0 - q1q1 - q2q2 + q3q3; // 误差向量 = 测量值叉乘推算值 ex = ay*vz - az*vy; ey = az*vx - ax*vz; ez = ax*vy - ay*vx; // 积分项 integralFBx += Kp * ex * dt; integralFBy += Kp * ey * dt; integralFBz += Kp * ez * dt; // 修正陀螺仪 gx += ex*Kp + integralFBx; gy += ey*Kp + integralFBy; gz += ez*Kp + integralFBz; // 四元数更新 q0 += (-q1*gx - q2*gy - q3*gz) * dt * 0.5f; q1 += ( q0*gx + q2*gz - q3*gy) * dt * 0.5f; q2 += ( q0*gy - q1*gz + q3*gx) * dt * 0.5f; q3 += ( q0*gz + q1*gy - q2*gx) * dt * 0.5f; // 归一化 norm = sqrtf(q0*q0+q1*q1+q2*q2+q3*q3); q0/=norm; q1/=norm; q2/=norm; q3/=norm; }Kp调大一点,姿态跟随快但容易引入加速度计噪声;Kp调小,姿态平滑但动态响应慢。我的经验是Kp从0.5起步,高速急刹、过减速带时观察姿态是否震荡,必要时再调Ki消除稳态偏差。
4.3 安装角标定与数据预处理
IMU不可能绝对水平安装,安装角度的微小误差会直接导致“重力漏进水平加速度”,进而造成位置漂移。我处理的方法是:车静止放在水平地面上,采集几十秒陀螺仪和加速度计数据,取平均作为零偏;同时根据加速度计三个轴的均值算出安装倾角,在解算前把载体坐标系的加速度扣除重力分量。
另外一个坑是非正交误差——多数IMU芯片出厂时做了正交校准,但为了保险,我习惯在代码里对这加速度计和陀螺仪各做一个三轴的灵敏度矩阵修正,这个可以通过IMU出厂自带的灵敏度值(如±16g量程对应的LSB/g)换算实现。ATGM336H和ICM20602的灵敏度值都在数据手册里能找到,换算错了会导致姿态解算输出偏大或偏小,表现就是车明明走直线,航向却在缓慢旋转。
重要:陀螺仪零偏会随温度变化,冬天室外场上车刚开机和跑了十分钟之后的零偏完全不一样。所以每次启动时都要做一次静止初始化(比如车在起点停2秒,程序自动采集50帧数据取平均),而不是在代码里写死一个常数。
5. GPS/惯导融合定位算法实现
5.1 松耦合EKF的状态量与观测设计
姿态解算已经给出了横滚、俯仰、航向,现在的问题是“位置在哪”。在极速越野组场景下,融合滤波器最核心的状态量包括:北向位置、东向位置、北向速度、东向速度、航向角误差、陀螺零偏。如果需要三维位置,也可以把垂向位置和垂向速度加进去,但赛道基本平坦,我通常不加,减少计算量,也避免垂向观测少导致的滤波发散。
状态方程用IMU来预测:北向和东向加速度分别是载体坐标系加速度经旋转矩阵投影到水平面的结果,积分得到速度,再积分得到位置。航向角的变化直接由陀螺仪Z轴角速度积分得来,同时把陀螺零偏作为慢变状态估计。
观测方程用GPS来修正:取当前时刻GPS解算出的北向、东向位置,以及从GPS多普勒速度转出的北向、东向速度($GNRMC里速度是以节为单位,要转换成米每秒,再根据航向角投影到北向和东向)。GPS位置噪声按单点定位通常给3-5米的标准差,速度噪声按0.1-0.3m/s,这两个参数对融合结果影响很大,可以在调试时用静止测试来确定。
5.2 滤波更新流程与代码骨架
整个EKF流程是标准的预测-更新循环:IMU每来一帧就做一次预测,GPS每来一帧就做一次更新。IMU的频率高,所以预测步执行频繁,GPS更新步低频执行,这正好符合松耦合的架构。
# 伪代码,只保留核心语句 class Ekf: def __init__(self): self.x = np.zeros(6) # [N, E, VN, VE, yaw_err, gyr_bias] self.P = np.eye(6) * 0.1 def predict(self, acc_N, acc_E, gyro_z, dt): # 状态预测 self.x[0] += self.x[2] * dt + 0.5 * acc_N * dt * dt self.x[1] += self.x[3] * dt + 0.5 * acc_E * dt * dt self.x[2] += acc_N * dt self.x[3] += acc_E * dt # 航向更新,用陀螺仪积分 self.x[4] += (gyro_z - self.x[5]) * dt # 状态协方差阵更新 F P F^T + Q # ... def update_gps(self, pos_N, pos_E, vel_N, vel_E): # 观测残差 y = np.array([pos_N - self.x[0], pos_E - self.x[1], vel_N - self.x[2], vel_E - self.x[3]]) # 卡尔曼增益 K = P H^T (H P H^T + R)^-1 # 状态修正、协方差修正 # ...这里的核心调试参数是过程噪声矩阵Q和观测噪声矩阵R。Q代表了我们对IMU推算的信任程度,R代表我们对GPS的信任程度。Q设得太大,融合结果偏信IMU,位置会飘;Q设得太小,结果偏信GPS,轨迹会抖动跳跃。我通常先在一次直线行驶中把数据记录下来,然后离线调Q和R,调到轨迹平滑且弯道跟随不滞后为止。
5.3 航向估计的特殊处理
极速越野组里面临一个很特别的问题:GPS航向在低速时特别不稳定,静止时航向完全随机跳变,而IMU航向有积分漂移。融合航向是整套系统的核心,因为航向直接决定了横向偏差和转向指令。
我的做法是把航向分成两层:融合滤波器内部使用IMU积分航向作为预测,GPS位置信息作为修正横向位置的手段,而不是直接用GPS航向去修正航向角。只有当车速大于2m/s时,GPS航向才加入航向观测;车速低于这个阈值时,GPS航向观测权重置为0,完全靠IMU和位置修正来维持方向。这个逻辑保证了硬件上低速机动时方向不会乱跳。
还有一个经验:在点与点之间的直线路段,利用“当前位置与目标路径点的连线方向”作为间接航向观测,能大幅抑制IMU航向漂移。具体实现方法是算出车到目标点的横向偏差和距离,反推期望航向,与当前航向做差,作为弱观测加入滤波器。效果非常明显,相当于用路径约束帮助定航向。
5.4 异常数据保护与失效处理
GPS数据不是一直都可靠的,异常值必须能自动剔除。我的保护逻辑分几层:第一层在解析时就过滤掉非固定解、卫星数少、HDOP大的数据;第二层做速率判别,如果GPS解算位置和上一次有效位置的距离除以时间差超过合理范围(比如大于10m/s),直接丢弃;第三层做新息卡方检验——在EKF更新之前,用卡尔曼预测值和GPS观测值算新息,如果新息大于3倍协方差阈值,说明GPS观测可能是野值,跳过本次更新。
这套“GPS生成式欺骗失效保护”的思路在竞赛里同样适用——不一定是对抗恶意欺骗,而是在信号丢失、干扰、多路径导致定位跳变时,保证融合结果不会跟着跳。实测下来,加了三层保护后,即使GPS短暂丢失10秒以上,惯性推算也能让车继续保持正确方向行驶,只是位置误差逐渐增大,一旦GPS恢复就能重新收敛。
6. 路径规划与控制执行
6.1 路径点处理与平滑
拿到赛道路径点之后(一般是若干个经纬度坐标点),先转成平面坐标,再进行预处理。原始路径点是折线形的,直接用来做跟踪会让车在拐点处急打方向,严重时直接甩尾冲出赛道。通常我会做两件事:一是去除离群点,把明显偏离赛道的经纬度点剔除;二是做路径平滑。
平滑方案有几种:三次样条插值、贝塞尔曲线拟合、移动平均滤波。我推荐用移动平均或B样条拟合,简单可控;如果追求更顺滑的轨迹,可以进一步用“曲率约束的路径平滑”,限制最大曲率,保证车辆在极限速度下侧向加速度不超限。平滑后的路径点密度要够,一般每10厘米一个点,方便控制算法查最近点。
6.2 横向控制:从Pure Pursuit到PID修正
极速越野组最常用的横向控制器是Pure Pursuit(纯追踪)和Stanley,两者都是几何方法。Pure Pursuit的核心思想是:以车当前位置为圆心,画一个半径为前视距离的圆,找到这个圆与路径的交点作为目标点,然后控制车辆朝目标点转向,使车辆始终“追着”前方的目标点走。
前视距离是唯一需要调的参数,它跟车速强相关:前视距离太小,车会在路径附近来回摆动;太大,过弯会切内线严重。我通常按车速比例动态调节:L = k * v + L_min,直线时用1.5米左右,高速时加到3米以上。弯道入口处可以额外判断路径曲率,曲率大就自动缩短前视距离。
# Pure Pursuit 关键步骤 dx = target_point[0] - vehicle_x dy = target_point[1] - vehicle_y alpha = atan2(dy, dx) - vehicle_yaw steer = atan2(2.0 * L * sin(alpha), L_forward)注意Pure Pursuit输出是期望转向半径或前轮转角,需要转换成舵机PWM或者差速比,这个映射关系要在实车上标定。我调试时先在场地里画同心圆,记录不同转角下车的实际转弯半径,做成查找表。
6.3 速度控制与赛道策略
速度控制的核心思想是“入弯减速、出弯加速”,但这个逻辑要跟定位精度配合起来。GPS融合输出的速度本身滞后且有噪声,所以我直接用IMU积分速度、融合后的GPS速度作为第二参考,速度环用PID控制。不建议直接对GPS速度做微分算加速度来控制油门,高频抖动会非常严重。
一个实用的赛道策略是:把路径点按曲率分段,曲率小的高速段全油门,曲率大的弯道口提前50到100米把速度降到阈值以下。提前量的计算要考虑当前车速和最大制动减速度,公式是:距离 = v^2 / (2 * a_max)。实测下来,用位置误差和航向误差来加权减速,比单纯按路径点曲率切分段更稳。
7. 常见问题与排查实录
7.1 GPS丢星与多路径效应
极速越野场地的GPS问题,最常见的是“跑一圈丢几次星”。原因通常是天线安装位置被车壳遮挡、在楼宇和树木旁多路径效应严重、以及周围有大功率射频设备干扰。多路径效应的表现很典型:位置不是缓慢漂移,而是突然跳到某个方向几十厘米甚至几米,之后再跳回来,轨迹图上能看到来回撞击的毛刺。
排查手法上,我习惯把GNSS卫星数和HDOP通过串口实时打印,跑车的时候旁边的人用手机日志记录下来。如果丢星集中在场地某个区域,说明那个位置有遮挡或强反射,要么改善天线位置,要么在路径规划时人为降低该区域的速度。如果丢星是随机全场的,优先检查天线周围有没有金属干扰源。
7.2 融合发散与参数异常
融合发散的表现是位置轨迹莫名飞出去,再也回不来,或者车辆跑直线但位置在缓慢漂移。先别急着改算法,按照下面几步排查:第一步看GPS原始点是否正常,如果原始点本身就是乱的,问题在硬件;第二步看IMU姿态是否稳定,静止时航向是否变化大,如果有,重新做零点校准;第三步看EKF预测值和观测值的新息,如果新息长期偏大,说明Q和R设定与实际噪声不符。我用离线数据回放工具反复调整,通常一小时能定位到问题。
7.3 上位机调试与数据回放
竞赛开发强烈建议提前做好上位机数据回放功能。串口输出包括时间戳、GPS原始坐标、融合坐标、姿态角、速度、控制量,全部按CSV格式记录到SD卡或通过无线模块发到电脑。Python脚本读取后,可画出轨迹、横向误差随时间变化曲线,还能叠加赛道路径点。
有个很实用的技巧:在车上固定一个颜色鲜艳的标记点,用无人机或者手机视频从高处俯拍车辆实际轨迹,然后和上位机记录的融合轨迹做对比。两边基本一致说明定位和控制都对;如果视频轨迹正常但融合轨迹歪了,问题在定位;如果融合轨迹正常但实车走得歪,问题在控制执行。这一步从根本上帮你分割问题域,少走很多弯路。
我个人在实际调车中最深的体会是:这套GPS与惯导融合系统的瓶颈往往不在算法本身,而在数据质量。天线没有摆好、IMU装得不够牢、供电纹波大,这些硬件问题会让任何高级算法都回天乏术;反过来,只要数据质量可靠,哪怕只是用卡尔曼滤波加一个简单的Pure Pursuit,也能在赛道上跑得很稳。先花时间把底层的传感器数据调理干净,后面所有环节都会痛快很多。