You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
7.3 KiB
203 lines
7.3 KiB
%% Set initial conditions |
|
clear all; |
|
dtSlow = 1/50; |
|
dtFast = 1/1000; |
|
rateMult = round(dtSlow/dtFast); |
|
duration = 60; |
|
indexLimitSlow = round(duration/dtSlow); |
|
indexLimitFast = indexLimitSlow*rateMult; |
|
|
|
% create data logging variables |
|
gimbal.time = zeros(1,indexLimitFast); |
|
gimbal.euler = zeros(3,indexLimitFast); |
|
gimbal.eulerTruth = zeros(3,indexLimitFast); |
|
gimbal.eulerError = zeros(3,indexLimitFast); |
|
|
|
% Use a random initial truth orientation |
|
phiInit = 0.1*randn; |
|
thetaInit = 0.1*randn; |
|
psiInit = 2*pi*rand - pi; |
|
quatTruth = EulToQuat([phiInit,thetaInit,psiInit]);% [1;0.05*randn;0.05*randn;2*(rand-0.5)]; |
|
quatLength = sqrt(quatTruth(1)^2 + quatTruth(2)^2 + quatTruth(3)^2 + quatTruth(4)^2); |
|
quatTruth = quatTruth / quatLength; |
|
TsnTruth = Quat2Tbn(quatTruth); |
|
|
|
% define the earths truth magnetic field |
|
declTruth = 10*pi/180; |
|
magEarthTruth = [0.25*cos(declTruth);0.25*sin(declTruth);-0.5]; |
|
|
|
% define the declination parameter assuming 2deg RMS error - this would be |
|
% obtained from the main EKF to take advantage of in-flight learning |
|
declParam = declTruth + 2*pi/180*randn; |
|
|
|
% define the magnetometer bias errors |
|
magMeasBias = 0.02*[randn;randn;randn]; |
|
|
|
% Define IMU bias errors and noise |
|
gyroBias = 1*pi/180*[randn;randn;randn]; |
|
accBias = 0.05*[randn;randn;randn]; |
|
gyroNoise = 0.01; |
|
accNoise = 0.05; |
|
|
|
% define the state covariances with the exception of the quaternion covariances |
|
Sigma_velNED = 0.5; % 1 sigma uncertainty in horizontal velocity components |
|
Sigma_dAngBias = 1*pi/180*dtSlow; % 1 Sigma uncertainty in delta angle bias |
|
Sigma_angErr = 1; % 1 Sigma uncertainty in angular misalignment (rad) |
|
covariance = single(diag([Sigma_angErr*[1;1;1];Sigma_velNED*[1;1;1];Sigma_dAngBias*[1;1;1]]).^2); |
|
|
|
% Initialise truth trajectory variables |
|
% fly a CCW circle with constant gimbal angles |
|
gPsiInit = 20*pi/180; % gimbal yaw |
|
gThetaInit = 0; % gimbal pitch |
|
gPhiInit = 0; % gimbal roll |
|
psiTruth = psiInit; |
|
radius = 20; |
|
gndSpd = 5; |
|
trackAngTruth = -pi; |
|
centripAccelMag = gndSpd/radius*gndSpd; |
|
gravAccel = [0;0;-9.81]; |
|
|
|
%% Main Loop |
|
hdgAlignedEKF=0; |
|
hdgAlignedGimbal=0; |
|
slowIndex = 0; |
|
delAngFast = [0;0;0]; |
|
delVelFast = [0;0;0]; |
|
delAngSlow = [0;0;0]; |
|
delVelSlow = [0;0;0]; |
|
prevAngRateMeas = [0;0;0]; |
|
prevAccelMeas = [0;0;0]; |
|
quatFast = [1;0;0;0]; |
|
quatFastSaved = quatFast; |
|
angRateBiasEKF = [0;0;0]; |
|
quatEKF = [1;0;0;0]; |
|
for fastIndex = 1:indexLimitFast % 1000 Hz gimbal prediction loop |
|
time = dtFast*fastIndex; |
|
% Calculate Truth Data |
|
% Need to replace this with a full kinematic model or test data |
|
% calculate truth angular rates - we don't start maneouvring until |
|
% heading alignment is complete |
|
psiRateTruth = gndSpd/radius*hdgAlignedEKF; |
|
angRateTruth = [0;0;psiRateTruth]; % constant yaw rate |
|
|
|
% calculate yaw and track angles |
|
psiTruth = psiTruth + psiRateTruth*dtFast; |
|
trackAngTruth = trackAngTruth + psiRateTruth*dtFast; |
|
|
|
% Cacluate truth quternion |
|
quatTruth = EulToQuat([phiInit,thetaInit,psiTruth]); |
|
|
|
% Calculate truth rotaton from sensor to NED |
|
TsnTruth = Quat2Tbn(quatTruth); |
|
|
|
% calculate truth accel vector |
|
centripAccel = centripAccelMag*[-sin(trackAngTruth);cos(trackAngTruth);0]; |
|
accelTruth = transpose(TsnTruth)*(gravAccel + centripAccel); |
|
|
|
% calculate truth velocity vector |
|
truthVel = gndSpd*[cos(trackAngTruth);sin(trackAngTruth);0]; |
|
|
|
% synthesise sensor measurements |
|
% Synthesise IMU measurements, adding bias and noise |
|
angRateMeas = angRateTruth + gyroBias + gyroNoise*[randn;randn;randn]; |
|
accelMeas = accelTruth + accBias + accNoise*[randn;randn;randn]; |
|
|
|
% synthesise velocity measurements |
|
measVel = truthVel; |
|
|
|
% synthesise gimbal angles |
|
gPhi = 0; |
|
gTheta = 0; |
|
gPsi = gPsiInit; |
|
|
|
% Define rotation from magnetometer to sensor using a 312 rotation sequence |
|
TmsTruth = calcTms(gPhi,gPsi,gTheta); |
|
|
|
% calculate rotation from NED to magnetometer axes Tnm = Tsm * Tns |
|
TnmTruth = transpose(TmsTruth) * transpose(TsnTruth); |
|
|
|
% synthesise magnetometer measurements adding sensor bias |
|
magMeas = TnmTruth*magEarthTruth + magMeasBias; |
|
|
|
% integrate the IMU measurements to produce delta angles and velocities |
|
% using a trapezoidal integrator |
|
if isempty(prevAngRateMeas) |
|
prevAngRateMeas = angRateMeas; |
|
end |
|
if isempty(prevAccelMeas) |
|
prevAccelMeas = accelMeas; |
|
end |
|
delAngFast = delAngFast + 0.5*(angRateMeas + prevAngRateMeas)*dtFast; |
|
delVelFast = delVelFast + 0.5*(accelMeas + prevAccelMeas)*dtFast; |
|
prevAngRateMeas = angRateMeas; |
|
prevAccelMeas = accelMeas; |
|
|
|
% Run an attitude prediction calculation at 1000Hz |
|
% Convert the rotation vector to its equivalent quaternion |
|
% using a first order approximation after applying the correction for |
|
% gyro bias using bias estimates from the EKF |
|
deltaQuat = [1;0.5*(angRateMeas - angRateBiasEKF)*dtFast]; |
|
% Update the quaternions by rotating from the previous attitude through |
|
% the delta angle rotation quaternion |
|
quatFast = QuatMult(quatFast,deltaQuat); |
|
% Normalise the quaternions |
|
quatFast = NormQuat(quatFast); |
|
% log the high rate data |
|
eulLogFast(:,fastIndex) = QuatToEul(quatFast); |
|
|
|
% every 20msec we send them to the EKF computer and reset |
|
% the total |
|
% we also save a copy of the quaternion from our high rate prediction |
|
if (rem(fastIndex,rateMult) == 0) |
|
delAngSlow = delAngFast; |
|
delVelSlow = delVelFast; |
|
delAngFast = [0;0;0]; |
|
delVelFast = [0;0;0]; |
|
quatFastSaved = quatFast; |
|
end |
|
|
|
% run the 50Hz EKF loop but do so 5 msec behind the |
|
% data transmission to simulate the effect of transmission and |
|
% computational delays |
|
if (rem(fastIndex,rateMult) == 5) |
|
slowIndex = slowIndex + 1; |
|
[quatEKF,angRateBiasEKF,EKFlogs,hdgAlignedEKF] = calcEKF(delAngSlow,delVelSlow,measVel,gPhi,gPsi,gTheta,magMeas,declParam,time,dtSlow,slowIndex,indexLimitSlow); |
|
end |
|
|
|
% Correct Gimbal attitude usng EKF data |
|
% Assume the gimbal controller receive the EKF solution 10 msec after |
|
% it sent the sensor data |
|
if (rem(fastIndex,rateMult) == 10) |
|
% calculate the quaternion from the EKF corrected attitude to the |
|
% attitude calculated using the local fast prediction algorithm |
|
deltaQuatFast = QuatDivide(quatEKF,quatFastSaved); |
|
% apply this correction to the fast solution at the current time |
|
% step (this can be applied across several steps to smooth the |
|
% output if required) |
|
quatFast = QuatMult(quatFast,deltaQuatFast); |
|
% normalise the resultant quaternion |
|
quatFast = NormQuat(quatFast); |
|
% flag when the gimbals own heading is aligned |
|
hdgAlignedGimbal = hdgAlignedEKF; |
|
end |
|
|
|
% Log gimbal data |
|
gimbal.time(fastIndex) = time; |
|
gimbal.euler(:,fastIndex) = QuatToEul(quatFast); |
|
gimbal.eulerTruth(:,fastIndex) = QuatToEul(quatTruth); |
|
if (hdgAlignedGimbal) |
|
gimbal.eulerError(:,fastIndex) = gimbal.euler(:,fastIndex) - gimbal.eulerTruth(:,fastIndex); |
|
if (gimbal.eulerError(3,fastIndex) > pi) |
|
gimbal.eulerError(3,fastIndex) = gimbal.eulerError(3,fastIndex) - 2*pi; |
|
elseif (gimbal.eulerError(3,fastIndex) < -pi) |
|
gimbal.eulerError(3,fastIndex) = gimbal.eulerError(3,fastIndex) + 2*pi; |
|
end |
|
else |
|
gimbal.eulerError(:,fastIndex) = [NaN;NaN;NaN]; |
|
end |
|
|
|
end |
|
|
|
%% Generate Plots |
|
close all; |
|
PlotData; |