【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 速度为05.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 完整代码#includeArduino.h#includeTinyGPSPlus.h#includemath.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;// 参考点初始位置作为本地坐标系原点doublerefLat0.0;doublerefLng0.0;boolrefSetfalse;// 上一时刻的位置和时间doublelastEast0.0;doublelastNorth0.0;unsignedlonglastTimeMs0;// 计算得到的速度和航向floatcalcSpeed0.0;floatcalcHeading0.0;// 将经纬度转换为本地ENU坐标voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,doubleeast,doublenorth);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());}// 定时输出2HzstaticunsignedlonglastPrintTime0;if(millis()-lastPrintTimePRINT_INTERVAL_MS){lastPrintTimemillis();Serial.println();if(gps.location.isValid()){doublelatgps.location.lat();doublelnggps.location.lng();// 设置参考点if(!refSet){refLatlat;refLnglng;refSettrue;Serial.println([INFO] 参考点已设置);}// 转换为ENU坐标doubleeast,north;latLngToENU(lat,lng,refLat,refLng,east,north);// 计算速度和航向unsignedlongcurrentTimeMsmillis();if(lastTimeMs0refSet){doubledt(currentTimeMs-lastTimeMs)/1000.0;if(dt0.01){doubledxeast-lastEast;doubledynorth-lastNorth;// 速度: m/s - km/hcalcSpeedsqrt(dx*dxdy*dy)/dt*3.6;// 航向: 弧度转角度0-360度calcHeadingatan2(dx,dy)*180.0/M_PI;if(calcHeading0)calcHeading360.0;}}// 更新状态lastEasteast;lastNorthnorth;lastTimeMscurrentTimeMs;// 输出数据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,doubleeast,doublenorth){constdoubleR6378137.0;// 地球半径(米)doublelatRadlat*M_PI/180.0;doublelngRadlng*M_PI/180.0;doublerefLatRadrefLat*M_PI/180.0;doublerefLngRadrefLng*M_PI/180.0;doublecosLatcos(refLatRad);eastR*(lngRad-refLngRad)*cosLat;northR*(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/hgps.course.deg()- 航向角度度3.2.2 本地墨卡托投影ENU坐标系原理将地球表面近似为平面以首次定位点为原点建立东-北-天坐标系。核心公式eastR*(lng-refLng)*cos(refLat)northR*(lat-refLat)其中R 地球半径6378137米refLat,refLng 参考点经纬度east,north 相对于参考点的东向、北向偏移米3.2.3 速度与航向计算// 速度 位移 / 时间calcSpeedsqrt(dx*dxdy*dy)/dt*3.6;// 航向 arctan2(东向位移, 北向位移)calcHeadingatan2(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 定位无效现象定位状态: 无效排查步骤检查天线连接是否牢固确保天线在室外开阔处等待至少30秒冷启动时间检查串口波特率是否正确ATGM332D默认96005.2 速度为0现象计算航速: 0.00 km/h排查步骤检查是否有连续的位置变化确认参考点已设置首次定位后检查时间差计算是否正确5.3 航向跳变现象航向角度突变原因GPS定位存在微小抖动低速运动时位移量小噪声影响大解决方案增加滤波算法如卡尔曼滤波对连续多个采样点进行平滑处理六、总结本文实现了从GPS数据采集到速度航向计算的完整流程硬件层面使用ESP32硬件串口与ATGM332D模块通信数据解析利用TinyGPSPlus库高效解析NMEA语句坐标转换通过本地墨卡托投影建立ENU坐标系速度计算基于位移差分计算真实运动参数该方案适用于无人机、机器人、车辆等需要实时定位和运动状态监测的应用场景。参考资料TinyGPSPlus官方文档ATGM332D模块手册NMEA-0183协议标准
【ESP32S3 + ATGM332D GPS模块实战:从NMEA解析到本地速度计算】
发布时间:2026/6/25 13:49:49
【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 速度为05.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 完整代码#includeArduino.h#includeTinyGPSPlus.h#includemath.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;// 参考点初始位置作为本地坐标系原点doublerefLat0.0;doublerefLng0.0;boolrefSetfalse;// 上一时刻的位置和时间doublelastEast0.0;doublelastNorth0.0;unsignedlonglastTimeMs0;// 计算得到的速度和航向floatcalcSpeed0.0;floatcalcHeading0.0;// 将经纬度转换为本地ENU坐标voidlatLngToENU(doublelat,doublelng,doublerefLat,doublerefLng,doubleeast,doublenorth);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());}// 定时输出2HzstaticunsignedlonglastPrintTime0;if(millis()-lastPrintTimePRINT_INTERVAL_MS){lastPrintTimemillis();Serial.println();if(gps.location.isValid()){doublelatgps.location.lat();doublelnggps.location.lng();// 设置参考点if(!refSet){refLatlat;refLnglng;refSettrue;Serial.println([INFO] 参考点已设置);}// 转换为ENU坐标doubleeast,north;latLngToENU(lat,lng,refLat,refLng,east,north);// 计算速度和航向unsignedlongcurrentTimeMsmillis();if(lastTimeMs0refSet){doubledt(currentTimeMs-lastTimeMs)/1000.0;if(dt0.01){doubledxeast-lastEast;doubledynorth-lastNorth;// 速度: m/s - km/hcalcSpeedsqrt(dx*dxdy*dy)/dt*3.6;// 航向: 弧度转角度0-360度calcHeadingatan2(dx,dy)*180.0/M_PI;if(calcHeading0)calcHeading360.0;}}// 更新状态lastEasteast;lastNorthnorth;lastTimeMscurrentTimeMs;// 输出数据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,doubleeast,doublenorth){constdoubleR6378137.0;// 地球半径(米)doublelatRadlat*M_PI/180.0;doublelngRadlng*M_PI/180.0;doublerefLatRadrefLat*M_PI/180.0;doublerefLngRadrefLng*M_PI/180.0;doublecosLatcos(refLatRad);eastR*(lngRad-refLngRad)*cosLat;northR*(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/hgps.course.deg()- 航向角度度3.2.2 本地墨卡托投影ENU坐标系原理将地球表面近似为平面以首次定位点为原点建立东-北-天坐标系。核心公式eastR*(lng-refLng)*cos(refLat)northR*(lat-refLat)其中R 地球半径6378137米refLat,refLng 参考点经纬度east,north 相对于参考点的东向、北向偏移米3.2.3 速度与航向计算// 速度 位移 / 时间calcSpeedsqrt(dx*dxdy*dy)/dt*3.6;// 航向 arctan2(东向位移, 北向位移)calcHeadingatan2(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 定位无效现象定位状态: 无效排查步骤检查天线连接是否牢固确保天线在室外开阔处等待至少30秒冷启动时间检查串口波特率是否正确ATGM332D默认96005.2 速度为0现象计算航速: 0.00 km/h排查步骤检查是否有连续的位置变化确认参考点已设置首次定位后检查时间差计算是否正确5.3 航向跳变现象航向角度突变原因GPS定位存在微小抖动低速运动时位移量小噪声影响大解决方案增加滤波算法如卡尔曼滤波对连续多个采样点进行平滑处理六、总结本文实现了从GPS数据采集到速度航向计算的完整流程硬件层面使用ESP32硬件串口与ATGM332D模块通信数据解析利用TinyGPSPlus库高效解析NMEA语句坐标转换通过本地墨卡托投影建立ENU坐标系速度计算基于位移差分计算真实运动参数该方案适用于无人机、机器人、车辆等需要实时定位和运动状态监测的应用场景。参考资料TinyGPSPlus官方文档ATGM332D模块手册NMEA-0183协议标准