1. 协议选型的起点:为什么UBX和NMEA 0183总被放在一起比较
搞嵌入式定位模块开发的人,绕不开一个场景:拿到一块GPS/北斗模组,比如u-blox的NEO-M8N、MAX-M10S或者国产的ATGM336H,第一件事就是决定让模组吐什么格式的数据给主控。这时候摆在面前的基本就两个选项——NMEA 0183和UBX。前者是几乎所有GNSS模组出厂默认输出的文本协议,后者是u-blox自家的二进制协议。很多人第一次接触u-center的时候,看到Messages视图里NMEA和UBX两栏并列,随手勾了几个,结果串口助手收到一堆乱码,或者定位数据死活解析不出来,问题往往就出在没搞清楚这两个协议的本质差异。
这篇文章面向的是正在做嵌入式定位功能开发的工程师,不管你是用STM32裸机跑串口解析,还是在嵌入式Linux应用层用Python读串口,只要涉及到GNSS模组的数据通信,这两个协议的选择就会直接影响你的代码复杂度、CPU占用、定位精度和调试效率。我会从实际项目出发,把UBX和NMEA 0183的核心区别拆开讲清楚,每个区别都配上u-center里的实际操作和配置截图说明,最后给出一个可以直接抄的选型决策表。
先说结论性的判断:如果你只是要拿经纬度、时间、速度这几个基本量,NMEA 0183足够用,开发成本最低;如果你需要原始观测量、卫星方位角仰角、精度因子、或者要做RTK差分、做后处理,那必须上UBX。但实际情况往往比这个二分法复杂,很多项目是两者混用——用NMEA做快速验证,用UBX做正式数据链路。下面我把这5个关键区别逐一拆解。
2. 区别一:数据格式与解析方式——文本行 vs 二进制帧
2.1 NMEA 0183的文本行结构
NMEA 0183本质上是一套ASCII文本协议,每条语句以$开头,以\r\n结尾,字段之间用逗号分隔。最常见的是GPGGA(全球定位系统定位数据)和GPRMC(推荐最小定位信息)。一条典型的GPGGA语句长这样:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47解析这种数据,你只需要按逗号split,然后按位置取字段就行。用C语言写的话,一个strtok循环就能搞定;用Python更简单,pyserial读一行,line.split(',')直接出结果。这就是NMEA最大的优势——人眼可读,调试零门槛。你甚至不需要写代码,打开串口助手就能看到定位数据在滚动。
但文本协议的代价也很明显。一条GPGGA语句大约70-80字节,其中真正有用的数值信息可能只占一半,其余都是逗号和字母标识。如果模组每秒输出5条NMEA语句(GGA、RMC、GSA、GSV、VTG),波特率9600的情况下,串口带宽占用相当可观。更麻烦的是GSV语句,卫星信息多的时候一条装不下,会拆成多条,解析逻辑要处理这种分片情况。
2.2 UBX的二进制帧结构
UBX完全是另一套逻辑。它的每一帧都是二进制,结构固定:
| 字段 | 长度 | 说明 |
|---|---|---|
| Sync Char 1 | 1字节 | 固定0xB5 |
| Sync Char 2 | 1字节 | 固定0x62 |
| Class | 1字节 | 消息类别,如0x01表示NAV |
| ID | 1字节 | 消息ID,如0x02表示POSLLH |
| Length | 2字节 | 载荷长度(小端) |
| Payload | 变长 | 实际数据 |
| CK_A | 1字节 | 校验和A |
| CK_B | 1字节 | 校验和B |
以UBX-NAV-POSLLH(0x01 0x02)为例,它的载荷是28字节,里面直接是int32类型的经纬度(单位1e-7度)、海拔(毫米)、精度估计等。没有逗号,没有字母标识,全是紧凑的二进制数值。
解析UBX需要你按字节流处理:先找同步头0xB5 0x62,再读Class和ID判断是什么消息,然后读Length确定载荷长度,最后校验CK_A和CK_B。校验算法是8位 Fletcher校验,不难但必须写对,否则数据错了你都不知道。
注意:UBX的Length字段是小端序,但载荷内部的数值字段可能是小端也可能是大端,取决于具体消息定义。比如NAV-POSLLH里的经纬度是int32小端,但有些消息里的浮点数是IEEE 754小端。写解析代码前一定要对着u-blox的Interface Description文档确认字节序。
2.3 解析复杂度对比与实操建议
从代码量上看,NMEA解析可能50行搞定,UBX解析至少200行起步(含校验和状态机)。但从运行时效率看,UBX完胜——同样传输定位信息,UBX-NAV-POSLLH一帧只要36字节(含帧头帧尾),而NMEA的GGA+RMC组合至少140字节。在波特率受限或者需要高频输出的场景下,这个差距会直接决定你能不能跑到10Hz以上的更新率。
我的实操建议是:原型验证阶段用NMEA,正式产品根据需求切换。在u-center里切换输出协议很简单,打开View -> Messages View,在NMEA栏取消勾选不需要的语句,在UBX栏勾选NAV-POSLLH等消息,然后点击Send。如果你想彻底关掉NMEA只留UBX,需要发一个UBX-CFG-PRT消息把串口的输出协议设为只有UBX。这个操作在u-center的Configuration View里也能做,Protocol选UBX,然后勾选对应的输入输出协议。
3. 区别二:信息密度与可用数据范围——基础定位 vs 全量观测量
3.1 NMEA能给你什么
NMEA 0183的标准语句集覆盖了绝大多数常规定位需求:
- GPGGA:时间、纬度、经度、定位质量、卫星数、HDOP、海拔
- GPRMC:时间、日期、经纬度、速度、航向
- GPGSA:当前使用的卫星PRN、PDOP、HDOP、VDOP
- GPGSV:可见卫星的PRN、仰角、方位角、信噪比
- GPVTG:地面速度、航向
这些数据对于车载导航、轨迹记录、时间同步这类应用完全够用。但如果你要做的事涉及到原始测量值,比如载波相位、伪距、多普勒频移,NMEA就无能为力了。NMEA的定位结果是模组内部解算完的“成品”,你拿不到“原料”。
3.2 UBX能给你什么
UBX的消息类别覆盖了GNSS接收机的全部内部状态。常用的几类:
- UBX-NAV-*:导航解算结果,包括POSLLH(经纬高)、VELNED(速度)、SAT(卫星状态)、DOP(精度因子)、PVT(位置速度时间综合)
- UBX-RXM-*:原始测量数据,包括RAW(伪距、载波相位、多普勒)、SFRBX(子帧原始数据)
- UBX-CFG-*:配置消息,用来设置更新率、动态模型、端口协议等
- UBX-MON-*:监控消息,包括硬件状态、通信统计、射频干扰
其中UBX-RXM-RAWX是RTK和后处理应用的核心。它输出的每条卫星观测记录包含伪距、载波相位、多普勒、载噪比,这些是做差分定位的必备输入。NMEA里完全没有对应的数据。
3.3 信息密度差异的实际影响
我做过一个对比测试:同一块NEO-M8N模组,分别用NMEA和UBX输出,波特率都设115200,看每秒能传多少有效信息。
| 对比项 | NMEA 0183 | UBX |
|---|---|---|
| 单次定位数据量 | GGA+RMC约140字节 | NAV-PVT约92字节 |
| 卫星详细信息 | GSV分片,多颗星时超200字节 | NAV-SAT约8+12×N字节 |
| 原始观测量 | 不支持 | RAWX每颗星约32字节 |
| 10Hz更新率下带宽 | 约14KB/s | 约9KB/s(含RAWX则更高) |
| 可解析字段数 | 约30个 | 超过100个 |
这个表说明一个问题:UBX在同等带宽下能承载的信息量远大于NMEA。如果你的应用需要高频输出且要卫星级细节,NMEA的GSV分片机制会让你很头疼——卫星数超过4颗就要拆多条,解析代码得维护一个状态机来拼接。
实操心得:在u-center里查看RAWX数据,需要先发UBX-CFG-MSG把RXM-RAWX打开。默认情况下这个端口是不输出RAWX的,因为数据量太大。打开后你会看到Messages View里RAWX的计数在涨,但如果你用串口助手直接看,全是二进制乱码,必须用u-center的Packet Console或者自己写解析工具才能读。
4. 区别三:配置与控制能力——只读输出 vs 双向交互
4.1 NMEA的被动性
NMEA 0183在设计上是一个“输出协议”。模组通过串口往外吐语句,主控只管接收解析。你当然可以通过发特定的NMEA命令来配置模组,比如$PUBX,40或者厂商私有的$PCAS命令,但这些命令的标准化程度很低,不同厂商、不同型号之间不通用。u-blox模组虽然支持一些NMEA配置命令,但功能远不如UBX-CFG全面。
换句话说,NMEA模式下你对模组的控制力很弱。你没法通过NMEA设置动态模型(比如车载、步行、飞行),没法调整导航更新率,没法配置RTK模式,没法读取硬件状态。这些操作全部要走UBX-CFG消息。
4.2 UBX的完整配置体系
UBX-CFG是u-blox模组的配置核心。常用的配置消息包括:
- CFG-PRT:设置串口波特率、输入输出协议
- CFG-RATE:设置测量周期和导航更新率
- CFG-NAV5:设置动态模型、固定高度、2D/3D模式
- CFG-GNSS:设置启用哪些星座(GPS、GLONASS、Galileo、北斗)
- CFG-MSG:设置每个端口输出哪些消息、输出频率
- CFG-TMODE3:设置RTK基准站或流动站模式
这些配置在u-center里都有图形界面。比如设置更新率到10Hz:打开Configuration View,选CFG-RATE,把Measurement Period设为100ms,Navigation Rate设为1,然后Send。模组会返回一个ACK-ACK确认,如果配置不合法会返回ACK-NAK。
4.3 配置持久化与掉电保存
这里有一个很多人踩过的坑:UBX配置默认在掉电后丢失。你通过CFG-MSG设置了输出消息,断电再上电,模组又回到出厂默认状态。要保存配置,必须发送UBX-CFG-CFG消息,选择保存到BBR(电池备份RAM)、Flash还是EEPROM。
在u-center里的操作是:Configuration View -> CFG-CFG,勾选Save current configuration,选择对应的存储设备(0=BBR,1=Flash,2=EEPROM,4=RAM),然后Send。不同模组的存储能力不同,NEO-M8N有Flash,MAX-M10S只有RAM和BBR,掉电时间长了BBR也会丢。
注意:频繁写Flash会缩短寿命,一般只在配置确定后写一次。开发阶段建议存BBR,量产固件里再写Flash。
5. 区别四:精度与定位模式支持——单点 vs RTK与原始数据
5.1 NMEA的精度天花板
NMEA输出的定位精度取决于模组内部的解算结果。单点定位下,普通GNSS模组的水平精度在2-3米CEP左右,加了SBAS能到1-2米。NMEA的GGA语句里有HDOP字段,可以粗略判断当前精度,但仅此而已。你拿不到载波相位,做不了厘米级定位。
5.2 UBX对RTK的支撑
RTK(实时动态差分)需要基准站和流动站之间传输原始观测量或差分改正数。u-blox的RTK方案里,基准站通过UBX-RXM-RAWX输出原始观测,或者通过UBX-RXM-SFRBX输出导航电文,然后由外部处理器(比如嵌入式Linux上的RTKLIB)计算差分改正,再通过UBX-RXM-EPH或者RTCM消息发给流动站。流动站收到改正数后,内部RTK引擎解算出厘米级位置,再通过UBX-NAV-PVT或NMEA的GGA输出。
整个链路里,NMEA只在最后输出结果时出现,中间的原始数据传输全靠UBX。如果你想用u-blox模组做RTK,UBX是必选项,没有替代方案。
5.3 精度因子与卫星状态的可观测性
UBX-NAV-DOP消息直接给出几何精度因子、水平精度因子、垂直精度因子、时间精度因子,比NMEA的GSA语句更完整。UBX-NAV-SAT给出每颗可见卫星的方位角、仰角、载噪比、锁定状态,比NMEA的GSV更详细且不分片。
这些数据在做多传感器融合的时候很有用。比如你在做组合导航,需要根据卫星几何分布动态调整GNSS的权重,UBX-NAV-DOP和NAV-SAT提供的信息比NMEA丰富得多。
6. 区别五:调试与开发效率——u-center实操对比
6.1 u-center里的NMEA调试
u-center连接模组后,默认就是NMEA模式。Text Console里能看到滚动的NMEA语句,Packet Console里能看到解析后的字段值。如果你想看某个字段的变化趋势,可以打开View -> Docking Windows -> Data View,选GGA,然后看Latitude、Longitude、Altitude的实时数值。
NMEA调试的最大好处是所见即所得。串口助手收到什么,u-center里就显示什么,不需要额外的解析工具。如果你在写嵌入式代码,可以直接把串口助手收到的NMEA语句复制到代码里做单元测试,输入输出完全确定。
6.2 u-center里的UBX调试
UBX调试需要多一步操作:在Messages View里勾选你要看的UBX消息,然后Send。勾选后,Packet Console里会出现对应的消息条目,点开能看到每个字段的十六进制和十进制值。如果你想看NAV-PVT的详细内容,双击Packet Console里的PVT条目,会弹出一个窗口显示所有字段。
UBX调试的难点在于二进制不可读。如果你用串口助手直接看,只能看到B5 62开头的乱码。要验证你的解析代码是否正确,可以用u-center的Export功能把Packet Console里的消息导出成文本,然后跟你的代码输出对比。
6.3 混合模式的调试技巧
实际项目里最常见的配置是:NMEA和UBX同时输出。比如让模组在串口1输出NMEA给主控做快速定位,同时在串口2输出UBX给另一个处理器做RTK。或者同一个串口上,NMEA和UBX交替输出,主控根据同步头区分。
在u-center里配置混合输出:Messages View里同时勾选NMEA的GGA、RMC和UBX的NAV-PVT,设置不同的输出频率。比如NMEA 1Hz,UBX 5Hz。这样主控可以用NMEA做低频监控,用UBX做高频控制。
实操心得:混合输出时要注意带宽。如果波特率只有9600,同时开NMEA和UBX很容易丢数据。建议至少115200起步,或者用UBX-CFG-PRT把波特率调高。调波特率的时候,先改模组再改u-center,否则会失联。具体操作:Configuration View -> CFG-PRT,选UART1,把Baudrate改成115200,Send,然后立刻在u-center的Receiver -> Port里把波特率也改成115200,重新连接。
7. 选型决策表与常见问题排查
7.1 一张表帮你做决定
| 需求场景 | 推荐协议 | 理由 |
|---|---|---|
| 快速验证模组是否工作 | NMEA | 串口助手直接可读 |
| 车载导航、轨迹记录 | NMEA | GGA+RMC足够,开发快 |
| 时间同步(PPS+UTC) | NMEA或UBX | NMEA的RMC有UTC,UBX的PVT有纳秒时间 |
| 高频控制(>5Hz) | UBX | 带宽效率高,延迟低 |
| RTK差分定位 | UBX | 必须用RAWX/SFRBX |
| 多传感器融合 | UBX | DOP和SAT信息更全 |
| 低功耗休眠唤醒 | UBX | 配置和状态读取更灵活 |
| 兼容老设备 | NMEA | 工业标准,通用性强 |
7.2 常见问题速查
问题一:u-center连上了但看不到数据。检查波特率是否匹配,检查View -> Messages View里对应协议的消息是否勾选。如果之前发过CFG-PRT把协议关了,需要重新发一条CFG-PRT把协议打开。
问题二:UBX解析出来全是0或者乱码。先确认字节序,再确认校验和是否正确。校验和算错的话,数据字段的偏移会全部错位。建议先用u-center导出一段已知正确的UBX数据,用你的解析代码跑一遍,对比每个字段。
问题三:NMEA的GSV语句卫星数对不上。GSV是分片传输的,第一条的第三个字段是总条数,第四条是当前条数。解析时要先读总条数,再循环读所有分片,最后合并。
问题四:配置保存后重启又丢了。确认是否发了CFG-CFG保存命令,确认存储介质是否支持掉电保存。BBR需要VBAT引脚有电,如果VBAT没接电池,掉电后BBR也会丢。
问题五:同时开NMEA和UBX后数据丢包。降低输出频率或者提高波特率。也可以用CFG-MSG把不需要的消息关掉,只留核心的几条。
7.3 我的个人经验
踩过几次坑之后,我现在的习惯是:新项目先用NMEA跑通硬件链路,确认模组、天线、供电都没问题,然后切到UBX做正式开发。切换的时候不要一次性把所有NMEA都关掉,留一条GGA做备份监控,万一UBX解析出问题,还能用GGA确认模组本身是好的。
另外,u-center的Configuration View里有一个“Send”按钮,每次改配置都要点一下才生效。很多人改了参数没点Send,以为已经配置好了,结果模组还是按老参数跑。这个按钮的位置在Configuration View的底部,不太显眼,但很关键。
最后分享一个快速判断模组是否支持UBX的方法:在u-center里打开Messages View,如果UBX栏是灰色的或者勾选后没有数据返回,说明这个模组可能不是u-blox芯片,或者固件被裁剪了。国产的ATGM336H、中科微的AT6558这些模组,虽然也输出NMEA,但UBX协议不一定完整支持,选型时要确认清楚。