楼主需要买接收前端芯片,自己做定位计算,这个工作有一定难度,以前要FPGA来算,现在技术发达了,可以试试好一点的带DSP核的MCU。现成GPS模块对垂直上升率有限制,火箭上基本会罢工,根本不用考虑。北斗未见论坛上有人试过。
原因:此前也想过使用陀螺仪计算用以获得相对位移以指引回收操作,然而测试的时候发现不仅算法比较困难,而且精度和资源占用方面也不如我意。
因而转而使用卫星定位系统以进行定位。
这里使用的是微型民用定位模块(以下简称模块),由于较为靠近室内,冷启动和热启动耗时都较长,如果在室外,性能会好很多。
也尝试打印了一下数据,结果是这数据量明显不适合直接打印……
——————————————————————————
正文:
材料准备:
网购一个模块(要带陶瓷天线),USB-TTL
(虽然这里用的是外国产品,但是实际测试的效果上来看,我没觉得中科微的和外国的有什么区别)
STCisp或者其他的总之能读串口就行的软件,也可以用正规的解析软件。
调试:
TXD接RXD,反之亦然,如果没有安装串口驱动需要安装。(如不知道串口参见百度)
打开串口,一般不用改:
通电,状态灯点亮,把天线放在尽可能靠近室外的地方,这将会大大提高成功率,此时不需要打开串口,因为此时模块处于冷启动状态,需要较长的时间进行定位,此时收到的数据都是无意义的。
一般几分钟过后,模块可以确认位置后,此时状态灯闪烁,打开串口,用文本模式接收,可以得到一长串数据,例子:
如果使用专业软件这时候一般都可以有图了。(注意软件不要多开)
时间需要+8小时
会飘完全正常,搜索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:热启动模块并通过无线传送数据
相关参数可以参考这个:
——————————————————
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;
}
}
}
__________________________________________________________________________________________________
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我用来烧录了)
[修改于 1年2个月前 - 2023/10/11 14:07:39]
楼主需要买接收前端芯片,自己做定位计算,这个工作有一定难度,以前要FPGA来算,现在技术发达了,可以试试好一点的带DSP核的MCU。现成GPS模块对垂直上升率有限制,火箭上基本会罢工,根本不用考虑。北斗未见论坛上有人试过。
楼主需要买接收前端芯片,自己做定位计算,这个工作有一定难度,以前要FPGA来算,现在技术发达了,可以...
感谢教导!我之前看到过似乎对加速度有锁,我做这个的原因是用于着陆之后搜寻的,以下是我的想法:箭载飞控不承担距离换算,发射之后开始计时,在开伞后或者着陆后热启动(发射前进行冷启动),只是挑选合格的GNRMC数据发送,避免在数千平米的地方找。
(稍后会同步并补充这部分到文章里)
感谢教导!我之前看到过似乎对加速度有锁,我做这个的原因是用于着陆之后搜寻的,以下是我的想法:箭载飞控...
只是为了回收的话,提前通电等定位成功再发射,然后定期单向广播坐标比较合适——箭在空中,信号覆盖不成问题,地面站可以用高增益天线。中间定位丢失影响不大。
如果降落时间足够长,那么进入降落程序再给模块通电也可以。
如果等到落地再启动,可能会掉进信号盲区/砸坏
只是为了回收的话,提前通电等定位成功再发射,然后定期单向广播坐标比较合适——箭在空中,信号覆盖不成问...
是的,这完全可以,不过我认为有难度。我目前意图制造的火箭最大高度不会超过200m,选用的无线电模块厂家介绍称1km完全没问题,不过到手之后会实测一下,之所以不考虑在着陆前进行信息发送的原因是:卫星定位模块在加速度达到4g时会自锁,目前我还不知道自锁的后果是什么,(参数详见我发的datasheet),以及另一个原因是由于高度小,在合适天气下也不会飘多远,在这种情况下大地曲率我认为可以通过给地面站天线加个杆子解决。
200字以内,仅用于支线交流,主线讨论请采用回复功能。