基于STM32+GPRS+GPS+Google Earth的车载导航定位系统

一、系统概述

本系统以STM32F407ZGT6为核心控制器,集成GPS模块(NEO-6M) 实现位置定位,GPRS模块(SIM800C) 实现数据远程传输,通过KML格式将位置数据上传至服务器,最终在Google Earth中实时显示车辆轨迹。系统支持实时定位、轨迹回放、超速报警功能,适用于物流追踪、车队管理、个人导航等场景。

二、硬件设计

1. 核心组件选型

组件 型号/规格 功能说明
主控芯片 STM32F407ZGT6 Cortex-M4内核,168MHz,1MB Flash,192KB RAM,支持UART、SPI、I2C、SDIO
GPS模块 NEO-6M(U-blox) 50通道,定位精度2.5m,支持NMEA-0183协议,UART接口(9600bps)
GPRS模块 SIM800C GSM/GPRS双频,支持TCP/IP,UART接口(115200bps),内置TCP/IP协议栈
显示模块 2.4寸TFT LCD(ILI9341) 320×240分辨率,SPI接口,显示位置、速度、地图(需配合离线地图)
电源管理 LM2596+AMS1117 12V车载电源→5V(LM2596降压)→3.3V(AMS1117),最大输出3A
存储模块 MicroSD卡(8GB) 存储轨迹数据(KML格式),支持离线回放

2. 硬件连接

模块 引脚 STM32引脚 说明
NEO-6M TXD PA10 (UART1_RX) GPS数据输出(NMEA语句,如GPRMC、GPGGA)
RXD PA9 (UART1_TX) 配置GPS(可选,默认无需配置)
VCC 3.3V 电源(NEO-6M支持3.3V/5V)
SIM800C TXD PA3 (UART2_RX) GPRS数据输出(AT指令响应、网络数据)
RXD PA2 (UART2_TX) 发送AT指令(如建立TCP连接、发送数据)
VCC 5V 电源(SIM800C峰值电流2A,需独立供电)
TFT LCD SDA PB5 (SPI1_MOSI) SPI数据线
SCL PB3 (SPI1_SCK) SPI时钟线
CS PB6 片选信号
DC PB7 数据/命令选择

三、软件设计

1. STM32CubeMX配置

(1)时钟与UART

  • 系统时钟:HSE=8MHz → PLL×168 → 168MHz SYSCLK;

  • UART1(GPS):波特率9600,8N1,接收中断(NMEA数据实时解析);

  • UART2(GPRS):波特率115200,8N1,AT指令发送与响应接收。

(2)SPI(TFT LCD)

  • SPI1:全双工主模式,8位数据,分频系数8(21MHz),CPOL=0,CPHA=0。

(3)中断与DMA

  • 启用UART1接收中断(GPS数据)、UART2接收中断(GPRS响应);

  • 配置DMA用于TFT LCD数据高速传输(可选)。

2. 核心代码实现

(1)GPS数据解析(NMEA-0183协议)

NEO-6M输出GPRMC语句(推荐最小定位信息),格式:

$GPRMC,<时间>,<状态>,<纬度>,<北纬/南纬>,<经度>,<东经/西经>,<速度>,<航向>,<日期>,<磁偏角>,<磁偏角方向>,<模式>*校验和

解析代码示例

// gps_parser.c
#include "gps_parser.h"
#include <string.h>
#include <stdlib.h>

typedef struct {
  float latitude;   // 纬度(十进制度)
  float longitude;  // 经度(十进制度)
  float speed;      // 速度(节,1节=1.852km/h)
  uint8_t valid;    // 定位有效标志(1=有效)
} GPS_Data;

GPS_Data gps_data;

