文号
941805
阅读
8135
回复
16
收藏
3
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
解码高速GPS
2025/02/02
IP:北京

飞控使用的GPS刷新率高达18Hz以上,使用UBX协议,除了能高速读出位置坐标,还能直接读出天向、东向、北向速度等信息,利于飞控使用。无人机上通常使用飞控板解码,不够灵活和微型化。本文报道Arduino单片机解码高速GPS,可组成更为简单灵活、微型化的飞控系统。

1,器材:

GPS模块,深圳市飞空科技RC12-B0带地磁,35元/片。

单片机采用Arduino Pro Micro或更小体积的Nano mini。

2,连线:

将GPS TX线连单片机D8脚,RX连D9,Vcc和GND分别连到单片机上。

3,代码:

使用软串口(8,9)读取GPS数据,UBX数据格式固定以B5 62开头,该GPS模块数据默认为NAV-PVT格式(码01 07)。数据头B5 62后面会接92个GPS数据,判断找到数据头后,逐个读出这些数据即可,详细数据格式可参考文章《UBX 协议报文整理》CIStoner。

注意每个读数操作后要加一个delay(1)延迟一下,不然数据不对,原因不明,实测有效。不需要的数据用for循环读,节约语句和内存。该示例中所有数据打印到串口显示,实际应用于飞控就不需要串口打印。

代码故意未做过多优化,利于初学者理解和调试,可以先写短一点只读最前面几个数,一步步调试,用串口打印如Serial.println(length),看看读出的数据对不对。

#include <SoftwareSerial.h>

SoftwareSerial gpsSerial(8, 9); // RX, TX


void setup() {

  Serial.begin(115200);

  gpsSerial.begin(115200);

}

void loop() {

  if (gpsSerial.available()) {

    if (gpsSerial.read() == 0xB5 ) {

      delay(1);

      if (gpsSerial.read() == 0x62 ) { // 同步字符判断

        delay(1);

      uint8_t msgClass = gpsSerial.read();

        delay(1);

      uint8_t msgId = gpsSerial.read();

        delay(1);

      uint8_t length = (gpsSerial.read());

        delay(1);

        gpsSerial.read();

        delay(1);

        Serial.println(length);    

        Serial.println(msgId);    

      if (msgClass == 0x01 && msgId == 0x07) { // NAV-PVT消息

        for(int i=0;i<4;i++){

          uint8_t Dt00 = gpsSerial.read();

          delay(1);  

        }

        uint8_t Dt1 = gpsSerial.read();

        delay(1);

        uint8_t Dt2 = gpsSerial.read();

        delay(1);      

        uint8_t Dt3 = gpsSerial.read();

        delay(1);

        uint8_t Dt4 = gpsSerial.read();

        delay(1);      

        uint8_t Dt5 = gpsSerial.read();

        delay(1);

        uint8_t Dt6 = gpsSerial.read();

        delay(1);      

        uint8_t Dt7 = gpsSerial.read();

        delay(1);

             

        // 日期和时间信息...

        Serial.print(Dt1+Dt2*256);

        Serial.print("-");

        Serial.print(Dt3);

        Serial.print("-");

        Serial.print(Dt4);

        Serial.print("  ");

        Serial.print(Dt5);  

        Serial.print("-");

        Serial.print(Dt6);

        Serial.print("-");

        Serial.println(Dt7);

        for(int i=0;i<12;i++){

          uint8_t Dt00 = gpsSerial.read();

          delay(1);  

        }  

//经纬度信息…

        uint8_t Dt8 = gpsSerial.read();

        delay(1);

        uint8_t Dt9 = gpsSerial.read();

        delay(1);      

        uint8_t Dt10 = gpsSerial.read();

        delay(1);

        uint8_t Dt11 = gpsSerial.read();

        delay(1);

        uint8_t Dt12 = gpsSerial.read();

        delay(1);      

        uint8_t Dt13 = gpsSerial.read();

        delay(1);      

        uint8_t Dt14

        delay(1);

        uint8_t Dt15 = XXXXXXXXXXXXXXXXXXXXX();

        delay(1);

        uint8_t Dt16 = XXXXXXXXXXXXXXXXXXXXX();

        delay(1);            

        Serial.print("卫星数");    

        Serial.println(Dt8);

        Serial.print("经度");    

Serial.println((1.0*Dt9+1.0*Dt10*256+1.0*Dt11*256*256+1.0*Dt12*256*256*256)/10000000.0);

        Serial.print("纬度");    

Serial.println((1.0*Dt13+1.0*Dt14*256+1.0*Dt15*256*256+1.0*Dt16*256*256*256)/10000000.0);

      }

    }

  }

  }

}



