加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
箭载飞控解析卫星定位数据(含代码!!!)
ouybg2023/10/08原创 低成本开源火箭 IP:广东

原因:此前也想过使用陀螺仪计算用以获得相对位移以指引回收操作,然而测试的时候发现不仅算法比较困难,而且精度和资源占用方面也不如我意。

因而转而使用卫星定位系统以进行定位。

这里使用的是微型民用定位模块(以下简称模块),由于较为靠近室内,冷启动和热启动耗时都较长,如果在室外,性能会好很多。

1696739356864.jpg

也尝试打印了一下数据,结果是这数据量明显不适合直接打印……

image.png

——————————————————————————

正文:

材料准备:

网购一个模块(要带陶瓷天线),USB-TTL

(虽然这里用的是外国产品,但是实际测试的效果上来看,我没觉得中科微的和外国的有什么区别)

STCisp或者其他的总之能读串口就行的软件,也可以用正规的解析软件。

image.png

调试:

TXD接RXD,反之亦然,如果没有安装串口驱动需要安装。(如不知道串口参见百度)

打开串口,一般不用改: image.png

通电,状态灯点亮,把天线放在尽可能靠近室外的地方,这将会大大提高成功率,此时不需要打开串口,因为此时模块处于冷启动状态,需要较长的时间进行定位,此时收到的数据都是无意义的。

一般几分钟过后,模块可以确认位置后,此时状态灯闪烁,打开串口,用文本模式接收,可以得到一长串数据,例子:

image.png

如果使用专业软件这时候一般都可以有图了。(注意软件不要多开)

image.png

时间需要+8小时

image.png

会飘完全正常,搜索100x100可能有点困难,但是搜个10x10还是比较容易的。

既然确定了模块OK,那么开始分析数据:

都已$起头,其中要用到的如下:

GPVTG:地面速度

GPGSA:卫星信息

GPGGA:定位信息

GPGSV:可见卫星信息

GPRMC:推荐的最小定位信息(!)

解包就可以了。

然而,有一个点需要注意

虽然这里都是以GP开头,但其实我们收到的很多,这分别对应:

北斗,GPS,GLONASS(北斗以BD开头)

我个人是倾向于用GNRMC(双星或多星联合定位)

GPRMC 最小定位信息:

