所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
箭载飞控解析卫星定位数据(含代码!!!)
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>纬度XXXXXXXmm(度分)格式(前面的0也将被传输)
  <4> 纬度半球N(北半球)或S(南半球)
  <5>经度XXXXXXXXmm(度分)格式(前面的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=数据无效)

原文参考以下的连接-XXXXXXXXXXXXXXXXXXXXXXX/csMapx/archive/2011/11/02/XXXXXXXXXXml-,故不赘述。

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

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

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

1:发射前冷启动

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

3:预定的开伞或者着陆

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

相关参数可以参考这个:

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

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

main.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(4, 1, "BDS/GPS");
	char* REVFORM;
	char* GNRMCD;
	char* DRESS[14];
	unsigned char count;
	OLED_ShowString(3, 1, "UNLOCATED");	
	while (1)
	{ 
		GNRMCD = strstr(Serial_RxPacket,"GNRMC");//GNRMC,
		if (Serial_RxFlag == 1)
		{
			if(GNRMCD!= NULL)
			{
			OLED_ShowString(3, 1,"               ");
			OLED_ShowString(3, 1, "LOCATED!");	
			OLED_ShowString(1, 1,"                  ");
			//OLED_ShowString(1,1,GNRMCD);	
			REVFORM = strtok(GNRMCD,",");
				count = 0;
			while(REVFORM!= NULL)
			{
				OLED_ShowString(2, 1,"               ");
			OLED_ShowString(2, 1, 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(4, 1, Serial_RxPacket);	
				
			Serial_RxFlag = 0;
		}
	}
}


image.png

__________________________________________________________________________________________________

Serial.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我用来烧录了)


[修改于 4个月12天前 - 2023/10/11 14:07:39]

来自:电子信息 / 电子技术地球科学 / 地理与地质严肃内容:教程/课程
5
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
虎哥
4个月15天前 IP:四川
926035

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

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

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

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

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

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

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

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

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

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

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

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

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

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

所属专业
所属分类
上级专业
同级专业
ouybg
进士 机友 笔友
文章
7
回复
12
学术分
0
2021/09/05注册,7天12时前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:广东
新版本公告
4个月12天前
更新了开源的程序! 程序现在已经可以识别以下内容: //13:校验 //12:可靠性 //9:日期 //8:海拔 //7:海里/h //6:东西经 //5:东西经坐标 //4:半球 //3:南北半球坐标 //1:格林尼治时间
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}