开源硬件
Arduino
客制化键盘
Arduino_寄存器
二进制运算
寄存器+二进制运算
LCD-逐字显示
密码依次录入
等待输入
WiFi Duck(无线击键注入攻击平台)
WiFi Duc-New
WiFi Duc-Old
蓝牙无线烧录
ESP8266
ESP-NOW
ESP8266看门狗
ESP8266-休眠模式
ESP01/01S使用说明
WIFI_SD
ESP8266-Web服务器
ESP8266-WIFI自动认证
ESP32
ESP32 ADC2
ESP32_PWM
ESP32_CAM
ESP32 小坦克
ESP32_限电保护
Arduino IDE 添加 ESP32
ESP32-iPhone BLE攻击
STM32
STM32F103-虚拟键盘
STC
STC8G1K08(A)
树莓派-触摸屏
Arduino IDE
Arduino_自制库
Arduino库收集
常见排序算法
冒泡排序
选择排序
插入排序
希尔排序
归并排序
快速排序
计数排序
预处理
millis(运行时长)
Arduino IDE 2.X-修改数据位置
Mixly
Mixly安装教程
Mixly 模块介绍
Mixly-添加ESP32CAM支持
Mixly-库定制工具
模块
4G模块连接物联网
GPS模块
语音模块(JQ8900)
安信可VB语音识别
28BYJ-48(5V步进)
FreeRTOS
FreeRTOS-多任务基础
FreeRTOS-任务共享全局变量
FreeRTOS-多核多任务
FreeRTOS-MUTEX
FreeRTOS-常规程序改多任务
FreeRTOS-定时器
LaserGRBL(激光雕刻)
LaserGRBL-GRBL
GRBL-CNC Shield v4
MicroPython
Scratch
Wokwi(在线仿真)
html转无符号数组
待做开源项目
本文档使用 MrDoc 发布
-
+
首页
GPS模块
## GPS基础了解 ### 全球四大卫星定位系统 * GPS系统(美国) * BDS系统(中国北斗) * GLONASS系统(俄罗斯) * 伽利略卫星导航系统(欧盟) ### GPS启动模式 GPS模块属于室外定位系统,所以`天线需要放室外才能定位` 第一次使用模块启动是`冷启动`,需要下载卫星数据,正常需要1-10分钟不等才能定位到。请耐心等待! * 冷启动 冷启动是指在一个陌生的环境下启动 GPS 直到 GPS 和周围卫星联系并且计算出 坐标的启动过程。以下几种情况开机均属冷启动: 1、初次使用时; 2、电池耗尽导致星历信息丢失时; 3、关机状态下将接收机移动 1000 公里以上距离。也就是说冷启动是通过硬件方 式的强制性启动,因为距离上次操作 GPS 已经把内部的定位信息清除掉,GPS 接 收机失去卫星参数,或者已经存在的参数和实际接收到卫星参数相差太多,导致 导航仪无法工作,必须从新获得卫星提供的坐标数据,所以说车辆从地库里启动 导航百分百算冷启动,这也是从地库出来搜星时间长的原因。 * 热启动 热启动是指在上次关机的地方没有过多移动启动 GPS,但距离上次定位时间必须 小于 2 个小时,通过软件的方式,进行一些启动前的保存和关闭等准备工作后的 启动。 * 温启动 温启动是指距离上次定位时间超过 2 个小时的启动,搜星定位时间介于冷启动和 热启动之间。如果您前一日使用过 GPS 定位,那么次日的第一次启动就属于温启 动,启动后会显示上次的位置信息。因为上次关机前的经纬度和高度已知,但由 于关机时间过长,星历发生了变化,以前的卫星接受不到了,参数中的若干颗卫 星已经和 GPS 接收机失去了联系,需要继续搜星补充位置信息,所以搜星的时间 要长于热启动,短于冷启动。 当GPS模块连续的寻找到三颗或以上的卫星的时候,它的日子就开始好过了,就比如我们人在野外临时凭感觉绘制了一张草草的地图,你至少可以判定自己的大概位置,这个时候,GPS查找新卫星所设定的范围就会缩,2D定位,也就是平面定位OK了,接下来就是花的时间去找更多的卫星,到了四颗卫星的时候,OK,3D定位,也就是高度都出来了. ### 卫星状态模式   卫星显示状态颜色有三种: * 绿色,数据包下载并已定位 * 蓝色,数据包下载中 * 红色,通过其他卫星感应到的信号 > u-center(NEO系列模块配置工具,其他GPS也可使用但无法配置) ## GPS模块接线  遵守RX TX交叉连接即可 一般默认波特率都为`9600` > 测试最好是带电脑到户外空旷地进行,若是把天线放在阳台外面的话,有一定几 率定位失败,这个受楼距,遮挡物等因素影响。 > 板载LED保持一定频率闪烁证明定位成功了 ## GPS使用 ### 串口使用  设置好波特率就行 串口工具都可使用,也可使用u-center这类可以直接读取位置的 ### 开发板使用 GPS模块,这里使用的是 [`TinyGPS++`](https://github.com/mikalhart/TinyGPSPlus/releases) 库来调用 Arduino、ESP8266、ESP32都可使用 ``` #include <TinyGPS++.h> //加载TinyGPS++库 #include <SoftwareSerial.h> //加载SoftwareSerial库,可以设置软串口 TinyGPSPlus gps; //加载TinyGPSPlus为gps,简化调用名 SoftwareSerial gps_ss(4, 5); //连接GPS模块,RX-4 TX-5,根据需求更改 void setup(){ gps_ss.begin(9600); //和GPS通讯的波特率,要和GPS模块一致 Serial.begin(9600); //主控和串口监视器的波特率,可以不和GPS模块一致 } void loop(){ while (gps_ss.available()) { //GPS获取数据了? if (gps.encode(gps_ss.read())) { //GPS数据解析有效? if (gps.location.isValid()) { //GPS位置有效? Serial.println(gps.location.lat(),5); //输出纬度,且保留5位小数 Serial.println(gps.location.lng(),5); //输出经度,且保留5位小数 } } } } ``` #### TinyGPS++添加北斗支持 TinyGPS++ 默认是只检测GP和GN 通过修改 `TinyGPS++.cpp` 文件来实现北斗BD或是GB的支持 ``` //添加函数名 #define _BDRMCterm "BDRMC" #define _BDGGAterm "BDGGA" //添加接受到信息检索时额外检索的函数 //注意好||之间的括号,要((a||b)||c)类似形式,a||b||c 语法是错误的 if ((!strcmp(term, _GPRMCterm) || !strcmp(term, _GNRMCterm))|| !strcmp(term, _BDRMCterm)) curSentenceType = GPS_SENTENCE_GPRMC; else if ((!strcmp(term, _GPGGAterm) || !strcmp(term, _GNGGAterm)) || !strcmp(term, _BDGGAterm)) curSentenceType = GPS_SENTENCE_GPGGA; else curSentenceType = GPS_SENTENCE_OTHER; ```   添加完后,正常使用 `TinyGPS++` 库就可以看到能够正常读取 `BD` 定位了 #### TinyGPS++可用函数 [微型GPS++](http://arduiniana.org/libraries/tinygpsplus/) 主要的 TinyGPS++ 对象包含几个核心子对象: * location – 最新的定位 * date – 最新日期修正 (UT) * time – 最新的时间修复(UT) * speed – 当前地面速度 * course – 当前地面航线 * altitude – 最新的海拔修复 * satellites – 可见的参与卫星的数量 * hdop – 水平精度降低 ``` Serial.println(gps.location.lat(), 6); // Latitude in degrees (double) Serial.println(gps.location.lng(), 6); // Longitude in degrees (double) Serial.print(gps.location.rawLat().negative ? "-" : "+"); Serial.println(gps.location.rawLat().deg); // Raw latitude in whole degrees Serial.println(gps.location.rawLat().billionths);// ... and billionths (u16/u32) Serial.print(gps.location.rawLng().negative ? "-" : "+"); Serial.println(gps.location.rawLng().deg); // Raw longitude in whole degrees Serial.println(gps.location.rawLng().billionths);// ... and billionths (u16/u32) Serial.println(gps.date.value()); // Raw date in DDMMYY format (u32) Serial.println(gps.date.year()); // Year (2000+) (u16) Serial.println(gps.date.month()); // Month (1-12) (u8) Serial.println(gps.date.day()); // Day (1-31) (u8) Serial.println(gps.time.value()); // Raw time in HHMMSSCC format (u32) Serial.println(gps.time.hour()); // Hour (0-23) (u8) Serial.println(gps.time.minute()); // Minute (0-59) (u8) Serial.println(gps.time.second()); // Second (0-59) (u8) Serial.println(gps.time.centisecond()); // 100ths of a second (0-99) (u8) Serial.println(gps.speed.value()); // Raw speed in 100ths of a knot (i32) Serial.println(gps.speed.knots()); // Speed in knots (double) Serial.println(gps.speed.mph()); // Speed in miles per hour (double) Serial.println(gps.speed.mps()); // Speed in meters per second (double) Serial.println(gps.speed.kmph()); // Speed in kilometers per hour (double) Serial.println(gps.course.value()); // Raw course in 100ths of a degree (i32) Serial.println(gps.course.deg()); // Course in degrees (double) Serial.println(gps.altitude.value()); // Raw altitude in centimeters (i32) Serial.println(gps.altitude.meters()); // Altitude in meters (double) Serial.println(gps.altitude.miles()); // Altitude in miles (double) Serial.println(gps.altitude.kilometers()); // Altitude in kilometers (double) Serial.println(gps.altitude.feet()); // Altitude in feet (double) Serial.println(gps.satellites.value()); // Number of satellites in use (u32) Serial.println(gps.hdop.value()); // Horizontal Dim. of Precision (100ths-i32) ``` 调试方法: * isValid() – 对象是否包含任何有效数据 * isUpdated() – 对象的值自上次查询以来是否已更新(不一定已更改) * age() – 该方法返回自上次更新以来的毫秒数。如果返回大于 1500 左右的值,则可能表明存在问题,例如修复丢失 * charsProcessed() – 对象接收的字符总数 * sentenceWithFix() – 已修复的 $GPRMC 或 $GPGGA 句子的数量 * failedChecksum() – 校验和测试失败的所有类型的句子数 * passedChecksum() – 通过校验和测试的所有类型的句子数 ##### 自定义NMEA提取 ``` TinyGPSCustom magneticVariation(gps, "GPRMC", 10) // 留意 $GPRMC 句子,并在每次流过时提取第 10 个逗号分隔的字段 //这时候`magneticVariation`和内置对象一样,可以直接调用 loop(){ if (magneticVariation.isUpdated()){ //查询对象的值自上次查询以来是否已更新 Serial.println(magneticVariation.value()); //输出`magneticVariation`查询的字段内容 } while (gps_ss.available() > 0)gps.encode(gps_ss.read()); //要使 TinyGPS++ 工作,必须使用encode()方法反复将字符从 GPS 模块传送到它 } ``` ## GPS模块使用北斗 实测发现GPS定位是不准的,大概偏移了一公里之多,北斗定位很精确 ### NEO系列 1. 打开u-center软件,点击view->text console,打开输出显示终端,这样可看到相关的输出信息,如GSV,RMC等信息; 2. 点击view->messages view,打开命令控制终端,这里可进行配置; 3. 在messages view中找到 UBX->CFG->GNSS(GNSS Config),点击后,会出现卫星模式设置对话框,我们可以设置成GPS+BeiDou,如下图所示:  4. 设置成了GPS+BeiDou,然后点击左下角的send按钮 注意,当设置了GNSS类型模式后,ublox模块会自动初始化一下,初始化完成后就是GPS+BeiDou模式了 5. 测试时发现只能显示GPS的SNR值,即在text console里只能看到GPGSV的信息,看不到GBGSV的信息,这时其实已经是有BeiDou参与定位了,只是串口没有输出相应的GBGSV值罢了,为了GBGSV值的输出,可以这样设置  6. 在NMEA中选择NMEA的版本号为V4.1,然后再点击send按钮发送该组命令就可以了,这样就看以看到GBGSV信息了,同时还可以显示出北斗的SNR值了,如下图:   其中B开头的表示北斗卫星,G表示GPS卫星 7. 设置保存在FLASH  ### ATGM336H-5N模块 使用`GNSSToolKit_Lite` 设置波特率,连接串口,设置为BD(北斗)模式  `注意:实测发现北斗和GPS一起用北斗会出现搜索到卫星却无法使用的问题,设置成单北斗模式正常使用` ## NMEA指令解析 ### NMEA-0183 协议简介 NMEA 0183是美国国家海洋电子协会(National Marine Electronics Association)为海用电子设备制定的标准格式。目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。 NMEA-0183协议采用ASCII码来传递GPS定位信息,我们称之为帧。 帧格式形如:**$aaccc,ddd,ddd,…,ddd*hh(CR)(LF)** 1. “$”:帧命令起始位 2. aaccc:地址域,前两位为识别符(aa),后三位为语句名(ccc) 3. ddd…ddd:数据 4. “*”:校验和前缀(也可以作为语句数据结束的标志) 5. hh:校验和(check sum),$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符) 6. (CR)(LF):帧结束,回车和换行符  UTC 时间即协调世界时,相当于本初子午线(0 度经线)上的时间,北京时间比 UTC 早 8 个小时。 ### 指令讲解 [GPS - NMEA 指令介绍](http://aprs.gids.nl/nmea/) > NEO系列的模块北斗是`GB`开头 > ATGM336H-5N模块的北斗是`BD`开头 ### 经纬度计算  * 度分格式 纬度:ddmm.mmmm 北纬 2236.9453 22+(36.9453/60)= 22.615755 经度:dddmm.mmmm 东经 11408.4790 114+(08.4790/60)=114.141317 * 度分秒格式 北纬 2236.9453 =22 度 36 分 0.9453x60 秒 = 22 度 36 分 56.718 秒 东经 11408.4790=114 度 8 分 0.4790x60 秒=114 度 8 分 28.74 秒 ### 直接读取 不使用相关库,直接串口读取,程序截取 ``` #include <SoftwareSerial.h> String lng; String lat; volatile double lng_the; volatile double lat_the; String reception_the; String rec[10]={}; SoftwareSerial mySerial1(4,5); void reception(String reception_in, int reception_num) { int i = 1; int location = 0; do{ location = String(reception_in).indexOf(String(",")); if (location != -1) { rec[(int)(i - 1)] = String(reception_in).substring(0,location); reception_in = String(reception_in).substring((location + 1),String(reception_in).length()); } else { if (String(reception_in).length() > 0) { rec[(int)(i - 1)] = reception_in; } } i++; if (i >= reception_num) { location = -1; } }while((location >= 0)); for (int z = 1; z <= 6; z = z + (1)) { Serial.println(rec[(int)(z - 1)]); delay(200); } } void setup(){ lng = ""; lat = ""; lng_the = 0; lat_the = 0; reception_the = ""; mySerial1.begin(9600); Serial.begin(9600); } void loop(){ if (mySerial1.available() > 0) { reception_the = mySerial1.readStringUntil('\n'); //获取串口数据, \n 截止,一般数据就是一行,结尾会有 \r\n 进行换行 reception_the = String(reception_the).substring(String(reception_the).indexOf(String("$GPRMC")),String(reception_the).length()); //实测相近的两组数据可能会被合并成一段,并不一定为"$GPRM"为首,查找内容,找到这个字符串进行定位,截取由"$GPRM"为首的字符串 if (String(reception_the).startsWith("$GPRMC")) { //如果是以"$GPRM"为首,才会进行转换 reception(reception_the, 7); //因为只要经纬度,截取前7个","分隔的内容 lng_the = String(rec[(int)(5)]).toFloat(); //转换为小数储存,下面计算使用 lat_the = String(rec[(int)(3)]).toFloat(); lng = String(String((lng_the / 100), 0)) + String(".") + String(String((((lng_the - floor(lng_the / 100)) / 60) * 10000)).substring(0,6)); //经度.换算成分秒 lat = String(String((lat_the / 100), 0)) + String(".") + String(String((((lat_the - floor(lng_the / 100)) / 60) * 10000)).substring(0,6)); //纬度.换算成分秒 } } } ```
造物者W
2022年1月11日 19:04
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码