diff --git a/matlab/GPSaidedINS_cycling.m b/matlab/GPSaidedINS_cycling.m new file mode 100644 index 0000000000000000000000000000000000000000..320ebfc03c08c8c59481326c0e970429f32c7799 --- /dev/null +++ b/matlab/GPSaidedINS_cycling.m @@ -0,0 +1,333 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% function call: out_data=GPSaidedINS(in_data,settings) +% +% This function integrates GNSS and IMU data. The data fusion +% is based upon a loosely-coupled feedback GNSS-aided INS approach. +% +% Input struct: +% in_data.GNSS.pos_ned GNSS-receiver position estimates in NED +% coordinates [m] +% in_data.GNSS.t Time of GNSS measurements [s] +% (in_data.GNSS.HDOP GNSS Horizontal Dilution of Precision [-]) +% (in_data.GNSS.VDOP GNSS Vertical Dilution of Precision [-]) +% in_data.IMU.acc Accelerometer measurements [m/s^2] +% in_data.IMU.gyro Gyroscope measurements [rad/s] +% in_data.IMU.t Time of IMU measurements [s] +% +% Output struct: +% out_data.x_h Estimated navigation state vector [position; velocity; attitude] +% out_data.delta_u_h Estimated IMU biases [accelerometers; gyroscopes] +% out_data.diag_P Diagonal elements of the Kalman filter state +% covariance matrix. +% out_data.energy overall estimated power consumption +% out_data.error average RMS +% +% +% Edit: Isaac Skog (skog@kth.se), 2016-09-06 +% Revised: Bo Bernhardsson, 2018-01-01 +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function out_data=GPSaidedINS(in_data,settings) + + +% Copy data to variables with shorter name +u=[in_data.IMU.acc;in_data.IMU.gyro]; +t=in_data.IMU.t; + +%% Initialization +% Initialize the navigation state +x_h=init_navigation_state(u,settings); + +% Initialize the sensor bias estimate +delta_u_h=zeros(6,1); + +% Initialize the Kalman filter +[P,Q1,Q2,~,~]=init_filter(settings); + + +% Allocate memory for the output data +N=size(u,2); +out_data.x_h=zeros(10,N); +out_data.x_h(:,1)=x_h; +out_data.diag_P=zeros(15,N); +out_data.diag_P(:,1)=diag(P); +out_data.delta_u_h=zeros(6,N); + +%% Information fusion +ctr_gnss_data=1838; +GPS_position=true; + +%% Initialize GPS sensor +sensor.state='warm_start_available'; +sensor.fetchfp=1.0; +sensor.geteph=Inf; +sensor.ephExp=1800.0; +sensor.fp=0; +sensor.eph=5; +sv=5; %initialize the number of visible satellites +turn=true; +power=400; +energy=0; +P_treshold=settings.P_treshold; + +pre_state='startup'; %initialize previous state of the sensor (used for displaying state changes) +P_store=0; %store summed valued of the trace of P in time +sv_store=sv; %store number of visible satellites in time + +%for k=2:N +for k=200000:210000 + + % Sampling period + Ts=t(k)-t(k-1); + + %%%%%%%%%%%%%%%%% + %% GPS sensor %% + %%%%%%%%%%%%%%%%% + %randomized number of visible satellites + %sv = random_satellites(sv); + sv=5; + %control action + P_store=[P_store,sum(sqrt(out_data.diag_P(1:3,k-1)))]; + if sum(sqrt(out_data.diag_P(1:3,k-1)))<P_treshold + turn=false; + else + turn=true; + end + %update sensor state +% pre_state=sensor.state; + sensor=gps_randomized(t(k),sensor,false,sv); %remove _randomized for worst case + %determine if GPS position is available + GPS_position=true;%strcmp(sensor.state,'position_available'); + %compute power consumption + if ~(strcmp(sensor.state,'no_info')||strcmp(sensor.state,'warm_start_available')) + energy=energy+power*Ts; + end + +% %print new state and current time if it changes +% if ~strcmp(pre_state, sensor.state) +% sensor.state +% t(k) +% end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % Calibrate the sensor measurements using current sensor bias estimate. + u_h=u(:,k)+delta_u_h; + + % Update the INS navigation state + x_h=Nav_eq(x_h,u_h,Ts); + + % Get state space model matrices + [F,G]=state_space_model(x_h,u_h,Ts); + + % Time update of the Kalman filter state covariance. + P=F*P*F'+G*blkdiag(Q1,Q2)*G'; + + % Defualt measurement observation matrix and measurement covariance + % matrix + + y = in_data.GNSS.pos_ned(:,ctr_gnss_data); + H = [eye(3) zeros(3,12)]; + R = settings.sigma_gps^2*eye(3); + + ind = zeros(1,6); % index vector, describing available measurements + % Check if GNSS measurement is available + if t(k)==in_data.GNSS.t(ctr_gnss_data) + if GPS_position + ind(1:3)=1; + end + % Update GNSS data counter + ctr_gnss_data=min(ctr_gnss_data+1,length(in_data.GNSS.t)); + end + + ind=logical(ind); + H=H(ind,:); + y=y(ind); + R=R(ind,ind); + + % Calculate the Kalman filter gain. + K=(P*H')/(H*P*H'+R); + + % Update the perturbation state estimate. + z=[zeros(9,1); delta_u_h]+K*(y-H(:,1:6)*x_h(1:6)); + + % Correct the navigation states using current perturbation estimates. + x_h(1:6)=x_h(1:6)+z(1:6); + x_h(7:10)=Gamma(x_h(7:10),z(7:9)); + delta_u_h=z(10:15); + + % Update the Kalman filter state covariance. + P=(eye(15)-K*H)*P; + + % Save the data to the output data structure + out_data.x_h(:,k)=x_h; + out_data.diag_P(:,k)=diag(P); + out_data.delta_u_h(:,k)=delta_u_h; + out_data.sv(k)=sv; +end + +%% plot feedback over P matrix +%figure(11) +%plot(P_store),grid + +%% output for simulation of energy-accuracy tradeoff +out_data.energy=energy; +xest = out_data.x_h(2,:); +yest = out_data.x_h(1,:); +xgps = interp1(in_data.GNSS.t,in_data.GNSS.pos_ned(2,:),in_data.IMU.t,'linear','extrap')'; +ygps = interp1(in_data.GNSS.t,in_data.GNSS.pos_ned(1,:),in_data.IMU.t,'linear','extrap')'; +xerr = xest - xgps; +yerr = yest - ygps; +out_data.error = sqrt(mean(xerr.^2+yerr.^2)); +out_data.P_store=P_store; +end + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SUB-FUNCTIONS %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Init filter %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function [P,Q1,Q2,R,H]=init_filter(settings) + + +% Kalman filter state matrix +P=zeros(15); +P(1:3,1:3)=settings.factp(1)^2*eye(3); +P(4:6,4:6)=settings.factp(2)^2*eye(3); +P(7:9,7:9)=diag(settings.factp(3:5)).^2; +P(10:12,10:12)=settings.factp(6)^2*eye(3); +P(13:15,13:15)=settings.factp(7)^2*eye(3); + +% Process noise covariance +Q1=zeros(6); +Q1(1:3,1:3)=diag(settings.sigma_acc).^2; +Q1(4:6,4:6)=diag(settings.sigma_gyro).^2; + +Q2=zeros(6); +Q2(1:3,1:3)=settings.sigma_acc_bias^2*eye(3); +Q2(4:6,4:6)=settings.sigma_gyro_bias^2*eye(3); + +% GNSS-receiver position measurement noise +R=settings.sigma_gps^2*eye(3); + +% Observation matrix +H=[eye(3) zeros(3,12)]; + +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Init navigation state %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function x_h=init_navigation_state(u,settings) + + +% Calculate the roll and pitch +f=mean(u(:,1:100),2); +roll=atan2(-f(2),-f(3)); +pitch=atan2(f(1),norm(f(2:3))); + +% Initial coordinate rotation matrix +Rb2t=Rt2b([roll pitch settings.init_heading])'; + +% Calculate quaternions +q=dcm2q(Rb2t); + +% Initial navigation state vector +x_h=[zeros(6,1); q]; + +end + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% State transition matrix %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [F,G]=state_space_model(x,u,Ts) + +% Convert quaternion to DCM +Rb2t=q2dcm(x(7:10)); + +% Transform measured force to force in +% the tangent plane coordinate system. +f_t=Rb2t*u(1:3); +St=[0 -f_t(3) f_t(2); f_t(3) 0 -f_t(1); -f_t(2) f_t(1) 0]; + +% Only the standard errors included +O=zeros(3); +I=eye(3); +Fc=[O I O O O; + O O St Rb2t O; + O O O O -Rb2t; + O O O O O; + O O O O O]; + +% Approximation of the discret +% time state transition matrix +F=eye(15)+Ts*Fc; + +% Noise gain matrix +G=Ts*[O O O O; Rb2t O O O; O -Rb2t O O; O O I O; O O O I]; +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Error correction of quaternion %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function q=Gamma(q,epsilon) + +% Convert quaternion to DCM +R=q2dcm(q); + +% Construct skew symetric matrx +OMEGA=[0 -epsilon(3) epsilon(2); epsilon(3) 0 -epsilon(1); -epsilon(2) epsilon(1) 0]; + +% Correct the DCM matrix +R=(eye(3)-OMEGA)*R; + +% Calculte the corrected quaternions +q=dcm2q(R); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Random evolution of visible satellites %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function sv=random_satellites(sv_old) +coin=random('uniform',0,1); +sv=sv_old; +if sv_old==6 %simulate satellite loss of visibility + if coin<0.005 + sv=5; + end +end +if sv_old==5 %simulate satellite loss of visibility + if coin>0.99 + sv=6; + end + if coin<0.001 + sv=4; + end +end +if sv_old==4 %simulate satellite loss of visibility + if coin>0.99 + sv=5; + end + if coin<0.0005 + sv=3; + end +end +if sv_old==3 %simulate satellite loss of visibility + if coin>0.95 + sv=4; + end +end +end diff --git a/matlab/cycling_input_data.mat b/matlab/cycling_input_data.mat new file mode 100644 index 0000000000000000000000000000000000000000..114f764bd44b9ec372f904e6cc3f529924d9f18f Binary files /dev/null and b/matlab/cycling_input_data.mat differ