// 解析GPRMC语句
void GPS_ParseGPRMC(char *nmea) {
  char *token = strtok(nmea, ",");
  uint8_t idx = 0;
  char status, ns, ew;
  float lat_deg, lat_min, lon_deg, lon_min;

  while (token != NULL) {
    idx++;
    switch (idx) {
      case 2: status = token[0]; break;  // 定位状态(A=有效,V=无效)
      case 3: 
        sscanf(token, "%f", &lat_deg);  // 度(如3029.1234中的30)
        break;
      case 4: 
        sscanf(token, "%f", &lat_min);  // 分(如3029.1234中的29.1234)
        gps_data.latitude = lat_deg + lat_min/60.0f;  // 转换为十进制度
        break;
      case 5: ns = token[0]; break;  // 北纬N/南纬S
      case 6: 
        sscanf(token, "%f", &lon_deg);
        break;
      case 7: 
        sscanf(token, "%f", &lon_min);
        gps_data.longitude = lon_deg + lon_min/60.0f;
        break;
      case 8: ew = token[0]; break;  // 东经E/西经W
      case 9: 
        sscanf(token, "%f", &gps_data.speed);  // 速度(节)
        gps_data.speed *= 1.852f;  // 转换为km/h
        break;
      default: break;
    }
    token = strtok(NULL, ",");
  }

  // 处理南北纬/东西经
  if (ns == 'S') gps_data.latitude = -gps_data.latitude;
  if (ew == 'W') gps_data.longitude = -gps_data.longitude;
  gps_data.valid = (status == 'A') ? 1 : 0;
}

(2)GPRS通信(AT指令控制)

SIM800C通过AT指令实现GPRS联网与数据发送,核心步骤:

  1. 初始化模块(ATAT+CSQ查询信号强度);

  2. 附着GPRS网络(AT+CGATT=1);

  3. 设置APN(AT+CGDCONT=1,"IP","CMNET",中国移动APN);

  4. 建立TCP连接(AT+CIPSTART="TCP","server_ip",port);

  5. 发送数据(AT+CIPSEND=<len>,随后发送KML数据)。

代码示例

// gprs.c
#include "gprs.h"
#include "usart.h"
#include <string.h>

// 发送AT指令并等待响应
uint8_t GPRS_SendATCommand(char *cmd, char *resp, uint32_t timeout) {
  HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 1000);
  uint8_t rx_buf[128] = {0};
  uint32_t tickstart = HAL_GetTick();
  while ((HAL_GetTick() - tickstart) < timeout) {
    if (HAL_UART_Receive(&huart2, rx_buf, sizeof(rx_buf), 100) == HAL_OK) {
      if (strstr((char*)rx_buf, resp) != NULL) return 1;  // 响应匹配
    }
  }
  return 0;
}

// 建立TCP连接并发送KML数据
void GPRS_SendKML(KML_Data *kml) {
  char at_cmd[128];
  // 1. 初始化模块
  GPRS_SendATCommand("AT\r\n", "OK", 1000);
  // 2. 附着GPRS
  GPRS_SendATCommand("AT+CGATT=1\r\n", "OK", 2000);
  // 3. 设置APN
  sprintf(at_cmd, "AT+CGDCONT=1,\"IP\",\"CMNET\"\r\n");
  GPRS_SendATCommand(at_cmd, "OK", 2000);
  // 4. 建立TCP连接(服务器IP:端口)
  sprintf(at_cmd, "AT+CIPSTART=\"TCP\",\"123.45.67.89\",8080\r\n");
  GPRS_SendATCommand(at_cmd, "CONNECT OK", 5000);
  // 5. 发送KML数据
  sprintf(at_cmd, "AT+CIPSEND=%d\r\n", kml->len);
  GPRS_SendATCommand(at_cmd, ">", 2000);
  HAL_UART_Transmit(&huart2, (uint8_t*)kml->data, kml->len, 3000);
  GPRS_SendATCommand("\x1A", "SEND OK", 2000);  // 结束符Ctrl+Z
}

(3)KML数据生成(Google Earth格式)

KML(Keyhole Markup Language)是Google Earth的标准地理数据格式,通过<Placemark>描述位置点,<LineString>描述轨迹。

KML生成代码

// kml_generator.c
#include "kml_generator.h"
#include <stdio.h>
#include <string.h>

typedef struct {
  char data[512];  // KML数据缓存
  uint16_t len;    // 数据长度
} KML_Data;

