目录
文章目录
- 目录
- 摘要
- 1.更新滤波器
- 2.使用GPS和测距仪更新EKF2的速度,位置信息
-
摘要
本节主要记录自己看EKF2的速度位置融合算法。
1.更新滤波器
void NavEKF2_core::UpdateFilter(bool predict)
{
startPredictEnabled = predict;
if (!statesInitialised)
{
return;
}
#if EK2_DISABLE_INTERRUPTS
irqstate_t istate = irqsave();
#endif
hal.util->perf_begin(_perf_UpdateFilter);
imuSampleTime_ms = frontend->imuSampleTime_us / 1000;
controlFilterModes();
readIMUData();
if (runUpdates)
{
UpdateStrapdownEquationsNED();
CovariancePrediction();
SelectMagFusion();
SelectVelPosFusion();
SelectRngBcnFusion();
SelectFlowFusion();
SelectTasFusion();
SelectBetaFusion();
updateFilterStatus();
}
calcOutputStates();
hal.util->perf_end(_perf_UpdateFilter);
#if EK2_DISABLE_INTERRUPTS
irqrestore(istate);
#endif
}
2.使用GPS和测距仪更新EKF2的速度,位置信息
SelectVelPosFusion();
void NavEKF2_core::SelectVelPosFusion()
{
if (magFusePerformed && dtIMUavg < 0.005f && !posVelFusionDelayed)
{
posVelFusionDelayed = true;
return;
} else
{
posVelFusionDelayed = false;
}
extNavDataToFuse = storedExtNav.recall(extNavDataDelayed, imuDataDelayed.time_ms);
readGpsData();
gpsDataToFuse = storedGPS.recall(gpsDataDelayed,imuDataDelayed.time_ms);
if (gpsDataToFuse && PV_AidingMode == AID_ABSOLUTE)
{
if (frontend->_fusionModeGPS <= 1)
{
fuseVelData = true;
} else
{
fuseVelData = false;
}
fusePosData = true;
extNavUsedForPos = false;
Vector3f posOffsetBody = AP::gps().get_antenna_offset(gpsDataDelayed.sensor_idx) - accelPosOffset;
if (!posOffsetBody.is_zero())
{
if (fuseVelData)
{
Vector3f angRate = imuDataDelayed.delAng * (1.0f/imuDataDelayed.delAngDT);
Vector3f velOffsetBody = angRate % posOffsetBody;
Vector3f velOffsetEarth = prevTnb.mul_transpose(velOffsetBody);
gpsDataDelayed.vel.x -= velOffsetEarth.x;
gpsDataDelayed.vel.y -= velOffsetEarth.y;
gpsDataDelayed.vel.z -= velOffsetEarth.z;
}
Vector3f posOffsetEarth = prevTnb.mul_transpose(posOffsetBody);
gpsDataDelayed.pos.x -= posOffsetEarth.x;
gpsDataDelayed.pos.y -= posOffsetEarth.y;
gpsDataDelayed.hgt += posOffsetEarth.z;
}
if (fuseVelData)
{
velPosObs[0] = gpsDataDelayed.vel.x;
velPosObs[1] = gpsDataDelayed.vel.y;
velPosObs[2] = gpsDataDelayed.vel.z;
}
velPosObs[3] = gpsDataDelayed.pos.x;
velPosObs[4] = gpsDataDelayed.pos.y;
} else if (extNavDataToFuse && PV_AidingMode == AID_ABSOLUTE)
{
extNavUsedForPos = true;
activeHgtSource = HGT_SOURCE_EV;
fuseVelData = false;
fuseHgtData = true;
fusePosData = true;
velPosObs[3] = extNavDataDelayed.pos.x;
velPosObs[4] = extNavDataDelayed.pos.y;
velPosObs[5] = extNavDataDelayed.pos.z;
if (!use_compass())
{
extNavUsedForYaw = true;
if (!yawAlignComplete)
{
extNavYawResetRequest = true;
magYawResetRequest = false;
gpsYawResetRequest = false;
controlMagYawReset();
finalInflightYawInit = true;
} else
{
fuseEulerYaw();
}
} else
{
extNavUsedForYaw = false;
}
} else
{
fuseVelData = false;
fusePosData = false;
}
if (gpsYawResetRequest)
{
realignYawGPS();
}
selectHeightForFusion();
if (gpsDataToFuse && PV_AidingMode == AID_ABSOLUTE && gpsDataDelayed.sensor_idx != last_gps_idx)
{
last_gps_idx = gpsDataDelayed.sensor_idx;
posResetNE.x = stateStruct.position.x;
posResetNE.y = stateStruct.position.y;
stateStruct.position.x = gpsDataNew.pos.x;
stateStruct.position.y = gpsDataNew.pos.y;
posResetNE.x = stateStruct.position.x - posResetNE.x;
posResetNE.y = stateStruct.position.y - posResetNE.y;
for (uint8_t i=0; i<imu_buffer_length; i++)
{
storedOutput[i].position.x += posResetNE.x;
storedOutput[i].position.y += posResetNE.y;
}
outputDataNew.position.x += posResetNE.x;
outputDataNew.position.y += posResetNE.y;
outputDataDelayed.position.x += posResetNE.x;
outputDataDelayed.position.y += posResetNE.y;
lastPosReset_ms = imuSampleTime_ms;
if (activeHgtSource == HGT_SOURCE_GPS)
{
posResetD = stateStruct.position.z;
stateStruct.position.z = -hgtMea;
posResetD = stateStruct.position.z - posResetD;
outputDataNew.position.z += posResetD;
outputDataDelayed.position.z += posResetD;
for (uint8_t i=0; i<imu_buffer_length; i++)
{
storedOutput[i].position.z += posResetD;
}
lastPosResetD_ms = imuSampleTime_ms;
}
}
if (fuseHgtData && PV_AidingMode == AID_NONE)
{
velPosObs[3] = lastKnownPositionNE.x;
velPosObs[4] = lastKnownPositionNE.y;
fusePosData = true;
fuseVelData = false;
}
if (fuseVelData || fusePosData || fuseHgtData)
{
FuseVelPosNED();
fuseVelData = false;
fuseHgtData = false;
fusePosData = false;
}
}
从这里我们可以看出GPS和测距仪是被用在EKF2的观测方程中,预测方程来自加速度和陀螺仪,这里我们分成两部分进行整理,高度的算法和水平位置算法
1.高度融合算法
selectHeightForFusion();
void NavEKF2_core::selectHeightForFusion()
{
readRangeFinder();
rangeDataToFuse = storedRange.recall(rangeDataDelayed,imuDataDelayed.time_ms);
if (rangeDataToFuse)
{
AP_RangeFinder_Backend *sensor = frontend->_rng.get_backend(rangeDataDelayed.sensor_idx);
if (sensor != nullptr)
{
Vector3f posOffsetBody = sensor->get_pos_offset() - accelPosOffset;
if (!posOffsetBody.is_zero())
{
Vector3f posOffsetEarth = prevTnb.mul_transpose(posOffsetBody);
rangeDataDelayed.rng += posOffsetEarth.z / prevTnb.c.z;
}
}
}
readBaroData();
baroDataToFuse = storedBaro.recall(baroDataDelayed, imuDataDelayed.time_ms);
if (extNavUsedForPos)
{
activeHgtSource = HGT_SOURCE_EV;
} else if (((frontend->_useRngSwHgt > 0) || (frontend->_altSource == 1)) && (imuSampleTime_ms - rngValidMeaTime_ms < 500))
{
if (frontend->_altSource == 1)
{
activeHgtSource = HGT_SOURCE_RNG;
} else
{
float rangeMaxUse = 1e-4f * (float)frontend->_rng.max_distance_cm_orient(ROTATION_PITCH_270) * (float)frontend->_useRngSwHgt;
bool aboveUpperSwHgt = (terrainState - stateStruct.position.z) > rangeMaxUse;
bool belowLowerSwHgt = (terrainState - stateStruct.position.z) < 0.7f * rangeMaxUse;
float horizSpeed = norm(stateStruct.velocity.x, stateStruct.velocity.y);
bool dontTrustTerrain = ((horizSpeed > frontend->_useRngSwSpd) && filterStatus.flags.horiz_vel) || !terrainHgtStable;
float trust_spd_trigger = MAX((frontend->_useRngSwSpd - 1.0f),(frontend->_useRngSwSpd * 0.5f));
bool trustTerrain = (horizSpeed < trust_spd_trigger) && terrainHgtStable;
if ((aboveUpperSwHgt || dontTrustTerrain) && (activeHgtSource == HGT_SOURCE_RNG))
{
if (frontend->_altSource == 0)
{
activeHgtSource = HGT_SOURCE_BARO;
} else if (frontend->_altSource == 2)
{
activeHgtSource = HGT_SOURCE_GPS;
}
} else if (belowLowerSwHgt && trustTerrain && (activeHgtSource != HGT_SOURCE_RNG))
{
activeHgtSource = HGT_SOURCE_RNG;
}
}
}
else if ((frontend->_altSource == 2) && ((imuSampleTime_ms - lastTimeGpsReceived_ms) < 500) && validOrigin && gpsAccuracyGood)
{
activeHgtSource = HGT_SOURCE_GPS;
} else if ((frontend->_altSource == 3) && validOrigin && rngBcnGoodToAlign)
{
activeHgtSource = HGT_SOURCE_BCN;
} else
{
activeHgtSource = HGT_SOURCE_BARO;
}
bool lostRngHgt = ((activeHgtSource == HGT_SOURCE_RNG) && ((imuSampleTime_ms - rngValidMeaTime_ms) > 500));
bool lostGpsHgt = ((activeHgtSource == HGT_SOURCE_GPS) && ((imuSampleTime_ms - lastTimeGpsReceived_ms) > 2000));
bool lostExtNavHgt = ((activeHgtSource == HGT_SOURCE_EV) && ((imuSampleTime_ms - extNavMeasTime_ms) > 2000));
if (lostRngHgt || lostGpsHgt || lostExtNavHgt)
{
activeHgtSource = HGT_SOURCE_BARO;
}
if (baroDataToFuse)
{
if (activeHgtSource != HGT_SOURCE_BARO)
{
calcFiltBaroOffset();
}
if (!getTakeoffExpected())
{
const float gndHgtFiltTC = 0.5f;
const float dtBaro = frontend->hgtAvg_ms*1.0e-3f;
float alpha = constrain_float(dtBaro / (dtBaro+gndHgtFiltTC),0.0f,1.0f);
meaHgtAtTakeOff += (baroDataDelayed.hgt-meaHgtAtTakeOff)*alpha;
}
}
if (gpsDataToFuse &&
(((frontend->_originHgtMode & (1 << 0)) && (activeHgtSource == HGT_SOURCE_BARO)) ||
((frontend->_originHgtMode & (1 << 1)) && (activeHgtSource == HGT_SOURCE_RNG)))
)
{
correctEkfOriginHeight();
}
if (extNavDataToFuse && (activeHgtSource == HGT_SOURCE_EV))
{
hgtMea = -extNavDataDelayed.pos.z;
posDownObsNoise = sq(constrain_float(extNavDataDelayed.posErr, 0.1f, 10.0f));
}
else if (rangeDataToFuse && (activeHgtSource == HGT_SOURCE_RNG))
{
if (prevTnb.c.z >= 0.7)
{
hgtMea = MAX(rangeDataDelayed.rng * prevTnb.c.z, rngOnGnd);
hgtMea -= terrainState;
fuseHgtData = true;
velPosObs[5] = -hgtMea;
posDownObsNoise = sq(constrain_float(frontend->_rngNoise, 0.1f, 10.0f));
posDownObsNoise += sq(rangeDataDelayed.rng * frontend->_terrGradMax) * MAX(0.0f , (1.0f - sq(prevTnb.c.z)));
} else
{
fuseHgtData = false;
}
} else if (gpsDataToFuse && (activeHgtSource == HGT_SOURCE_GPS))
{
hgtMea = gpsDataDelayed.hgt;
velPosObs[5] = -hgtMea;
fuseHgtData = true;
if (gpsHgtAccuracy > 0.0f)
{
posDownObsNoise = sq(constrain_float(gpsHgtAccuracy, 1.5f * frontend->_gpsHorizPosNoise, 100.0f));
} else
{
posDownObsNoise = sq(constrain_float(1.5f * frontend->_gpsHorizPosNoise, 0.1f, 10.0f));
}
}
else if (baroDataToFuse && (activeHgtSource == HGT_SOURCE_BARO))
{
hgtMea = baroDataDelayed.hgt - baroHgtOffset;
velPosObs[5] = -hgtMea;
fuseHgtData = true;
posDownObsNoise = sq(constrain_float(frontend->_baroAltNoise, 0.1f, 10.0f));
if (getTakeoffExpected() || getTouchdownExpected())
{
posDownObsNoise *= frontend->gndEffectBaroScaler;
}
if (motorsArmed && getTakeoffExpected())
{
hgtMea = MAX(hgtMea, meaHgtAtTakeOff);
}
} else
{
fuseHgtData = false;
}
hgtRetryTime_ms = (useGpsVertVel && !velTimeout) ? frontend->hgtRetryTimeMode0_ms : frontend->hgtRetryTimeMode12_ms;
if (imuSampleTime_ms - lastHgtPassTime_ms > hgtRetryTime_ms)
{
hgtTimeout = true;
} else
{
hgtTimeout = false;
}
}
这部分重点需要注意的是:
1.选择主要的高度观察传感器源。
2.获取观察数据
1.主高度观察传感器
frontend->_altSource
AP_GROUPINFO("ALT_SOURCE", 9, NavEKF2, _altSource, 0),
这里通过这个参数选择主要的高度传感器源,
_altSource=0:气压计
_altSource=1:测距仪
_altSource=2:GPS
_altSource=3:无线电
2.获取观察数据
velPosObs[5] = -hgtMea;
其中Vector6 velPosObs; // 速度和位置组合测量组的观测(3x1 m,3x1 m/s)
2.进行高度估计
如果选择RTK高度,这里需要注意的是下面这段代码
if (gpsDataToFuse && PV_AidingMode == AID_ABSOLUTE && gpsDataDelayed.sensor_idx != last_gps_idx)
{
last_gps_idx = gpsDataDelayed.sensor_idx;
posResetNE.x = stateStruct.position.x;
posResetNE.y = stateStruct.position.y;
stateStruct.position.x = gpsDataNew.pos.x;
stateStruct.position.y = gpsDataNew.pos.y;
posResetNE.x = stateStruct.position.x - posResetNE.x;
posResetNE.y = stateStruct.position.y - posResetNE.y;
for (uint8_t i=0; i<imu_buffer_length; i++)
{
storedOutput[i].position.x += posResetNE.x;
storedOutput[i].position.y += posResetNE.y;
}
outputDataNew.position.x += posResetNE.x;
outputDataNew.position.y += posResetNE.y;
outputDataDelayed.position.x += posResetNE.x;
outputDataDelayed.position.y += posResetNE.y;
lastPosReset_ms = imuSampleTime_ms;
if (activeHgtSource == HGT_SOURCE_GPS)
{
posResetD = stateStruct.position.z;
stateStruct.position.z = -hgtMea;
posResetD = stateStruct.position.z - posResetD;
outputDataNew.position.z += posResetD;
outputDataDelayed.position.z += posResetD;
for (uint8_t i=0; i<imu_buffer_length; i++)
{
storedOutput[i].position.z += posResetD;
}
lastPosResetD_ms = imuSampleTime_ms;
}
}
执行数据融合接口
if (fuseVelData || fusePosData || fuseHgtData)
{
FuseVelPosNED();
fuseVelData = false;
fuseHgtData = false;
fusePosData = false;
}
void NavEKF2_core::FuseVelPosNED()
{
hal.util->perf_begin(_perf_FuseVelPosNED);
velHealth = false;
posHealth = false;
hgtHealth = false;
Vector3f velInnov;
bool fuseData[6] = {false,false,false,false,false,false};
uint8_t stateIndex;
uint8_t obsIndex;
Vector6 R_OBS;
Vector6 R_OBS_DATA_CHECKS;
float SK;
if (fuseVelData || fusePosData || fuseHgtData)
{
float posErr = frontend->gpsPosVarAccScale * accNavMag;
if (PV_AidingMode == AID_NONE)
{
if (tiltAlignComplete && motorsArmed)
{
R_OBS[0] = sq(constrain_float(frontend->_noaidHorizNoise, 0.5f, 50.0f));
} else
{
R_OBS[0] = sq(0.5f);
}
R_OBS[1] = R_OBS[0];
R_OBS[2] = R_OBS[0];
R_OBS[3] = R_OBS[0];
R_OBS[4] = R_OBS[0];
for (uint8_t i=0; i<=2; i++) R_OBS_DATA_CHECKS[i] = R_OBS[i];
} else
{
if (gpsSpdAccuracy > 0.0f)
{
R_OBS[0] = sq(constrain_float(gpsSpdAccuracy, frontend->_gpsHorizVelNoise, 50.0f));
R_OBS[2] = sq(constrain_float(gpsSpdAccuracy, frontend->_gpsVertVelNoise, 50.0f));
} else
{
R_OBS[0] = sq(constrain_float(frontend->_gpsHorizVelNoise, 0.05f, 5.0f)) + sq(frontend->gpsNEVelVarAccScale * accNavMag);
R_OBS[2] = sq(constrain_float(frontend->_gpsVertVelNoise, 0.05f, 5.0f)) + sq(frontend->gpsDVelVarAccScale * accNavMag);
}
R_OBS[1] = R_OBS[0];
if (gpsPosAccuracy > 0.0f)
{
R_OBS[3] = sq(constrain_float(gpsPosAccuracy, frontend->_gpsHorizPosNoise, 100.0f));
} else
{
R_OBS[3] = sq(constrain_float(frontend->_gpsHorizPosNoise, 0.1f, 10.0f)) + sq(posErr);
}
R_OBS[4] = R_OBS[3];
for (uint8_t i=0; i<=2; i++) R_OBS_DATA_CHECKS[i] = sq(constrain_float(frontend->_gpsHorizVelNoise, 0.05f, 5.0f)) + sq(frontend->gpsNEVelVarAccScale * accNavMag);
}
R_OBS[5] = posDownObsNoise;
for (uint8_t i=3; i<=5; i++) R_OBS_DATA_CHECKS[i] = R_OBS[i];
if (useGpsVertVel && fuseVelData && (frontend->_altSource != 2))
{
float hgtErr = stateStruct.position.z - velPosObs[5];
float velDErr = stateStruct.velocity.z - velPosObs[2];
if ((hgtErr*velDErr > 0.0f) && (sq(hgtErr) > 9.0f * (P[8][8] + R_OBS_DATA_CHECKS[5])) && (sq(velDErr) > 9.0f * (P[5][5] + R_OBS_DATA_CHECKS[2])))
{
badIMUdata = true;
} else
{
badIMUdata = false;
}
}
if (fusePosData)
{
innovVelPos[3] = stateStruct.position.x - velPosObs[3];
innovVelPos[4] = stateStruct.position.y - velPosObs[4];
varInnovVelPos[3] = P[6][6] + R_OBS_DATA_CHECKS[3];
varInnovVelPos[4] = P[7][7] + R_OBS_DATA_CHECKS[4];
float maxPosInnov2 = sq(MAX(0.01f * (float)frontend->_gpsPosInnovGate, 1.0f))*(varInnovVelPos[3] + varInnovVelPos[4]);
posTestRatio = (sq(innovVelPos[3]) + sq(innovVelPos[4])) / maxPosInnov2;
posHealth = ((posTestRatio < 1.0f) || badIMUdata);
if (PV_AidingMode == AID_NONE)
{
posHealth = true;
lastPosPassTime_ms = imuSampleTime_ms;
} else if (posHealth || posTimeout)
{
posHealth = true;
lastPosPassTime_ms = imuSampleTime_ms;
if (posTimeout || ((P[6][6] + P[7][7]) > sq(float(frontend->_gpsGlitchRadiusMax))))
{
ResetPosition();
ResetVelocity();
fusePosData = false;
fuseVelData = false;
zeroRows(P,6,7);
zeroCols(P,6,7);
P[6][6] = sq(float(0.5f*frontend->_gpsGlitchRadiusMax));
P[7][7] = P[6][6];
posTestRatio = 0.0f;
velTestRatio = 0.0f;
}
} else
{
posHealth = false;
}
}
if (fuseVelData)
{
uint8_t imax = 2;
if (frontend->_fusionModeGPS > 0 || PV_AidingMode != AID_ABSOLUTE || frontend->inhibitGpsVertVelUse)
{
imax = 1;
}
float innovVelSumSq = 0;
float varVelSum = 0;
for (uint8_t i = 0; i<=imax; i++)
{
stateIndex = i + 3;
velInnov[i] = stateStruct.velocity[i] - velPosObs[i];
varInnovVelPos[i] = P[stateIndex][stateIndex] + R_OBS_DATA_CHECKS[i];
innovVelSumSq += sq(velInnov[i]);
varVelSum += varInnovVelPos[i];
}
velTestRatio = innovVelSumSq / (varVelSum * sq(MAX(0.01f * (float)frontend->_gpsVelInnovGate, 1.0f)));
velHealth = ((velTestRatio < 1.0f) || badIMUdata);
if (velHealth || velTimeout)
{
velHealth = true;
lastVelPassTime_ms = imuSampleTime_ms;
if (PV_AidingMode == AID_ABSOLUTE && velTimeout)
{
ResetVelocity();
fuseVelData = false;
velTestRatio = 0.0f;
}
} else
{
velHealth = false;
}
}
if (fuseHgtData)
{
innovVelPos[5] = stateStruct.position.z - velPosObs[5];
varInnovVelPos[5] = P[8][8] + R_OBS_DATA_CHECKS[5];
hgtTestRatio = sq(innovVelPos[5]) / (sq(MAX(0.01f * (float)frontend->_hgtInnovGate, 1.0f)) * varInnovVelPos[5]);
hgtHealth = ((hgtTestRatio < 1.0f) || badIMUdata);
if (hgtHealth || hgtTimeout || (PV_AidingMode == AID_NONE && onGround))
{
if (onGround)
{
float dtBaro = (imuSampleTime_ms - lastHgtPassTime_ms)*1.0e-3f;
const float hgtInnovFiltTC = 2.0f;
float alpha = constrain_float(dtBaro/(dtBaro+hgtInnovFiltTC),0.0f,1.0f);
hgtInnovFiltState += (innovVelPos[5]-hgtInnovFiltState)*alpha;
} else
{
hgtInnovFiltState = 0.0f;
}
if (hgtTimeout)
{
ResetHeight();
}
hgtHealth = true;
lastHgtPassTime_ms = imuSampleTime_ms;
}
}
if (fuseVelData && velHealth)
{
fuseData[0] = true;
fuseData[1] = true;
if (useGpsVertVel) {
fuseData[2] = true;
}
tiltErrVec.zero();
}
if (fusePosData && posHealth) {
fuseData[3] = true;
fuseData[4] = true;
tiltErrVec.zero();
}
if (fuseHgtData && hgtHealth) {
fuseData[5] = true;
}
for (obsIndex=0; obsIndex<=5; obsIndex++)
{
if (fuseData[obsIndex])
{
stateIndex = 3 + obsIndex;
if (obsIndex <= 2)
{
innovVelPos[obsIndex] = stateStruct.velocity[obsIndex] - velPosObs[obsIndex];
R_OBS[obsIndex] *= sq(gpsNoiseScaler);
}
else if (obsIndex == 3 || obsIndex == 4)
{
innovVelPos[obsIndex] = stateStruct.position[obsIndex-3] - velPosObs[obsIndex];
R_OBS[obsIndex] *= sq(gpsNoiseScaler);
} else if (obsIndex == 5)
{
innovVelPos[obsIndex] = stateStruct.position[obsIndex-3] - velPosObs[obsIndex];
const float gndMaxBaroErr = 4.0f;
const float gndBaroInnovFloor = -0.5f;
if(getTouchdownExpected() && activeHgtSource == HGT_SOURCE_BARO)
{
innovVelPos[5] += constrain_float(-innovVelPos[5]+gndBaroInnovFloor, 0.0f, gndBaroInnovFloor+gndMaxBaroErr);
}
}
varInnovVelPos[obsIndex] = P[stateIndex][stateIndex] + R_OBS[obsIndex];
SK = 1.0f/varInnovVelPos[obsIndex];
for (uint8_t i= 0; i<=15; i++)
{
Kfusion[i] = P[i][stateIndex]*SK;
}
if (!inhibitMagStates)
{
for (uint8_t i = 16; i<=21; i++)
{
Kfusion[i] = P[i][stateIndex]*SK;
}
} else
{
for (uint8_t i = 16; i<=21; i++)
{
Kfusion[i] = 0.0f;
}
}
if (!inhibitWindStates)
{
Kfusion[22] = P[22][stateIndex]*SK;
Kfusion[23] = P[23][stateIndex]*SK;
} else
{
Kfusion[22] = 0.0f;
Kfusion[23] = 0.0f;
}
for (uint8_t i= 0; i<=stateIndexLim; i++)
{
for (uint8_t j= 0; j<=stateIndexLim; j++)
{
KHP[i][j] = Kfusion[i] * P[stateIndex][j];
}
}
bool healthyFusion = true;
for (uint8_t i= 0; i<=stateIndexLim; i++)
{
if (KHP[i][i] > P[i][i])
{
healthyFusion = false;
}
}
if (healthyFusion)
{
for (uint8_t i= 0; i<=stateIndexLim; i++)
{
for (uint8_t j= 0; j<=stateIndexLim; j++)
{
P[i][j] = P[i][j] - KHP[i][j];
}
}
ForceSymmetry();
ConstrainVariances();
stateStruct.angErr.zero();
for (uint8_t i = 0; i<=stateIndexLim; i++)
{
statesArray[i] = statesArray[i] - Kfusion[i] * innovVelPos[obsIndex];
}
stateStruct.quat.rotate(stateStruct.angErr);
if (obsIndex != 5)
{
tiltErrVec += stateStruct.angErr;
}
if (obsIndex == 0)
{
faultStatus.bad_nvel = false;
} else if (obsIndex == 1) {
faultStatus.bad_evel = false;
} else if (obsIndex == 2) {
faultStatus.bad_dvel = false;
} else if (obsIndex == 3) {
faultStatus.bad_npos = false;
} else if (obsIndex == 4) {
faultStatus.bad_epos = false;
} else if (obsIndex == 5) {
faultStatus.bad_dpos = false;
}
} else
{
if (obsIndex == 0) {
faultStatus.bad_nvel = true;
} else if (obsIndex == 1) {
faultStatus.bad_evel = true;
} else if (obsIndex == 2) {
faultStatus.bad_dvel = true;
} else if (obsIndex == 3) {
faultStatus.bad_npos = true;
} else if (obsIndex == 4) {
faultStatus.bad_epos = true;
} else if (obsIndex == 5) {
faultStatus.bad_dpos = true;
}
}
}
}
}
hal.util->perf_end(_perf_FuseVelPosNED);
}
这里有个地方需要注意,垂直方向上的观察速度来源
if (fuseVelData)
{
velPosObs[0] = gpsDataDelayed.vel.x;
velPosObs[1] = gpsDataDelayed.vel.y;
velPosObs[2] = gpsDataDelayed.vel.z;
}
velPosObs[3] = gpsDataDelayed.pos.x;
velPosObs[4] = gpsDataDelayed.pos.y;
水平方向的融合跟垂直方向基本相似,这里暂不讲述
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)