【ESP32S3 + ATGM332D GPS模块实战:从NMEA解析到本地速度计算】
- 项目概述
- 一、硬件准备
- 1.1 核心部件
- 1.2 硬件连接
- 二、软件配置
- 2.1 PlatformIO环境配置
- 2.2 核心库介绍
- 三、代码实现
- 3.1 完整代码
- 3.2 关键技术解析
- 3.2.1 NMEA数据解析
- 3.2.2 本地墨卡托投影(ENU坐标系)
- 3.2.3 速度与航向计算
- 四、测试结果
- 4.1 输出示例
- 4.2 数据对比分析
- 4.3 关键指标
- 五、常见问题排查
- 5.1 定位无效
- 5.2 速度为0
- 5.3 航向跳变
- 六、总结
项目概述
本文详细介绍如何使用ESP32通过硬件串口读取ATGM332D GPS模块数据,使用TinyGPSPlus库解析NMEA语句,并通过本地墨卡托投影(ENU坐标系)计算真实速度和航向。
源码地址:https://download.csdn.net/download/VOR234/93024101
一、硬件准备
先采用GnssToolKit3配置GPS使用模式为航海模式
更新速率为10Hz
配置波特率为9600
查看原始NEMA数据
查看收星情况
1.1 核心部件
| 部件 | 型号 | 说明 |
|---|---|---|
| 主控板 | ESP32-S3 | 开发板 |
| GPS模块 | ATGM332D | 支持GPS/北斗双模定位 |
| 天线 | GPS有源天线 | SMA接口 |
1.2 硬件连接
ATGM332D模块 ESP32-S3 TX ----------> GPIO17 (UART1_RX) RX ----------> GPIO18 (UART1_TX) VCC ----------> 3.3V GND ----------> GND注意事项:
- ATGM332D默认波特率为9600
- 使用硬件串口Serial1,避免占用调试串口
- GPS模块需外接有源天线并放置在开阔处
二、软件配置
2.1 PlatformIO环境配置
在platformio.ini中添加依赖库:
[env:freenove_esp32_s3_wroom] platform = espressif32 board = freenove_esp32_s3_wroom framework = arduino monitor_speed = 115200 upload_speed = 921600 lib_deps = mikalhart/TinyGPSPlus@^1.0.3 ; GPS NMEA解析库2.2 核心库介绍
TinyGPSPlus是一个轻量级、高效的NMEA解析库,特点:
- 支持所有标准NMEA语句
- 自动解析经纬度、时间、速度、航向等信息
- 内存占用小,适合嵌入式设备
三、代码实现
3.1 完整代码
#include<Arduino.h>#include<TinyGPSPlus.h>#include<math.h>// GPS模块配置#defineGPS_UARTSerial1// 使用硬件串口1#defineGPS_BAUD_RATE9600// ATGM332D默认波特率#defineGPS_TX_PIN18// UART1_TX#defineGPS_RX_PIN17// UART1_RX#definePRINT_INTERVAL_MS500// 2Hz输出频率// TinyGPSPlus对象TinyGPSPlus gps;// 参考点(初始位置作为本地坐标系原点)doublerefLat=0.0;doublerefLng=0.0;boolrefSet=false;// 上一时刻的位置和时间doublelastEast=0.0;doublelastNorth=0.0;unsignedlonglastTimeMs=0;// 计算得到的速度和航向floatcalcSpeed=0.0;floatcalcHeading=0.0;// 将经纬度转换为本地ENU坐标voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,double&east,double&north);voidsetup(){Serial.begin(115200);delay(500);Serial.println("ATGM332D GPS模块测试 - 本地墨卡托投影计算");// 初始化GPS串口GPS_UART.begin(GPS_BAUD_RATE,SERIAL_8N1,GPS_RX_PIN,GPS_TX_PIN);delay(1000);Serial.printf("GPS UART: Serial1, TX=%d, RX=%d, Baud=%d\n",GPS_TX_PIN,GPS_RX_PIN,GPS_BAUD_RATE);Serial.printf("输出频率: %d Hz\n",1000/PRINT_INTERVAL_MS);Serial.println("========================================");}voidloop(){// 持续读取并解析GPS数据while(GPS_UART.available()>0){gps.encode(GPS_UART.read());}// 定时输出(2Hz)staticunsignedlonglastPrintTime=0;if(millis()-lastPrintTime>=PRINT_INTERVAL_MS){lastPrintTime=millis();Serial.println("========================================");if(gps.location.isValid()){doublelat=gps.location.lat();doublelng=gps.location.lng();// 设置参考点if(!refSet){refLat=lat;refLng=lng;refSet=true;Serial.println("[INFO] 参考点已设置");}// 转换为ENU坐标doubleeast,north;latLngToENU(lat,lng,refLat,refLng,east,north);// 计算速度和航向unsignedlongcurrentTimeMs=millis();if(lastTimeMs>0&&refSet){doubledt=(currentTimeMs-lastTimeMs)/1000.0;if(dt>0.01){doubledx=east-lastEast;doubledy=north-lastNorth;// 速度: m/s -> km/hcalcSpeed=sqrt(dx*dx+dy*dy)/dt*3.6;// 航向: 弧度转角度,0-360度calcHeading=atan2(dx,dy)*180.0/M_PI;if(calcHeading<0)calcHeading+=360.0;}}// 更新状态lastEast=east;lastNorth=north;lastTimeMs=currentTimeMs;// 输出数据Serial.println("定位状态: 有效");if(gps.time.isValid()){Serial.printf("UTC时间: %02d:%02d:%06.3f\n",gps.time.hour(),gps.time.minute(),gps.time.second()+gps.time.centisecond()/100.0);}if(gps.date.isValid()){Serial.printf("日期: %04d-%02d-%02d\n",gps.date.year(),gps.date.month(),gps.date.day());}Serial.printf("纬度: %.6f°\n",lat);Serial.printf("经度: %.6f°\n",lng);Serial.printf("东向偏移: %.3f m\n",east);Serial.printf("北向偏移: %.3f m\n",north);Serial.printf("计算航速: %.6f km/h\n",calcSpeed);Serial.printf("计算航向: %.6f°\n",calcHeading);Serial.printf("原始航速: %.6f km/h\n",gps.speed.kmph());Serial.printf("原始航向: %.6f°\n",gps.course.deg());Serial.printf("卫星数量: %d\n",gps.satellites.value());if(gps.hdop.isValid()){Serial.printf("HDOP: %.2f\n",gps.hdop.hdop());}if(gps.altitude.isValid()){Serial.printf("海拔高度: %.2f m\n",gps.altitude.meters());}}else{Serial.println("定位状态: 无效");Serial.println("请将天线放置在开阔地带...");Serial.printf("已处理字符数: %lu\n",gps.charsProcessed());}Serial.println("========================================");Serial.println();}if(gps.charsProcessed()<10){Serial.println("等待GPS数据...");delay(500);}}// 经纬度转ENU坐标voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,double&east,double&north){constdoubleR=6378137.0;// 地球半径(米)doublelatRad=lat*M_PI/180.0;doublelngRad=lng*M_PI/180.0;doublerefLatRad=refLat*M_PI/180.0;doublerefLngRad=refLng*M_PI/180.0;doublecosLat=cos(refLatRad);east=R*(lngRad-refLngRad)*cosLat;north=R*(latRad-refLatRad);}3.2 关键技术解析
3.2.1 NMEA数据解析
TinyGPSPlus通过gps.encode()方法逐字符解析NMEA数据流:
while(GPS_UART.available()>0){gps.encode(GPS_UART.read());}解析后的关键数据:
gps.location.lat()- 纬度(十进制度)gps.location.lng()- 经度(十进制度)gps.speed.kmph()- 对地速度(km/h)gps.course.deg()- 航向角度(度)
3.2.2 本地墨卡托投影(ENU坐标系)
原理:将地球表面近似为平面,以首次定位点为原点建立东-北-天坐标系。
核心公式:
east=R*(lng-refLng)*cos(refLat)north=R*(lat-refLat)其中:
R= 地球半径(6378137米)refLat,refLng= 参考点经纬度east,north= 相对于参考点的东向、北向偏移(米)
3.2.3 速度与航向计算
// 速度 = 位移 / 时间calcSpeed=sqrt(dx*dx+dy*dy)/dt*3.6;// 航向 = arctan2(东向位移, 北向位移)calcHeading=atan2(dx,dy)*180.0/M_PI;说明:
- 速度单位转换:m/s × 3.6 = km/h
- 航向范围:0°(正北)→ 90°(正东)→ 180°(正南)→ 270°(正西)
四、测试结果
串口接收数据
4.1 输出示例
======================================== 定位状态: 有效 UTC时间: 13:01:11.600 日期: 2026-06-24 纬度: 38.869444° 经度: 121.532086° 东向偏移: -1.430 m 北向偏移: 2.727 m 计算航速: 3.079846 km/h 计算航向: 317.520691° 原始航速: 2.277960 km/h 原始航向: 323.150000° 卫星数量: 21 HDOP: 0.80 海拔高度: 82.00 m ========================================4.2 数据对比分析
| 参数 | 原始GPS数据 | 计算值(ENU) | 说明 |
|---|---|---|---|
| 航速 | 2.28 km/h | 3.08 km/h | 计算值更接近实际运动状态 |
| 航向 | 323.15° | 317.52° | 计算值基于实际位移方向 |
4.3 关键指标
- 定位精度:水平精度优于1米(HDOP < 1.0)
- 卫星数量:21颗(GPS+北斗双模)
- 更新频率:约10Hz(模块输出)
- 计算频率:2Hz(程序设定)
五、常见问题排查
5.1 定位无效
现象:定位状态: 无效
排查步骤:
- 检查天线连接是否牢固
- 确保天线在室外开阔处
- 等待至少30秒冷启动时间
- 检查串口波特率是否正确(ATGM332D默认9600)
5.2 速度为0
现象:计算航速: 0.00 km/h
排查步骤:
- 检查是否有连续的位置变化
- 确认参考点已设置(首次定位后)
- 检查时间差计算是否正确
5.3 航向跳变
现象:航向角度突变
原因:
- GPS定位存在微小抖动
- 低速运动时位移量小,噪声影响大
解决方案:
- 增加滤波算法(如卡尔曼滤波)
- 对连续多个采样点进行平滑处理
六、总结
本文实现了从GPS数据采集到速度航向计算的完整流程:
- 硬件层面:使用ESP32硬件串口与ATGM332D模块通信
- 数据解析:利用TinyGPSPlus库高效解析NMEA语句
- 坐标转换:通过本地墨卡托投影建立ENU坐标系
- 速度计算:基于位移差分计算真实运动参数
该方案适用于无人机、机器人、车辆等需要实时定位和运动状态监测的应用场景。
参考资料:
- TinyGPSPlus官方文档
- ATGM332D模块手册
- NMEA-0183协议标准