1年5个月前IP:湖北
944114
1楼

大佬问一下,这个可以防止gps限高限速吗?

引用
评论
2
Tangshm作者
1年5个月前IP:北京
944115
2楼
引用ZZCjas发表于1楼的内容
大佬问一下,这个可以防止gps限高限速吗?

“防止”?是想说“规避”吗?法规要自觉遵守哈~😳

引用
评论
1年5个月前IP:湖北
944117
3楼
引用Tangshm发表于2楼的内容
“防止”?是想说“规避”吗?法规要自觉遵守哈~😳

不是的,就指的是民用gps超一定高度或一定速度

直接锁星丢星的现象,之前看虎哥说过自己写个解算程序就可以解决问题,所以问一下😅

引用
评论
1
Tangshm作者
1年5个月前IP:北京
944118
4楼
引用ZZCjas发表于3楼的内容
不是的,就指的是民用gps超一定高度或一定速度直接锁星丢星的现象,之前看虎哥说过自己写个解算程序就可以解决问题,所以问一下😅

有可能哦!这个也不大好测试,尤其是高速,好一点火箭经常就超音速。高度限制估计几公里内不会有问题,不然都不实用了。还打算用个氢气球系留升空测试一下,升到高空后来个自由落体,也许可以测个高速

引用
评论
1年5个月前修改于 1年5个月前IP:湖北
944119
5楼
引用Tangshm发表于4楼的内容
有可能哦!这个也不大好测试,尤其是高速,好一点火箭经常就超音速。高度限制估计几公里内不会有问题,不然都不实用了。还打算用个氢气球系留升空测试一下,升到高空后来个自由落体,也许可以测个高速

御坂18650的ssr3A就是升到3000m直接丢星

了,具体可以看帖子和下面虎哥的评论

https://www.kechuang.org/t/85313#container

IMG_3016.png

引用
评论
1
Tangshm作者
1年5个月前IP:北京
944123
6楼

看了,看看怎么测试一下!

引用
评论
Tangshm作者
1年5个月前IP:北京
944129
7楼
引用ZZCjas发表于5楼的内容
御坂18650的ssr3A就是升到3000m直接丢星了,具体可以看帖子和下面虎哥的评论https://www.kechuang.org/t/85313#container[图片]

这个GPS的操作极限指标

Screenshot_20250517_212621_com.taobao.taobao_edit_578610430022126.jpg

加速度这个是比较局限的

引用
评论(1)
11个月20天前IP:广东
947663
8楼
引用ZZCjas发表于3楼的内容
不是的,就指的是民用gps超一定高度或一定速度直接锁星丢星的现象,之前看虎哥说过自己写个解算程序就可以解决问题,所以问一下😅

据网友传言,ATGM336H这个一旦锁定了,后面超过限制也不会失锁

引用
评论
1
11个月20天前IP:四川
947664
9楼
引用warmonkey发表于8楼的内容
据网友传言,ATGM336H这个一旦锁定了,后面超过限制也不会失锁

据网友说现在有些GPS为了追求灵敏度,将环路调得过于极限,以至于更早就失锁了。国产GPS反而基本上没有遵循美国体系的限制条款,只要刷个迟钝的固件就能扛造很多,或者是一旦锁定就改变环路参数让动态范围变大。

引用
评论
2
Tangshm作者
11个月20天前IP:北京
947666
10楼

我只试过短时间加速(用橡皮筋弹射,0.2s加速),没失锁,现在又不怎么弄火箭了,加上是帝都有箭也不好发,需要找人合作打一两发实测一下才好

