PDR/1.Software/PDR 1.04/src/Location.c

389 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name : pdr_location.c
* Current Version : V1.2
* Author : logzhan
* Date of Issued : 2020.7.3
* Comments : PDR导航算法主流程
********************************************************************************/
/* Header File Including -----------------------------------------------------*/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include "Location.h"
#include "Kalman.h"
#include "Pedometer.h"
#include "AHRS.h"
#include "pdr_detector.h"
#include "Utils.h"
#include "scene_recognition.h"
#include "CoordTrans.h"
/* Global Variable Definition ------------------------------------------------*/
PDR_t* pdr; // PDR全局信息
StepPredict stepPredict = { 0 };
classifer sys;
GNSS_t pgnss; // 定位相关信息
EKFPara_t g_kfPara;
double GpsPos[HIST_GPS_NUM][3] = {{0}};
static uint32_t g_status;
extern AHRS_t g_ahrs;
double refGpsYaw = -1000;
double yaw_bias = -1;
float dir[2] = { 0 };
float bias_dir[2] = { 0 };
/**---------------------------------------------------------------------
* Function : PDRNav_Init
* Description : PDR导航系统初始化
* Date : 2022/09/21 logzhan
*---------------------------------------------------------------------**/
void PDRNav_Init(void) {
memset(&g_kfPara, 0, sizeof(g_kfPara));
memset(&pgnss, 0, sizeof(pgnss));
memset(GpsPos, 0, sizeof(GpsPos));
memset(&stepPredict, 0, sizeof(stepPredict));
memset(&sys, 0, sizeof(sys));
memset(dir, 0, sizeof(dir));
memset(bias_dir, 0, sizeof(bias_dir));
// GNSS定位初始化
InitGnssInfo();
// 初始化AHRS姿态解算相关
AHRS_Init();
// 检测器初始化
Detector_Init();
// PDR初始化
pdr = Base_Init();
//base文件中姿态角计算初始化
EKF_Init();
// 场景识别模块初始化
SceneRecog_Init();
yaw_bias = -1;
}
/**---------------------------------------------------------------------
* Function : InitGnssInfo
* Description : PDS GNSS定位信息初始化
* Date : 2020/02/01 logzhan:
* 创建初始版本
* 2020/02/08: logzhan 修改initGps为initGnssInfo
* 加符合函数功能
*---------------------------------------------------------------------**/
void InitGnssInfo(void) {
pgnss.lastError = ACCURACY_ERR_MAX;
pgnss.overVelCount = 0;
}
/**---------------------------------------------------------------------
* Function : ResetSystemStatus
* Description : PDR 重设系统状态
* Date : 2022/09/16 logzhan
*---------------------------------------------------------------------**/
void ResetSystemStatus(EKFPara_t *kf)
{
memset(kf, 0, sizeof(EKFPara_t));
pdr->fusionPdrFlg = PDR_FALSE;
pdr->sysStatus = IS_INITIAL;
pgnss.overVelCount = 0;
}
/**---------------------------------------------------------------------
* Function : InsLocation
* Description : PDR 惯性导航定位
* Date : 2022/09/16 logzhan
*---------------------------------------------------------------------**/
int InsLocation(IMU_t* ImuData, EKFPara_t* Ekf) {
// AHRS姿态更新占2%的运行时间
if (UpdateAHRS(ImuData)) {
// 完成姿态估计后计算PCA 占26%的运行时间
ComputePCA(&g_ahrs);
}
// 更新用户运动类型检测器IMU信息以及用户运动类型检测, 占21%的运行时间
DetectorUpdateIMU(ImuData);
pdr->gyroTime = ImuData->gyr.time;
// 为什么手持类型是0.1的运动频率?
if (pdr->motionType == DETECTOR_TYPE_HANDHELD)pdr->motionFreq = 0.1f;
// 计步器输入传感器数据占10%的运行时间
PedometerUpdate(ImuData, &pdr->steps);
// ----- Filter Initialization ------- //
if (pdr->sysStatus == IS_INITIAL) {
pdr->ts = ImuData->gyr.time;
pdr->lastSteps = pdr->steps;
stepPredict.lastTime = 0;
stepPredict.fnum = 0;
stepPredict.fsum = 0;
return 0;
}
pdr->ts = ImuData->gyr.time;
if ((0 == ImuData->acc.s[0] && 0 == ImuData->acc.s[1] && 0 == ImuData->acc.s[2]) ||
(0 == ImuData->mag.s[0] && 0 == ImuData->mag.s[1] && 0 == ImuData->mag.s[2])) {
return 0;
}
// 识别当前状态是否平稳这个函数计算量到达整个系统10%的计算量, 以后根据情况进行优化
StateRecognition(ImuData->acc.s, &sys);
//pdr->heading = CalPredHeading(...);
double imuYaw = - g_ahrs.yaw;
if (imuYaw < 0) {
imuYaw += 360;
}
pdr->heading = imuYaw / (180 / 3.14159265);
if (fabs(pdr->insHeadingOffset) > 0.000001) {
imuYaw = imuYaw - pdr->insHeadingOffset * (180 / 3.14159265);
pdr->heading = imuYaw / (180 / 3.14159265);
}
// PDR 惯性位置预测
InsLocationPredict(pdr, &stepPredict, ImuData, &pgnss, Ekf);
pdr->lastSteps = pdr->steps;
pdr->lastHeading = pdr->heading;
return 0;
}
/**----------------------------------------------------------------------
* Function : GnssInsFusionLocation
* Description : PDR GPS融合INS惯性定位
* Date : 2021/01/29 logzhan
*---------------------------------------------------------------------**/
int GnssInsFusionLocation(lct_nmea *nmea_data, EKFPara_t *kf, lct_fs *result)
{
// GPS数据更新
GnssUpdate(&pgnss, nmea_data);
// 检查时间差是否满足条件
if (fabs(pgnss.timestamps - pdr->ts) >= 1000)return TYPE_FIX_NONE;
// 根据nmea数据识别场景是否是开阔天空场景
pdr->sceneType = isOpenArea(nmea_data);
// 根据HDOP、速度、卫星数量等判断gps yaw是否可用
pdr->gpsSpeed = pgnss.vel * GPS_SPEED_UNIT;
pdr->lastGpsHeading = pdr->gpsHeading;
pdr->gpsHeading = GnssCalHeading(&pgnss);
// 如果在win32平台下仿真调试,则添加下面代码
#ifdef _WIN32
result->pdrHeading = kf->pXk[3];
result->gpsHeading = pdr->gpsHeading;
result->hdop = pgnss.HDOP;
result->accuracy = nmea_data->accuracy.err;
result->gpsSpeed = pdr->gpsSpeed;
result->motionType = pdr->motionType;
#endif
CalPdrHeadingOffset(nmea_data, pdr);
// 计算两次GPS时间之间步数变化量
pdr->deltaStepsPerGps = pdr->steps - pdr->lastGpsSteps;
pdr->lastGpsSteps = pdr->steps;
int mode = DetectFixMode(&pgnss, kf, pdr, result);
if (mode != TYPE_FIX_PDR)return mode;
//根据历史GPS拟合输出预估的GPS轨迹航向角
pdr->trackHeading = CalGnssTrackHeading(GpsPos, pgnss);
// 如果系统没有初始化,则先初始化
if (pdr->sysStatus == IS_INITIAL) {
return InitProc(&pgnss, kf, pdr, result);
}
// 如果惯导没有达到输出条件,则不预测轨迹
if (!pdr->fusionPdrFlg)return ResetOutputResult(&pgnss, pdr, kf, result);
// 根据GPS信息, 计算kf的过程噪声和观测噪声
EKFCalQRMatrix(nmea_data, pdr, &sys, kf);
if (pgnss.satellites_num > 4){
// 如果GPS航向角处于无效状态且速度也是无效状态
if (pgnss.yaw == INVAILD_GPS_YAW || pgnss.vel <= 0.0) {
//if(pdr->heading != INVAILD_GPS_YAW){
// pgnss.yaw = (float)(R2D(pdr->heading));
//}else if (pdr->trackHeading != INVAILD_GPS_YAW) {
// pgnss.yaw = (float)(R2D(pdr->trackHeading));
//}
pgnss.yaw = (float)kf->pXk[3];
}
if (pgnss.yaw != INVAILD_GPS_YAW) {
GnssInsLocFusion(&pgnss, pdr, &sys, yaw_bias, kf, result);
return TYPE_FIX_PDR;
}
}
// 不满足推算条件直接输出原始GPS
if (pgnss.satellites_num > 4 && nmea_data->accuracy.err > 0 &&
nmea_data->accuracy.err < 15) {
OutputOriginGnssPos(&pgnss, result);
pdr->NoGnssCount = 0;
return TYPE_FIX_GPS;
}
// 如果没有GPS信号那么PDR也会接着推算位置, 但是最多推算10个点
if (pdr->NoGnssCount < MAX_NO_GPS_PREDICT) {
// 无GPS状态位置推算
NoGnssInfoPredict(kf, result, pdr);
return TYPE_FIX_PDR;
}
pdr->NoGnssCount++;
return 0;
}
/**----------------------------------------------------------------------
* Function : NoGnssInfoPredict
* Description : 在没有gps信息时预测GPS位置
* Date : 2022/09/16
*---------------------------------------------------------------------**/
void NoGnssInfoPredict(EKFPara_t* kf, lct_fs* result, PDR_t* p_pdr)
{
double ned[3] = {0}, lla[3] = {0};
ned[0] = kf->pXk[0] - p_pdr->x0;
ned[1] = kf->pXk[1] - p_pdr->y0;
ned[2] = 0;
NED2WGS84(p_pdr->pllaInit, ned, lla);
result->latitude = lla[0];
result->latitude_ns = pgnss.lat_ns;
result->longitudinal = lla[1];
result->longitudinal_ew = pgnss.lon_ew;
result->time = pgnss.timestamps;
p_pdr->NoGnssCount++;
}
/**----------------------------------------------------------------------
* Function : calPdrHeadingOffset
* Description : 利用GPS信号较好时的偏航角修正磁力计计算方向键的偏移
* Date : 2020/07/08 logzhan
*---------------------------------------------------------------------**/
void CalPdrHeadingOffset(lct_nmea* nmea_data, PDR_t* p_pdr) {
//惯导姿态角与GPS行进角度的对准判断GPS信号是否正常以及惯导是否平稳
if (refGpsYaw != -1000)return;
if ((pgnss.satellites_num > 4 && pgnss.HDOP < 2.0 && pgnss.yaw != -1 && pgnss.vel >= 1.0)
&& (nmea_data->accuracy.err > 0 && nmea_data->accuracy.err < 4)
&& (pdr_detStatic(pdr, &pgnss, (p_pdr->steps - p_pdr->lastSteps)) == 0 && pdr->motionType == 2))
{
/*这里放姿态角与GPS对准part1 start*/
double imuHeading = atan2(2 * (g_ahrs.qnb[0] * g_ahrs.qnb[3] + g_ahrs.qnb[1] * g_ahrs.qnb[2]),
1 - 2 * (g_ahrs.qnb[2] * g_ahrs.qnb[2] + g_ahrs.qnb[3] * g_ahrs.qnb[3]));
if (imuHeading < 0) {
imuHeading += TWO_PI;
}
if (fabs(imuHeading - p_pdr->gpsHeading) < PI / 18)
{
//printf("offset 标定\n");
//p_pdr->insHeadingOffset = imuHeading - p_pdr->gpsHeading;
}
}
}
/**----------------------------------------------------------------------
* Function : pdr_detectFixMode
* Description : 检测当前PDR处于的模式如果是车载和静止模式,根据情况选择输出
* 原始GPS或者不输出
* Date : 2020/07/08 logzhan : 修改-1为INVAILD_GPS_YAW提高
* 代码的可读性
*---------------------------------------------------------------------**/
int DetectFixMode(GNSS_t* pgnss, EKFPara_t* kf, PDR_t* g_pdr, lct_fs* result) {
// 检测用户是否处于静止状态
if (pdr_detStatic(pdr, pgnss, g_pdr->deltaStepsPerGps)) {
if (pgnss->error > 0 && pgnss->error < 15 &&
pgnss->lastError >= pgnss->error) {
OutputOriginGnssPos(pgnss, result);
pgnss->lastError = pgnss->error;
return TYPE_FIX_PDR;
}
// 否则不输出位置
return TYPE_FIX_NONE;
}
// 如果是检测到是开车状态则输出GPS位置
if (DetectCarMode(pgnss, pdr, pdr->deltaStepsPerGps, &pgnss->overVelCount)) {
// 如果检测到车载模式则重置PDR状态需要重新初始化
ResetSystemStatus(kf);
if (pgnss->error >= 0 || (pgnss->satellites_num > 4
&& pgnss->HDOP < 2.0)) {
OutputOriginGnssPos(pgnss, result);
return TYPE_FIX_GPS;
}
// 精度过差,不输出位置
return TYPE_FIX_NONE;
}
return TYPE_FIX_PDR;
}
/**----------------------------------------------------------------------
* Function : GnssCalHeading
* Description : 获取GPS偏航角(0 - 360°)
* Date : 2022/09/16 logzhan
* 代码的可读性
*---------------------------------------------------------------------**/
double GnssCalHeading(GNSS_t* pgnss) {
double gnssHeading = INVAILD_GPS_YAW;
if (pgnss->satellites_num > 4 && pgnss->HDOP < 2.0 && pgnss->vel >= 1.0
&& pgnss->yaw != INVAILD_GPS_YAW && pgnss->error < 50) {
gnssHeading = pgnss->yaw * RADIAN_PER_DEG;
}
return gnssHeading;
}
/**----------------------------------------------------------------------
* Function : pdr_nmea2Gnss
* Description : nmea数据结构转gnss数据
* Date : 2020/07/08 logzhan
*---------------------------------------------------------------------**/
void Nmea2Gnss(lct_nmea* nmea_data, GNSS_t* pgnss)
{
lct_nmea_rmc rmc = { 0 };
if (nmea_data->rmc[0].time > nmea_data->rmc[1].time) {
rmc = nmea_data->rmc[0];
}else {
rmc = nmea_data->rmc[1];
}
// 更新上一次的Accuracy Error
pgnss->lastError = pgnss->error;
pgnss->lat = rmc.latitude * PI / 180;
pgnss->lat_ns = rmc.rmc_latitude_ns;
pgnss->lon = rmc.longitudinal * PI / 180;
pgnss->lon_ew = rmc.longitudinal_ew;
//pgnss->vel = rmc.speed * GPS_SPEED_UNIT;
pgnss->vel = rmc.speed;
pgnss->HDOP = nmea_data->gsa[0].hdop;
pgnss->yaw = rmc.cog;
pgnss->quality = 0;
pgnss->error = nmea_data->accuracy.err;
pgnss->timestamps = rmc.time;
pgnss->satellites_num = nmea_data->gga.satellites_nb;
}
/**----------------------------------------------------------------------
* Function : GnssUpdate
* Description : nmea数据结构转gnss数据
* Date : 2020/07/08 logzhan
* 2020/02/09 logzhan : 函数对应原pdr_gpsUpdate函数
* 经过引用分析发现每次GPS更新拷贝gnss结构体和nmea结构体其实没
* 什么用,还增加运算量
*---------------------------------------------------------------------**/
void GnssUpdate(GNSS_t* gps, lct_nmea* nmea) {
// nmea数据转gnss数据结构
Nmea2Gnss(nmea, &pgnss);
// GPS回调更新暂时不清楚函数功能
gpsUpdateCb(gps);
}