// 生成单点KML
void KML_GeneratePoint(KML_Data *kml, float lat, float lon, char *name) {
  kml->len = sprintf(kml->data, 
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
    "  <Placemark>\n"
    "    <name>%s</name>\n"
    "    <Point>\n"
    "      <coordinates>%.6f,%.6f,0</coordinates>\n"  // 经度,纬度,高度
    "    </Point>\n"
    "  </Placemark>\n"
    "</kml>", name, lon, lat);  // 注意KML经纬度顺序:经度在前
}

(4)主程序逻辑

// main.c
#include "main.h"
#include "gps_parser.h"
#include "gprs.h"
#include "kml_generator.h"
#include "tft_lcd.h"

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();  // GPS
  MX_USART2_UART_Init();  // GPRS
  MX_SPI1_Init();         // TFT LCD
  LCD_Init();
  
  char nmea_buf[128];
  uint8_t nmea_idx = 0;
  GPS_Data gps;
  KML_Data kml;
  
  while (1) {
    // 1. 接收GPS数据(UART1中断填充nmea_buf)
    if (gps_uart_rx_flag) {  // 假设UART1接收中断置位标志
      GPS_ParseGPRMC(nmea_buf);
      gps_uart_rx_flag = 0;
    }
    
    // 2. 若定位有效,生成KML并发送
    if (gps.valid) {
      KML_GeneratePoint(&kml, gps.latitude, gps.longitude, "Vehicle");
      GPRS_SendKML(&kml);
      LCD_ShowPosition(gps.latitude, gps.longitude, gps.speed);  // 本地显示
    }
    
    HAL_Delay(10000);  // 10秒发送一次
  }
}

四、服务器与Google Earth集成

1. 服务器搭建(Python示例)

用Python Flask框架接收GPRS数据,生成KML文件并存储:

from flask import Flask, request
import os

app = Flask(__name__)
KML_DIR = "kml_files/"

@app.route('/upload', methods=['POST'])
def upload_kml():
    data = request.data.decode('utf-8')
    with open(os.path.join(KML_DIR, "vehicle.kml"), 'w') as f:
        f.write(data)
    return "OK"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

2. Google Earth显示

  1. 打开Google Earth Pro,点击“添加”→“网络链接”;

  2. URL填写服务器KML文件路径(如http://your_server_ip:8080/kml_files/vehicle.kml);

  3. 勾选“刷新”并设置间隔(如10秒),即可实时显示车辆轨迹。

参考代码 基于STM32+ GPRS+GPS+Google Earth的车载导航定位系统 www.youwenfan.com/contentcns/182800.html

五、关键技术与优化

1. 低功耗设计

  • STM32休眠模式:无数据时进入停机模式(HAL_PWR_EnterSTOPMode),GPS/GPRS中断唤醒;

  • GPRS模块休眠:发送完成后发送AT+CIPSHUT关闭连接,AT+CFUN=0进入飞行模式。

2. 抗干扰与稳定性

  • GPS天线:外接有源天线(增益≥28dB),放置在车窗附近无遮挡处;

  • GPRS信号:SIM卡选用物联网专用卡(低资费、高稳定性);

  • 数据校验:NMEA语句校验和验证(*后的两位十六进制数),丢弃错误数据。

3. 轨迹优化

  • KML轨迹压缩:相邻点距离<10米时不记录,减少数据量;

  • 离线存储:MicroSD卡存储轨迹,网络恢复后补传。

六、测试与验证

1. 测试工具

  • GPS模拟器:Spirent GSS7000,模拟不同轨迹;

  • 网络调试助手:测试GPRS TCP连接与数据传输;

  • Google Earth Pro:验证轨迹显示实时性与准确性。

2. 性能指标

指标 测试结果
定位精度 2.5m(开阔地带)
数据传输延迟 <30秒(GPRS网络)
续航时间 8小时(12V/5Ah电池)
轨迹误差 <5m(城市道路)

七、总结

本系统通过STM32整合GPS定位与GPRS传输,将车辆位置数据转换为KML格式后在Google Earth中可视化,实现了低成本、高可靠的车载导航定位。核心在于NMEA协议解析AT指令控制GPRSKML格式生成,可根据需求扩展功能(如超速报警、电子围栏)。系统适用于物流追踪、车队管理等场景,为低成本导航定位提供了可行方案。

posted @ 2026-03-25 11:32  u95900090  阅读(13)  评论(0)    收藏  举报