元器件 · 传感器
NEO-6M GPS 模块
NEO-6M 通过串口为户外机器人提供绝对位置。本文讲怎么读取 NMEA 语句、为什么 HDOP 比标称精度更重要,以及它做不到什么。
它是什么
GNSS 接收机:一块接收卫星星座广播的授时信号、并根据各信号到达时刻之差解算自身位置的芯片。NEO-6M 是入门机器人最先见到的模块——u-blox 接收机、陶瓷贴片天线和一个串口。
车上唯一能报告绝对位置的传感器。本站其他传感器——编码器、IMU、反射阵列——测的都是变化量或局部特征。正是 GPS 让里程计漂移不至于无界累积,也正是它让一台户外小车可以被告知一公里外的目的地。
工作原理
不用你问它,它 9600 波特率 NMEA 0183 句子往外说——一逗号分隔行一消息,每秒好几条。
其中两条就承载了机器人需要的一切:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,... fix quality, sats, HDOP
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,... validity, position, speed, course
读到定位解之后,先做有效性筛选。RMC 里的状态字段 A 表示有效、V 表示无效,而 GGA 里的定位质量为 0 表示无解——这两种情况下,句子照样带着看起来很合理的坐标。HDOP 字段抓的是更隐蔽的失效:它描述的是卫星几何分布,而几何分布差时,它会给出一个自信却偏出几十米的答案。
纬度是度分格式(4807.038 是 48° 07.038′),不是度。转一次,再转相对固定原点的米——算术在这里——下游全是控制器懂的单位。
什么时候用它
机器人在户外作业、行驶距离超过里程计能撑住的范围时用它:航点车、割草机、测绘平台、农业机器人。粗略地说,开了三十米之后,纯靠航位推算累积的误差就已经超过一次 GPS 定位所携带的误差。
室内永远别用,追求精度的时候也别用:2.5 m 对一辆 30 cm 宽的车来说实在太远了。GPS 告诉你身处田地的哪一块;轮子编码器告诉你在这块里的哪一米以内。两者都用,并做融合。
按价值排序的升级路径:先提高更新率(5 或 10 Hz 能消除大部分数据陈旧问题),然后换多星座模块(GPS 加上 GLONASS 或 Galileo 能改善几何分布,也就是改善 HDOP),最后才轮到 RTK——它能达到厘米级,但需要基站、数据链路,以及一份不同的预算。
常见坑
- **冷启动以分钟计,不是以秒计。**没有星历时,接收机必须以 50 bps 从卫星下载星历。机器人在户外首次上电时看起来像是死了,通常只是在等。
- **它没有航向。**只有位置。要配上磁力计或陀螺仪;对地航向在约 1 m/s 以下毫无意义。
- **往 RX 灌 5 V 会烧掉 3.3 V 模块。**查一下你的转接板有没有做电平转换。模块 TX 接 5 V 板子没问题。
- **天线要的是天空,不是视野。**它上方的金属——包括你自己的底盘板——都会让你损失卫星。把它装在顶部,平放、朝上。
- 绝不直接跟随原始定位解。在两次定位之间做航位推算,每来一个新解就修正一次;直接跟随定位解会让跟踪误差大一个数量级,转向次数是四倍。
- **六位小数是 11 cm——而且已经超出
float能存的精度。**换算成相对于任务原点的米,而不是拿原始的度数去做算术。
引脚与接线
| 引脚 | 名 | 作用 |
|---|---|---|
| 1 | VCC | 3.3–5 V。查自己转接板稳压没——很多 NEO-6M 板有 |
| 2 | RX | 模块收配置。裸模块 3.3 V 逻辑 |
| 3 | TX | 模块输出 NMEA。接 5 V 输入安全 |
| 4 | GND | 地 |
| 5 | PPS | 秒脉冲,对 GPS 时。大多数项目无视 |
| GPS | Arduino Uno | 说明 |
|---|---|---|
| VCC | 5 V | 板子有稳压 5 V;没有 3.3 V |
| GND | GND | |
| TX | 4 脚(软串口 RX) | 模块说,板子听 |
| RX | 经分压接 3 脚 | 只配置模块才要 |
RX 完全可以悬空。GPS 默认 9600 波特率 NMEA 一直流,不问也说,只读位置的车这就够。提更新率、静默无用句子才接 RX。
不用库读它
#include <SoftwareSerial.h>
SoftwareSerial gps(4, 3); // RX from module TX, TX to module RX
char line[100];
uint8_t len = 0;
void setup() {
Serial.begin(115200);
gps.begin(9600);
}
void loop() {
while (gps.available()) {
char c = gps.read();
if (c == '\n') {
line[len] = '\0';
if (strncmp(line, "$GPRMC", 6) == 0) handleRmc(line);
len = 0;
} else if (len < sizeof(line) - 1) {
line[len++] = c;
}
}
}
void handleRmc(char *s) {
// $GPRMC,time,status,lat,N/S,lon,E/W,speed,course,date,...
char *field[13] = {nullptr};
uint8_t n = 0;
for (char *tok = strtok(s, ","); tok && n < 13; tok = strtok(nullptr, ",")) field[n++] = tok;
if (n < 7 || field[2][0] != 'A') return; // 'V' = void. Coordinates are still present
// and still meaningless. Gate on this.
Serial.print(toDegrees(field[3], field[4][0]), 6);
Serial.print(' ');
Serial.println(toDegrees(field[5], field[6][0]), 6);
}
// NMEA gives ddmm.mmmm — degrees and DECIMAL MINUTES, not decimal degrees.
double toDegrees(const char *ddmm, char hemisphere) {
double raw = atof(ddmm);
int deg = (int)(raw / 100);
double minutes = raw - deg * 100;
double result = deg + minutes / 60.0;
return (hemisphere == 'S' || hemisphere == 'W') ? -result : result;
}
最要紧两行正是大家跳的两行。
field[2][0] != 'A' 是有效性判据。没有定位解的接收机照样会发出格式完美的 RMC 句子并带着坐标——通常是上次已知位置,或者全零。不检查状态标志的代码,会自信地把机器人导航到大西洋中央,也就是 0°N 0°E 所在之处。
toDegrees 之所以存在,是因为 4807.038 不是 4807.038 度,也不是 48.07038 度,而是 48° 07.038′,也就是 48.1173°。把 NMEA 坐标当成十进制度来用,是最常见的 GPS 缺陷,而误差大到不可能忽略——这倒是它唯一的好处。
真正的项目请用 TinyGPS++。它把所有句子类型、校验和以及各种边界情况都处理掉了。上面这个手写版本值得跑一遍,好让你知道它究竟在做什么。
精度:数字真意思
| 数 | 典型 NEO-6M | 含义 |
|---|---|---|
| 水平精度 | 约 2.5 m CEP | 一半的定位解落在 2.5 m 内,另一半落在外面 |
| 更新率 | 默认 1 Hz,可配 5 Hz | 在 1 m/s 下,1 Hz 的定位解在读到时最多已经旧了一米 |
| 冷启动 | 约 27 s,没有星历时以分钟计 | 没坏;它正以 50 bps 在下载 |
| 热启动 | 约 1 s | 有备份电源维持星历 |
| 速度精度 | 约 0.1 m/s | 确实不错——比位置精度还好 |
**CEP 是中位数,不是上界。**一半的定位解比 2.5 m 更差,而在建筑物附近这条长尾拖得更长。对一辆 30 cm 宽的车来说,“几米内的某个地方”不是一个位置——而是一片区域。
**HDOP 是要看的字段。**讲卫星几何不讲信号质量,自信答案偏几十米的就是它:
| HDOP | 质量 | 用吗 |
|---|---|---|
| < 1 | 理想 | 用 |
| 1–2 | 极好 | 用 |
| 2–5 | 好 | 小心用 |
| 5–10 | 一般 | 只慢修正 |
| > 10 | 差 | 丢弃这个定位解 |
有效标志和 HDOP 都要筛选。HDOP 为 12 的定位解不算定位解;那只是一个带小数点的猜测。
静态漂移,为什么要紧
把 GPS 模块纹丝不动地放十分钟,记录下每一个定位解。你会得到一团几米宽的散布点。什么都没动。
在写任何导航代码之前亲眼看到这一点,是最重要的一件事,因为直接跟随原始定位解为什么没有希望,答案就在这里。喂给控制器这样一团散布,控制器就会追着它跑,于是机器人持续蛇形前进却几乎没有进展。而在两次定位之间用编码器做航位推算、只把每个新定位解用来修正缓慢漂移的话,同一个控制器就能跟踪得很干净——航点导航教程里有具体数字。
配置模块
提高更新率是性价比最高的一项改动,值得在任何其他事情之前先做。u-blox 模块接受二进制 UBX 命令:
// UBX-CFG-RATE: 200 ms measurement period -> 5 Hz
const uint8_t setRate5Hz[] = {
0xB5, 0x62, 0x06, 0x08, 0x06, 0x00,
0xC8, 0x00, // 200 ms
0x01, 0x00, // one measurement per solution
0x01, 0x00, // align to GPS time
0xDE, 0x6A // checksum
};
void setup() {
gps.begin(9600);
delay(100);
gps.write(setRate5Hz, sizeof(setRate5Hz));
}
5 Hz 下 9600 波特率成瓶颈——全 NMEA 句子 200 ms 塞不下。要么波特率 38400,要么关不用的句子(GSV 尤其啰嗦,报车不需要的卫星细节)。
注意 NEO-6M 配置不存,模块无电池备份。每次开机发。
故障排查
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 完全无数据 | TX/RX 没交叉或波特率错 | 模块 TX → 板子 RX;先试 9600 |
| 有数据但永远没有定位解 | 室内 | GPS 需要天空,一扇窗户通常不够 |
| 首次定位要几分钟 | 没有星历的冷启动 | 正常。到开阔的户外去等 |
| 位置 0,0 | 用了无效的定位解 | 按 RMC 状态标志 A 做筛选 |
| 位置离谱 | 把 ddmm.mmmm 当成了度 | 换算:deg + minutes/60 |
| 位置跳几米 | 接收机的正常噪声 | 正常现象。与里程计融合;绝不直接跟随原始定位解 |
| 定位解很自信但偏得很远 | 卫星几何分布差 | 丢弃 HDOP 高于约 5 的定位解 |
| 卫星比预期少 | 天线被遮挡 | 装在顶部朝上,避开底盘板 |
| 模块发烫死 | RX 灌 5 V 进 3.3 V | 接线前查板子有没有做电平转换 |
| 设成 5 Hz 后工作一会儿就停 | 9600 波特率带不动这些句子 | 提高波特率或关掉 GSV |
选哪个模块
| 模块 | 星座 | 速率 | 说明 |
|---|---|---|---|
| NEO-6M | GPS | 1–5 Hz | 便宜默认。老但到处都是 |
| NEO-M8N | GPS + GLONASS + Galileo | 最高 10 Hz | 几何分布好、HDOP 好——合乎理性的升级 |
| NEO-M9N | 支持多频段 | 最高 25 Hz | 城区环境明显更好 |
| ZED-F9P(RTK) | 多频 | 20 Hz | 厘米精度,要基站数据链 |
升级路按序走。更新率去大半陈旧,白送。多星座接收机见星多,几何好即 HDOP 好——精度规格之外真实世界提升大,尤其楼边。RTK 能达到厘米级,对一台需要在田里沿着线条走的机器人来说是质变,但它需要基站、修正数据链路,以及大约十倍的预算。
任何价位、任何型号的 GPS 模块都不给你的一样东西:静止时的航向。对地航向是从连续位置推算出来的,所以在约 1 m/s 以下它全是噪声,零速时更毫无意义。一台需要知道自己朝哪儿的机器人,无论 GPS 多好,都需要磁力计或陀螺仪。
探索关联图谱
用到它的搭建
包含 NEO-6M GPS 模块 的项目、学习路线和模拟器。
问题
NEO-6M GPS 模块 常见问题
NEO-6M 实际能有多准?
大约有一半时间误差在 2.5 米以内,前提是天空视野开阔、卫星几何分布良好——这是数据手册上的数字,对一台在建筑物旁边或树下行驶的机器人来说是偏乐观的。重要的是,它精度变差时给出的是自信的错误答案,而不是明显的故障,所以每一个定位解都要按上报的定位质量和 HDOP 进行筛选,而不是因为解析成功就相信这个位置。
HDOP 是什么?为什么它比精度数字更重要?
水平精度因子(HDOP)描述的是当前可见卫星的几何分布。卫星在天空中分散开来,解算的条件就好;卫星挤在一起,解算的条件就差,而接收机照样会以十足的信心上报一个位置。HDOP 低于 2 算好,高于约 2.5 就值得丢弃。它是 GGA 句子里最有用的一个字段,却几乎没人去读。
GPS 模块能告诉机器人它朝向哪里吗?
不能。定位解只是一个位置,一台面朝北静止不动的机器人和一台面朝南的机器人,得到的定位解完全相同。对地航向是由连续的位置计算出来的,所以只有机器人真正在移动时才有意义——速度低于大约每秒 1 米时,它基本上就是噪声。户外漫游车会把 GPS 与磁力计(提供绝对航向)或陀螺仪(提供短期航向)搭配使用,或者两者都用。
为什么机器人沿 GPS 航点行驶时会蛇形摆动?
因为 1 Hz 的定位解既陈旧又有噪声,而直接喂原始定位解的路径跟踪器会朝每一个新解猛扑过去。在同一条路线上、用同一个控制器实测,带米级噪声的 1 Hz 定位解会把 0.09 m 的均方根横向误差放大到 1.25 m,并让转向动作增加到四倍半。解决办法是在两次定位之间做航位推算,用每个新的定位解去修正这个估计,而不是替换它。
在室内能用吗?
基本上永远不能,而这并不是故障。GPS 卫星的信号到达时比热噪声底还弱,需要毫无遮挡的天空;一个屋顶就能把它挡住。放在窗台上的模块有时能捕获卫星,但会因多路径效应报告很大的误差。请在户外开阔处测试,并为冷启动留出一分钟,再下结论说有东西坏了。
用 5 V 还是 3.3 V?
大多数转接板带有稳压器,VCC 可以接 5 V,但模块自身的逻辑是 3.3 V。在没有电平转换的板子上把 5 V 灌进 RX,是这类模块最常见的死法。请查清你手上那块板子的情况,拿不准就让 RX 经过一个分压电路——模块的 TX 接到 5 V 单片机上则没问题,因为 3.3 V 已经超过 AVR 的逻辑高电平阈值。
延伸阅读