引用
评论
11个月20天前IP:中国
947667
11楼

我大学毕业之后的第一份工作,开发WinCE系统,那时车主都会买一个mp4大小的车载GPS仪,当时GPS芯片上海一家公司国产的的,他的芯片通过SPI接到我们的芯片上,闭源的驱动,搜星直接消耗掉单核ARM9 40%以上的CPU,系统变得很卡顿,现在想起来怀疑那玩意很大可能是个SDR,把基带信号拿给ARM去处理

引用
评论
2
11个月19天前IP:广东
947675
12楼

尽量不要用delay。。。这不是RTOS

想要好的实时性,所有时基都应该由中断实现

这种东西要用状态机写的,你也没有判断是否存在数据,也没有做校验,都是不可靠的,有一个获取缓冲区有多少byte的函数

然后取一个数据,调用状态机去处理,类似下面我写的寄代码


          while (Serial.available() > 0)  //循环串口是否有数据
          {

void Data_chuli(uint8_t readbit){
  static uint16_t data_len=0,data_readlen=0,data_crc=0;
    switch(uart_ztj){
      case 0:
        if(readbit==0xBE){
          read_data[0]=readbit;
          uart_ztj=1;
        }
        break;
      case 1:
        if(readbit==0xAA){
          read_data[1]=readbit;
          uart_ztj=2;
        }else{
          uart_ztj=0;
        }
        break;
      case 2:
        data_len=readbit;
        read_data[2]=readbit;
        uart_ztj=3;
        break;
      case 3:
        data_len+=readbit<<8;
        read_data[3]=readbit;
        if(data_len<=DATA_BUF+1){
          uart_ztj=4;
        }else{
          uart_ztj=0;
        }
        break;
      case 4:
        data_status=readbit;
        read_data[4]=readbit;
        if(data_len==1){
          uart_ztj=6;
        }else{
          uart_ztj=5;
        }
        data_readlen=1;
        break;
      case 5:
        read_data[data_readlen+4]=readbit;
        data_readlen++;
        if(data_readlen>=data_len){
          uart_ztj=6;
        }
        break;
      case 6:
        data_crc=readbit;
        uart_ztj=7;
        break;
      case 7:
        data_crc+=readbit<<8;
        uart_ztj=8;
        break;
      case 8:
        if(readbit==0xEB){
          uart_ztj=9;
        }else{
          uart_ztj=0;
        }
        break;
      case 9:
        if(readbit==0xAA){
          uart_ztj=0;
          if(CRC16_XMODEN(read_data,data_len+4)==data_crc){//成功接收
            read_en=0;
          }
        }else{
          uart_ztj=0;
        }
        break;
    }
}



引用
评论
11个月19天前修改于 11个月19天前IP:广东
947678
13楼
引用虎哥发表于9楼的内容
据网友说现在有些GPS为了追求灵敏度,将环路调得过于极限,以至于更早就失锁了。国产GPS反而基本上没有遵循美国体系的限制条款,只要刷个迟钝的固件就能扛造很多,或者是一旦锁定就改变环路参数让动态范围变大...

image.png

是的没错。这东西里面是个kalman filter,本质上是靠推测运动状态来平滑测量噪声。模块上面没有陀螺仪和加速度计,所以模型就变得很重要了


从这个案例可以看出来英美是怎么倒退的。老牌发达国家的行政机器已经太大太老了,像一个巨型僵尸,扼杀一切改进的可能性。这一类管制策略是典型的掩耳盗铃:禁止不了别的国家搞研发,禁止自己的总可以吧。

在美国和加拿大的大学,小卫星项目组对这个非常头疼。GNSS接收机被列为管制商品,接触的人员都要做背景审查,导致整个流程极为艰难。信号都发出来了,难道要禁止别人用电脑接收么?真是笑掉大牙。

GPS的C/A码,带宽只有1M,用一个树莓派就能跑SDR解调。spacex的falcon 9从爱好者接收解码的遥测数据看,大概率用的gnss-sdr,或者是自制的软件GNSS接收机。

这一套管制逻辑在冷战期间可能行得通,毕竟那时候买得起电脑的都没几个。但现在是互联网时代了,知识以前所未有的速度传播,算力驱动的自动机械(CNC、光刻机等)和设计仿真软件极快速度普及。孟加拉都出现了能设计170G毫米波功放的作坊。

中国无人机与美国无人机行业的此消彼长,也是类似的原因。当3dr与dji干架最激烈的时候,3dr的硅谷办公室被查封了。稀土也是类似情况,只要是钍矿伴生的一律以防止核扩散为理由禁止开采。现在环保署把高氯酸盐都管制了,北美最后一家AP工厂已经关门。所有火工品原料从中日韩进口(大概率中国),同时北美区域模型火箭发动机全部断货。

引用
评论
Tangshm作者
11个月18天前IP:北京
947695
14楼
引用xljxlj发表于12楼的内容
尽量不要用delay。。。这不是RTOS想要好的实时性,所有时基都应该由中断实现这种东西要用状态机写的,你也没有判断是否存在数据,也没有做校验,都是不可靠的,有一个获取缓冲区有多少byte的函数然后取...

好啊学习一下。最近不搞这个,忙科研工作去了,有热心创友的话接力啊。主要问题是确认一下这个牌子GPS在大加速下是否会锁死。目前这个代码数据可靠性实测还可以,代码风格属易懂但偏啰嗦型。现在是模块与单片机的串口速率有点细微差别,直接加延迟做匹配

引用
评论
8个月8天前IP:中国
949036
15楼

我之前用esp32搓出来一个建议的定位装置,还能测速度,不过用的是别人提前写好的gps库,直接帮你处理了位置数据接收到后的格式。另外尽量少用阻塞式函数(单片机的硬件资源宝贵)IMG20251102190646.jpg

引用
评论
Tangshm作者
6个月5天前IP:北京
949828
16楼

GPS串口通讯代码有更新,使用标准GPIO串口通讯函数uart及中断。单片机主频降至66.5MHz,在集成度高的情况下减少对GPS干扰。下面是读取日期时间及卫星数的例程:


#include <hardware/clocks.h>


#include "hardware/uart.h"


#include "hardware/irq.h"


uint16_t Yr;


uint8_t SA,Mt,Dy,Hr,Mn,Sd;


unsigned char GPS_buf[75];




void setup() {


set_sys_clock_khz(66500,true);


//使用Uart0串口及中断读GPS


uart_init(uart0, 115200); // 初始化 UART


gpio_set_function(12, GPIO_FUNC_UART); // 设置 GPIO 引脚为 UART 功能(例如 GP12 作为 TX,GP13作为 RX)


gpio_set_function(13, GPIO_FUNC_UART);


gpio_pull_up(13); // 确保 RX 引脚上拉,避免悬空输入


uart_set_irqs_enabled(uart0, true, false); // 启用 UART 中断,读数中断,写数不中断


irq_set_exclusive_handler(UART0_IRQ, GPS_Read); // 设置GPS读数中断处理函数


irq_set_enabled(UART0_IRQ, true); // 启用串口中断


delay(1000);


}


// GPS读数中断函数


void GPS_Read() {


while (uart_is_readable(uart0)) {


uint8_t ch = uart_getc(uart0); // 读取一个字


if (ch==0xB5){


ch = uart_getc(uart0); // 读取一个字


if (ch==0x62){


uint8_t msgClass = uart_getc(uart0);


uint8_t msgId = uart_getc(uart0);


uint8_t length = (uart_getc(uart0));


if (msgClass 0x01 && msgId 0x07) { // NAV-POSLLH消息


for(uint8_t i=0;i<73;i++){


GPS_buf[i] = uart_getc(uart0);


}


Yr = (GPS_buf[6]<<8)|GPS_buf[5];


Mt = GPS_buf[7];


Dy = GPS_buf[8];


Hr = GPS_buf[9]+8;


Mn = GPS_buf[10];


Sd = GPS_buf[11];


SA = GPS_buf[24];


}


}


}


}


}

引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的