news 2026/6/25 13:49:38

【ESP32S3 + ATGM332D GPS模块实战:从NMEA解析到本地速度计算】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【ESP32S3 + ATGM332D GPS模块实战:从NMEA解析到本地速度计算】

【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/h3.08 km/h计算值更接近实际运动状态
航向323.15°317.52°计算值基于实际位移方向

4.3 关键指标

  • 定位精度:水平精度优于1米(HDOP < 1.0)
  • 卫星数量:21颗(GPS+北斗双模)
  • 更新频率:约10Hz(模块输出)
  • 计算频率:2Hz(程序设定)

五、常见问题排查

5.1 定位无效

现象定位状态: 无效

排查步骤

  1. 检查天线连接是否牢固
  2. 确保天线在室外开阔处
  3. 等待至少30秒冷启动时间
  4. 检查串口波特率是否正确(ATGM332D默认9600)

5.2 速度为0

现象计算航速: 0.00 km/h

排查步骤

  1. 检查是否有连续的位置变化
  2. 确认参考点已设置(首次定位后)
  3. 检查时间差计算是否正确

5.3 航向跳变

现象:航向角度突变

原因

  • GPS定位存在微小抖动
  • 低速运动时位移量小,噪声影响大

解决方案

  • 增加滤波算法(如卡尔曼滤波)
  • 对连续多个采样点进行平滑处理

六、总结

本文实现了从GPS数据采集到速度航向计算的完整流程:

  1. 硬件层面:使用ESP32硬件串口与ATGM332D模块通信
  2. 数据解析:利用TinyGPSPlus库高效解析NMEA语句
  3. 坐标转换:通过本地墨卡托投影建立ENU坐标系
  4. 速度计算:基于位移差分计算真实运动参数

该方案适用于无人机、机器人、车辆等需要实时定位和运动状态监测的应用场景。


参考资料

  • TinyGPSPlus官方文档
  • ATGM332D模块手册
  • NMEA-0183协议标准
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 13:49:14

如何解决小说创作中的组织混乱问题:使用Bibisco的完整解决方案

如何解决小说创作中的组织混乱问题&#xff1a;使用Bibisco的完整解决方案 【免费下载链接】bibisco Novel writing software 项目地址: https://gitcode.com/gh_mirrors/bi/bibisco 在小说创作过程中&#xff0c;作者常常面临角色管理混乱、情节结构松散、数据丢失风险…

作者头像 李华
网站建设 2026/6/25 13:47:16

Mac上使用VScode优雅开发STM32

这里插播一个小Tips适用于平时使用VSCode开发不同方向的朋友&#xff1a;我们可以使用VSCode的Profile功能实现不同开发的隔离&#xff0c;比如你要是个全才&#xff0c;你又会搞嵌入式&#xff0c;又搞AI&#xff0c;又搞JAVA等&#xff0c;那你就可以为自己不同的开发方向设置…

作者头像 李华
网站建设 2026/6/25 13:46:16

如何免费绕过iOS激活锁:3步实用解决方案

如何免费绕过iOS激活锁&#xff1a;3步实用解决方案 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n applera1n是一款专为iOS 15-16系统设备设计的激活锁绕过工具&#xff0c;为iPhone 6s至iPhone X用户…

作者头像 李华
网站建设 2026/6/25 13:38:14

Ai2Psd:重新定义AI到PSD转换的革命性脚本工具

Ai2Psd&#xff1a;重新定义AI到PSD转换的革命性脚本工具 【免费下载链接】ai-to-psd A script for prepare export of vector objects from Adobe Illustrator to Photoshop 项目地址: https://gitcode.com/gh_mirrors/ai/ai-to-psd 你是否曾经在Adobe Illustrator中精…

作者头像 李华
网站建设 2026/6/25 13:38:14

如何快速清理Windows系统:Win11Debloat完整使用指南

如何快速清理Windows系统&#xff1a;Win11Debloat完整使用指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and cust…

作者头像 李华
网站建设 2026/6/25 13:37:21

LarkMidTable数据中台:3大核心问题解决方案与5步实践指南

LarkMidTable数据中台&#xff1a;3大核心问题解决方案与5步实践指南 【免费下载链接】LarkMidTable LarkMidTable 是一站式开源的数据中台&#xff0c;实现中台的 基础建设&#xff0c;数据治理&#xff0c;数据开发&#xff0c;监控告警&#xff0c;数据服务&#xff0c;数据…

作者头像 李华