数据详解:$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
  <1> UTC 时间,hhmmss(时分秒)格式
  <2> 定位状态,A=有效定位,V=无效定位
  <3>纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
  <4> 纬度半球N(北半球)或S(南半球)
  <5>经度dddmm.mmmm(度分)格式(前面的0也将被传输)
  <6> 经度半球E(东经)或W(西经)
  <7>地面速率(000.0~999.9节,前面的0也将被传输)
  <8>地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输)
  <9> UTC 日期,ddmmyy(日月年)格式
  <10>磁偏角(000.0~180.0度,前面的0也将被传输)
  <11> 磁偏角方向,E(东)或W(西)
  <12>模式指示(仅NMEA01833.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

原文参考以下的连接-https://www.cnblogs.com/csMapx/archive/2011/11/02/2232663.html-,故不赘述。

注意速度单位是海里/h,也就是节。

——————————————————

2023/10/08关于加速度的问题,在网友指正后确认如下:

1:发射前冷启动

2:在点火倒计时结束前关闭模块

3:预定的开伞或者着陆

4:热启动模块并通过无线传送数据

相关参数可以参考这个:

attachment icon ATGM336H_5N BDS_GNSS定位导航模块用户手册.pdf 2.30MB PDF 80次下载 预览

——————————————————

main.c

C++
#include "stm32f10x.h"                  // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h" #include "string.h" int main(void) { OLED_Init(); Serial_Init(); OLED_ShowString(41"BDS/GPS"); char* REVFORM; char* GNRMCD; char* DRESS[14]; unsigned char count; OLED_ShowString(31"UNLOCATED"); while (1) {  GNRMCD = strstr(Serial_RxPacket,"GNRMC");//GNRMC, if (Serial_RxFlag == 1) { if(GNRMCD!= NULL) { OLED_ShowString(31,"               "); OLED_ShowString(31"LOCATED!"); OLED_ShowString(11,"                  "); //OLED_ShowString(1,1,GNRMCD); REVFORM = strtok(GNRMCD,","); count = 0; while(REVFORM!= NULL) { OLED_ShowString(21,"               "); OLED_ShowString(21, REVFORM); DRESS[count] = REVFORM; count++; /* CRFC = strtok(REVFORM,","); while(CRFC != NULL) { OLED_ShowString(3, 1,"               "); OLED_ShowString(3, 1, CRFC); CRFC = strtok(NULL,","); }*/ REVFORM = strtok(NULL,","); } //13:校验 //12:可靠性 //9:日期 //8:海拔 //7:海里/h //6:东西经 //5:东西经坐标 //4:半球 //3:南北半球坐标 //1:格林尼治时间 //0 OLED_ShowString(1,1,DRESS[5]); } // OLED_ShowString(4, 1,"               "); OLED_ShowString(41, Serial_RxPacket); Serial_RxFlag = 0; } } }


image.png

__________________________________________________________________________________________________

Serial.c

C++
#include "stm32f10x.h"                  // Device header #include <stdio.h> #include <stdarg.h> char Serial_RxPacket[100]; //"@MSG\r\n" uint8_t Serial_RxFlag; void Serial_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART2, &USART_InitStructure); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART2, ENABLE); } void Serial_SendByte(uint8_t Byte) { USART_SendData(USART2, Byte); while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); } void Serial_SendArray(uint8_t *Array, uint16_t Length) { uint16_t i; for (i = 0; i < Length; i ++) { Serial_SendByte(Array[i]); } } void Serial_SendString(char *String) { uint8_t i; for (i = 0; String[i] != '\0'; i ++) { Serial_SendByte(String[i]); } } uint32_t Serial_Pow(uint32_t X, uint32_t Y) { uint32_t Result = 1; while (Y --) { Result *= X; } return Result; } void Serial_SendNumber(uint32_t Number, uint8_t Length) { uint8_t i; for (i = 0; i < Length; i ++) { Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0'); } } int fputc(int ch, FILE *f) { Serial_SendByte(ch); return ch; } void Serial_Printf(char *format, ...) { char String[100]; va_list arg; va_start(arg, format); vsprintf(String, format, arg); va_end(arg); Serial_SendString(String); } void USART2_IRQHandler(void) { static uint8_t RxState = 0; static uint8_t pRxPacket = 0; if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET) { uint8_t RxData = USART_ReceiveData(USART2); if (RxState == 0) { if (RxData == '$' && Serial_RxFlag == 0) { RxState = 1; pRxPacket = 0; } } else if (RxState == 1) { if (RxData == '\r') { RxState = 2; } else { Serial_RxPacket[pRxPacket] = RxData; pRxPacket ++; } } else if (RxState == 2) { if (RxData == '\n') { RxState = 0; Serial_RxPacket[pRxPacket] = '\0'; Serial_RxFlag = 1; } } USART_ClearITPendingBit(USART2, USART_IT_RXNE); } }

(串口框架由B站“江科大”编写)

我将其改为了UART2,因而如需要直接复制粘贴需要注意接线方式。

(因为UART1我用来烧录了)


[修改于 1年6个月前 - 2023/10/11 14:07:39]

来自:电子信息 / 电子技术地球科学 / 地理与地质严肃内容:教程/课程
5
新版本公告
~~空空如也
虎哥
1年6个月前 IP:四川
926035

楼主需要买接收前端芯片,自己做定位计算,这个工作有一定难度,以前要FPGA来算,现在技术发达了,可以试试好一点的带DSP核的MCU。现成GPS模块对垂直上升率有限制,火箭上基本会罢工,根本不用考虑。北斗未见论坛上有人试过。

引用
评论
4
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ouybg作者
1年6个月前 IP:广东
926036
引用虎哥发表于1楼的内容
楼主需要买接收前端芯片,自己做定位计算,这个工作有一定难度,以前要FPGA来算,现在技术发达了,可以...

感谢教导!我之前看到过似乎对加速度有锁,我做这个的原因是用于着陆之后搜寻的,以下是我的想法:箭载飞控不承担距离换算,发射之后开始计时,在开伞后或者着陆后热启动(发射前进行冷启动),只是挑选合格的GNRMC数据发送,避免在数千平米的地方找。

(稍后会同步并补充这部分到文章里)

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
amo
1年6个月前 IP:广东
926037
引用ouybg发表于2楼的内容
感谢教导!我之前看到过似乎对加速度有锁,我做这个的原因是用于着陆之后搜寻的,以下是我的想法:箭载飞控...

只是为了回收的话,提前通电等定位成功再发射,然后定期单向广播坐标比较合适——箭在空中,信号覆盖不成问题,地面站可以用高增益天线。中间定位丢失影响不大。

如果降落时间足够长,那么进入降落程序再给模块通电也可以。

如果等到落地再启动,可能会掉进信号盲区/砸坏

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ouybg作者
1年6个月前 IP:广东
926135
引用amo发表于3楼的内容
只是为了回收的话,提前通电等定位成功再发射,然后定期单向广播坐标比较合适——箭在空中,信号覆盖不成问...

是的,这完全可以,不过我认为有难度。我目前意图制造的火箭最大高度不会超过200m,选用的无线电模块厂家介绍称1km完全没问题,不过到手之后会实测一下,之所以不考虑在着陆前进行信息发送的原因是:卫星定位模块在加速度达到4g时会自锁,目前我还不知道自锁的后果是什么,(参数详见我发的datasheet),以及另一个原因是由于高度小,在合适天气下也不会飘多远,在这种情况下大地曲率我认为可以通过给地面站天线加个杆子解决。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
白沫0001
1年5个月前 IP:天津
926642

我现在也是用的GPS,但是我的串口选择的是Zigbee,感觉解算卫星数据的时候好像会断连(可能是我没把天线放在箭体外部的原因),不过目前没遇到加速度导致自锁的情况

引用
评论
1
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
ouybg
进士 机友 笔友
文章
7
回复
12
学术分
0
2021/09/05注册,5个月12天前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:广东
新版本公告
1年6个月前
更新了开源的程序! 程序现在已经可以识别以下内容: //13:校验 //12:可靠性 //9:日期 //8:海拔 //7:海里/h //6:东西经 //5:东西经坐标 //4:半球 //3:南北半球坐标 //1:格林尼治时间
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

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