123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- %% 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 manoeuvring 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;
|