
无人驾驶(二)---室外导航之RTK配置与接入及GPS与UTM坐标转换1. 为什么无人驾驶室外导航离不开RTK靠普通GPS真的会翻车去年我做了一台无人小车在园区里跑得好好的一出园区大门就开始“画龙”。不是电机的问题也不是转向标定的问题最后用日志一看定位轨迹在整个路口来回跳了七八米。那一刻我意识到室外无人驾驶的精度天花板根本不在于控制算法而在于位置传感器本身。普通消费级GPS在开阔地带大概能到2到5米的精度在树荫、楼群附近直接变成“薛定谔的定位”——每一帧数据都不可信而无人驾驶需要的是厘米级别的定位尤其是在车道保持、自动泊车、精准停靠这些动作里30厘米的误差就足以让车辆骑上马路牙子。这就是为什么RTKReal-Time Kinematic实时动态载波相位差分成了室外导航的主力方案。RTK和普通GPS的本质区别在于它不依赖GPS卫星播发的伪距码而是直接利用卫星信号的载波相位做差分计算。用一个比喻来说普通GPS是靠“大约在几点”来估算距离RTK则是用“从几点几分几秒到几微秒”来精确测量距离。再加上一个已知坐标的基准站做差分把大气误差、卫星钟差这些公共误差全部抵消掉移动站在开阔环境下能稳定达到厘米级固定解。RTK配置和接入看起来就是接天线、连串口、配波特率、发RTCM数据真正落地时会遇到一堆离谱问题固定解出不来、坐标跳变、转换后轨迹扭曲、天线安装位置不合适导致多路径效应……这篇文章我先讲清RTK为什么是刚需再把从硬件选型到接入配置、从坐标转换到实测排障的完整思路串起来给准备在室外无人平台上用RTK的朋友一个可以参考的路径。2. RTK硬件选型与天线安装的实用心得2.1 基准站和移动站两个站缺一不可RTK系统至少需要一个基准站和一个移动站。基准站的位置必须是已知的它接收卫星信号并计算观测误差然后通过电台、WiFi或4G把修正数据发给移动站。移动站装在车上也接收卫星信号同时接收基准站发来的差分数据两边的载波相位做差消除公共误差得到高精度坐标。选型时两个站要一起考虑。对我这个项目来说选的是某款u-blox F9P模块因为它原生支持RTK解算同时输出原始观测数据和RTCM 3.3格式价格也不算离谱。如果你只是想快速验证也可以买成品的RTK套件比如千寻、中海达、华测的移动端配一个CORS账号省去自建基准站的麻烦。但自建基准站能帮你理解整个链路后续调试更顺手。2.2 天线安装位置所有“玄学”问题的大本营RTK天线听起来就是“粘在车顶就行”实际上它是全车最容易出Bug的器件。我踩过的最大一个坑是天线装在了车顶金属横梁旁边导致多路径效应严重。多路径效应是卫星信号打到地面、墙体、金属架上再反射进天线相当于天线听到了回音相位观测值被污染固定解频繁丢失定位轨迹一抖一抖。天线安装的几个硬性规则尽可能放在车身最高点四周270度范围内不要有遮挡物天线自身的接地面要水平。远离大块金属面、光伏板、金属货架、摄像头支架至少保证50厘米以上的净空。陶瓷天线更要注意接地如果天线底部没有直接和车身金属接触信号重心会偏低固定的效果很糟糕。线缆尽量用原装的低损耗同轴线不要自己压接头接头处进了一次水整条链路就报废。当时我们测过同一个天线放在车顶中间和放在行李箱边缘的位置固定解的比例从92%降到了64%这个数据比任何理论解释都直观。2.3 核心参数波特率、数据格式、天线相位中心大多数RTK模块通过串口或USB口输出数据接入时先确认波特率是否匹配常见的有38400、115200也有默认9600的。数据格式方面移动站最容易忽视的是要输出带质量标识的GGA语句其中第6个字段“定位质量标识”能区分单点解、浮点解和固定解这是排查问题的第一入口。天线相位中心和机械中心之间其实存在一个偏移量高精度场景下必须校准。简单做法是把天线固定在一个已知点比如用全站仪测过的地钉静止采集两分钟以上把输出坐标和已知坐标的差值作为一个常数修正。如果你只是做室外导航、不追求绝对地理坐标的厘米级这个偏移量可以暂时忽略但别忽略之后拿全站仪来“打假”不然落差会非常尴尬。3. RTK配置与接入的完整操作流程照着做就能跑通3.1 从零开始搭建一套迷你RTK这里以u-blox F9P 树莓派为例写一遍我实际用过的配置过程。前提是树莓派里装好了串口工具比如Python的pyserial。第一步是接线。F9P开发板上有5V和GND排针串口TX/RX分别接树莓派的RX/TX别反接我当时把TX接TX结果什么都没有还以为是模块坏了。接好之后在树莓派上执行ls /dev/ttyAMA0确认串口节点存在如果用的是USB转串口那可能是/dev/ttyUSB0。第二步是配置输出消息。F9P在上电后默认可能不放任何数据需要用U-Center软件Windows先连接一次在UBX-CFG-MSG里面把NMEA里的GGA、GST、RMC打开同时设置输出频率——我一般设10Hz太高占带宽太低控制算法会滞后。第三步是启动基准站。基准站需要知道自己的精确坐标常见方式有两种一种是根据官方状态查询到的坐标输入进去另一种是在同一位置连续静态采集10分钟以上用模块自带的Survey-in模式自动收敛出一个坐标。我推荐先用静态采集一次然后手动固定坐标避免重复收敛慢的问题。第四步是启动移动站的base station数据输入。如果你是自建链路基准站的RTCM 3.3仿真数据通过串口或者TCP传到移动站如果购买CORS账号则通过ntrip协议拉取差分数据。第五步是确认固定解。看GGA输出中的第6个字段值为4代表固定解值为5代表浮点解值为1代表单点解。如果你看到浮点解持续了好几分钟先检查天线遮挡和差分数据质量而不是调整代码参数。3.2 实测中的数据流与配置项检查我用一个表格整理我常用的检查项每次接入新硬件都能靠它快速定位问题检查项预期状态异常可能的根因串口连接能持续输出NMEA或UBX帧接线反了、波特率不一致、串口号错误GGA语句存在每秒10帧左右配置消息未开启重新用U-Center配置卫星数至少10颗以上环境遮挡严重天线位置不对信噪比40 dBHz以上天线损坏、线缆接头松动、天线未接地差分数据输入移动站能接收到RTCM帧基准站未启动、端口或网络不通、协议格式错误定位质量标识固定解4基线长度超过20km、大气扰动大、差分中断这里有一条容易忽略的链路问题如果你用笔记本电脑连接移动站直接用USB转串口模块供电电流不够可能导致模块自动降频或者重启时不时掉固定解。换个质量好的、能提供500mA以上电流的电源就好很多。3.3 罗盘与惯导的初步融合RTK不是万能的RTK的更新频率一般到10Hz在车辆高速运动时是不够的而且在隧道、立交桥底、高楼区信号会被完全遮挡。就算固定解一直存在RTK给出的坐标也有“噪声毛刺”直接拿去做路径跟踪车辆会轻微抖动。建议把RTK当成一个绝对位置观测值用扩展卡尔曼滤波或因子图融合IMU惯性测量单元和轮速计才能得到平滑、高速率、鲁棒的定位结果。我自己的经验是在融合前先对RTK坐标做一步粗处理后验处理如果与上一帧位移超过2米同时车速小于0.5m/s大概率是跳变直接丢掉这一帧。别小看这个暴力删帧它对IMU融合初期的稳定性帮助特别大。4. 从GPS到UTM坐标系背后的原理与转换实现4.1 经纬度和米制坐标谁更适合无人驾驶GPS卫星直接给出的是WGS84坐标系下的经度、纬度和高度单位是度而无人驾驶控制层和路径规划通常希望坐标是米制单位。你可以用正弦余弦算两点的距离但每帧都这么做代码冗长且容易出错。更好的办法是把经纬度投影到平面坐标最常用的是UTMUniversal Transverse Mercator通用横轴墨卡托坐标系。UTM的原理可以这样理解把地球按经度每6度分一个带全球共60个带然后用一个横轴圆柱去切地球把带内区域投影到平面上。为了控制变形UTM规定中央经线比例因子为0.9996这样在中央经线附近长度变形最小偏离中央经线越远变形越大。对无人驾驶这种“局部区域作业”的场景跨带情况很少用UTM非常合适。4.2 手写一个WGS84转UTM的可直接用的函数写转换代码时直接调用PROJ库最省事但如果你只是做快速验证不想引入大依赖我这里给一个Python实现基于标准椭球参数推导精度在亚毫米级。import math def wgs84_to_utm(lat, lon): # WGS84椭球参数 a 6378137.0 f 1.0 / 298.257223563 k0 0.9996 zone int((lon 180) // 6) 1 lon0 (zone - 1) * 6 - 180 3 # 中央经线 lat_rad math.radians(lat) lon_rad math.radians(lon) lon0_rad math.radians(lon0) e2 2 * f - f * f e_prime2 e2 / (1 - e2) N a / math.sqrt(1 - e2 * math.sin(lat_rad) ** 2) T math.tan(lat_rad) ** 2 C e_prime2 * math.cos(lat_rad) ** 2 A math.cos(lat_rad) * (lon_rad - lon0_rad) M a * ((1 - e2 / 4 - 3 * e2 ** 2 / 64 - 5 * e2 ** 3 / 256) * lat_rad - (3 * e2 / 8 3 * e2 ** 2 / 32 45 * e2 ** 3 / 1024) * math.sin(2 * lat_rad) (15 * e2 ** 2 / 256 45 * e2 ** 3 / 1024) * math.sin(4 * lat_rad) - (35 * e2 ** 3 / 3072) * math.sin(6 * lat_rad)) x k0 * N * (A (1 - T C) * A ** 3 / 6 (5 - 18 * T T * T 72 * C - 58 * e_prime2) * A ** 5 / 120) y k0 * (M N * math.tan(lat_rad) * (A ** 2 / 2 (5 - T 9 * C 4 * C * C) * A ** 4 / 24 (61 - 58 * T T * T 600 * C - 330 * e_prime2) * A ** 6 / 720)) # 北半球Y偏置 y 10000000.0 if lat 0 else 0.0 x 500000.0 # X方向偏置 return x, y, zone这个函数的输出是UTM东向Easting和北向Northing单位为米。有了这个局部平面坐标你可以直接计算车辆当前位置和路径点之间的距离、航向角做路径跟踪时不再有“经度和纬度怎么相加”的尴尬。4.3 UTM转换中必须避开的三个坑UTM看起来简单实际使用时有几个非常容易踩的坑。第一是中央经线没设对如果车在120度附近自动算出来zone50中央经线117度但RTK输出的经纬度可能略有波动导致zone号在49和50之间跳变坐标跳变上万米。这时需要强制固定一个zone不能动态切换。第二是处理y轴偏置南半球需要加10000000米北半球不加。如果你在国内工作忘了加偏置也不会马上发现但一旦做全球部署就会出错。我建议不管在哪代码里都显式判断纬度正负。第三是使用基准面的微小差异WGS84和CGCS2000在绝大多数情况下差值小于1米对室外导航够用但如果你做进港靠泊之类的厘米级项目就需要做七参数转换否则绝对位置会整体偏移。5. 实测中的坐标漂移问题与完整排查链路5.1 一次“固定解消失”的现场复盘有一次我们在一个新建园区测试固定解比例只有30%严重时几分钟都回不来。现场环境很开阔理论上传星条件很好。我当时的排查顺序是先看卫星数量正常情况下12颗以上现场却有30多颗但信噪比普遍低于35dBHz。这个现象说明不是数量问题。再看差分数据基准站输出的RTCM帧在电脑上解码正常但移动站接收后没有任何响应。我以为是蓝牙不匹配换两根串口线、调半天还是不行。最后重新扫描了天线发现天线正上方2米处有一根高压电线强电场干扰了信号接收天线整体放在了车顶空调外机和新风管道之间形成了严重的电磁干扰和多路径混合。处理办法把天线挪到车顶最前方远离高压线路重新启动固定解比例恢复到95%以上。这个案例给我最大的启发是RTK调试不要盯着代码看先看物理环境。任何“软件不管用”的问题80%都是“硬件安装不过关”。5.2 坐标转换之后的轨迹偏移排查当RTK坐标转成UTM后如果轨迹和实际道路之间出现“整体偏移几米”的情况最该检查的是UTM带的中央经线。有一个真实案例车辆在zone 49和zone 50交界的城市运行代码里自动算zone的时候偶发跳变导致轨迹在某个路口忽然从X140000跳到X500000整个路径全部错乱。解决方法是固定一个区域用的zone比如整个城市固定为zone 50然后做一个全网校验。另一个容易被忽略的是RTK模块输出经纬度时某些固件默认输出的是WGMS84也未开启度分秒格式导致纬度从31.123456写成31°1234.56转换时直接偏了一个数量级。用转换工具前先打印原始GGA语句看到底是十进制度数还是度分格式确保无误。5.3 常见误区与经验总结误区一RTK固定解就是绝对精确。实际上固定解的定位噪声在1到3厘米但累计漂移和坐标转换误差会叠加需要定期用全站仪或地面标识校验。误区二UTM坐标是平面直线。UTM是投影坐标在大范围路径规划时必须考虑跨带切换不能把两个不同带的坐标直接相减求距离。误区三CORS账号一定稳定。实际使用中运营商网络波动会导致差分数据中断固定解会降级成浮点解甚至单点解一定要在代码里对定位质量做降级处理。6. 把RTK接入无人驾驶系统的最后一步这篇文章讲到的内容其实就是我在做室外无人车时最常用到的一条完整链路选天线、装天线、配模块、看GGA质量、确认差分数据、转UTM坐标、排查漂移。整个过程看起来琐碎但每部都会有惊喜。记得有一次在楼群环绕的停车场RTK固定解只剩不到10%我一度怀疑模块坏了后来发现是基准站在楼顶被一个塔吊结构挡住了天空。个人体会最深的一次是把RTK融入闭环之后发现车辆在路口的停靠精度从半米直接提升到十几厘米那种“肉眼可见”的变化让我觉得前面所有折腾都值得。如果你刚开始接触RTK和UTM建议不要一上来就追求高大全先搭一个最简单的静态基准站加移动站在露天场地把固定解搞到90%以上再考虑动态融合。下次我再展开讲讲怎么把RTK和IMU做松耦合融合以及如何处理一段时间内RTK信号丢失时定位连续性不崩的方法。这个系列我会持续更新希望这篇能帮你少走一点弯路的弯头。