clear ;%清除工作区的缓存变量 close all; % 关闭当前的所有窗口 clc; %清除命令行的数据 g = 9.7833; %尝试读取csv文件 data_file = csvread('LoggedData2_CalInertialAndMag.csv',1 ,0); %把陀螺仪数据,加速度数据拿出来 gyr_data = data_file(:, 2:4) *pi/180; acc_data = data_file(:, 5:7) * g; press_data = data_file(:,8:9)'; %参数设置 data_size = size(gyr_data,1); dt = 1/100; %当地重力加速度 %设置过程噪声 % 状态量为【姿态误差,速度误差,位置误差】 % 相应的过程噪声协方差 sigma_omega = 1e-2; sigma_a = 1e-2; % 观测噪声协方差,只观测速度误差 sigma_v = 1e-2; R = diag([sigma_v sigma_v sigma_v*0.1]).^2; R_xy = diag([sigma_v sigma_v sigma_v sigma_v sigma_v sigma_v]).^2; %初始化姿态矩阵 %先根据加速度计算俯仰角,翻滚角,航向角 pitch = asin(-acc_data(1,1)/g); roll = atan2(acc_data(1,2), acc_data(1,3)); yaw = 0; C_prev = [cos(yaw)*cos(pitch), -sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll), sin(yaw)*sin(roll)+ cos(yaw)*sin(pitch)*cos(roll); sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(yaw)*sin(pitch)*sin(roll), -cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)* cos(roll); -sin(pitch), cos(pitch)* sin(roll), cos(pitch)*cos(roll);]; C = C_prev; acc_n = zeros(3, data_size); acc_n(:,1) = C*acc_data(1,:)'-[0,0,g]'; vel_n = zeros(3, data_size); vel_n_norm = zeros(1,data_size); pos_n = zeros(3, data_size); P = zeros(9,9); H = [zeros(3,3), zeros(3,3), eye(3); ]; H_xy= [zeros(3,3), zeros(3,3), eye(3); zeros(3,3),eye(3),zeros(3,3); ]; gyr_norm = nan(1,data_size); acc_norm = nan(1,data_size); zupt = zeros(1,data_size); pitch_data = nan(1,data_size); roll_data = nan(1, data_size); yaw_data = nan(1, data_size); C_buff = zeros(3,3,data_size); C_buff(:,:,1) = C; last_zupt = 0; mini_update = 0; step_pos = zeros(3,1); has_zupt = 0; zupt_pos.x = 0; zupt_pos.y = 0; zupt_pos.z = 0; zupt_pos.yaw = 0; last_step_xy = [0;0]; last_step_xy_tmp = [0;0]; pos_result = zeros(3,data_size); last_pos = [0,0,0]'; last_zupt_index = 30; %以下为记录输出所用 last_record = 0; output_signal = 0; output_return = 0; stand_num = 0; vel_local = zeros(2, data_size); last_running = 1; gyr_norm_flag = 10000; for i = 2 : data_size %姿态矩阵更新, 其实有没有陀螺仪零片都没多大问题 % w= gyrData - gyrBias w = gyr_data(i,:)'; %陀螺仪K时刻求模,分析数据时候用 gyr_norm(i) = norm(w); %当前时刻陀螺仪生成的叉乘矩阵,姿态矩阵迭代需要 temp = [0, -w(3), w(2); w(3), 0, -w(1); -w(2), w(1), 0;]* dt; % 姿态矩阵迭代的经典公示了,也可以用其他的迭代公式 C= C_prev * (2*eye(3)+temp)/(2*eye(3) - temp); %速度位置初步更新 acc = acc_data(i,:)'; %加速度计,分析数据时候用 acc_norm(i) = norm(acc); %acc_n为当地的坐标系下的加速度表示,并减去了自身的重力加速度计 acc_n(:,i) = C * acc; %导航坐标系下的速度向量迭代公式 vel_n(:,i) = vel_n(:, i-1) + (acc_n(:,i)- [0,0,g]')*dt; %导航坐标系下的位置更新公式 pos_n(:,i) = pos_n(:,i-1) + vel_n(:,i) *dt ; %状态转移矩阵 S = [0, -acc_n(3,i), acc_n(2,i); acc_n(3,i), 0, -acc_n(1,i); -acc_n(2,i), acc_n(1,i), 0;]; %状态向量的顺序为[delta_pitch, delta_roll, delta_yaw, % delta_px, delta_py, delta_pz,] % delta_vx, delta_vy, delta_vz,] % xk = F*x(k-1); % 式子的意义 第一行代表 姿态角的误差预测没有改变 % 第二行代表 当前的位置误差 = 上一个时刻位置误差 + 上一个时刻速度误差*时间 % 第三行代表 当前的速度误差 = 上一个时刻速度误差 - 加速度X姿态角误差*时间 % 简单来说是由于姿态误差引起的加速度测量误差,再乘以时间,就得到额外的速度误差 F = [eye(3), zeros(3,3), zeros(3,3); zeros(3,3), eye(3), dt*eye(3); -dt*S, zeros(3,3), eye(3)]; %过程噪声的协方差矩阵 Q = diag([sigma_omega sigma_omega sigma_omega sigma_a sigma_a sigma_a sigma_a sigma_a sigma_a]*dt).^2; %卡尔曼里面的协方差矩阵迭代公式了 P = F*P*F' + Q; % 这里说明一下, if(norm(w)< 1.0) 是来判断IMU触碰到地上用的 % 一般走路 用不着达到1.0,0.35就可以了,但是剧烈运动是达到1.0的 % 用固定阈值来判断不是恰当的,可以根据前面一段(t-k:k) 陀螺仪数据是预测 % 或者根据一步间的IMU数据来衡量跑步还是普通走路 % 这里为了方便测试,用固定值1.0来算好了 if(i > 15) max_window = max(gyr_norm(i-14:i)); min_window = min(gyr_norm(i-14:i)); end output_return = 0; yaw_data(i) = atan2(C(2,1),C(1,1)); pitch_data(i) = asin(-C(3,1)); roll_data(i) = atan2(C(3,2),C(3,3)); pos_result(:,i) = pos_n(:,i) - last_pos; if( (((gyr_norm(i)< 0.6) && (i > 15) && ((max_window - min_window) < 0.6))... || ((gyr_norm(i)< 1.5) && (i > 15) && (i- last_zupt_index > 25) &&(max_window - min_window) > 6.0))... || ((gyr_norm(i)< 1.5) && (i > 15) && (i- last_zupt_index > 50) && (max_window - min_window) > 5.0)) if(last_zupt == 1) stand_num = stand_num + 1; output_signal = 1; %用来记录零速的时刻, 可以忽略 zupt(i) = 0.7; %计算卡尔曼滤波增益,公式公式 % 说明一下H矩阵为观测矩阵,在线性系统下就是值1.观测量需要用到哪一个就在zeros(1,9)中对应的位置为1 K = P*H'/(H*P*H' + R); delta_x = K * [vel_n(:,i)]; P = P - K*H*P; %姿态矩阵补偿 C_fix = [0, -delta_x(3), delta_x(2); delta_x(3), 0, -delta_x(1); -delta_x(2), delta_x(1), 0]; %姿态矩阵补偿 C = ((2*eye(3)-C_fix)/(2*eye(3) + C_fix))* C; %位置补偿 pos_n(:,i) = pos_n(:,i) - delta_x(4:6); vel_n(:,i) = vel_n(:,i) - delta_x(7:9); %防止脚地上的时候,Z方向的速度过快 yaw = 0.0; pitch = asin(-C(3,1)); roll = roll_data(i); C = [cos(pitch),sin(pitch)*sin(roll), sin(pitch)*cos(roll); 0.0, cos(roll), -1*sin(roll); -sin(pitch), cos(pitch)* sin(roll), cos(pitch)*cos(roll);]; last_pos = pos_n(:, i); %记录一步一步的位置移动 end last_zupt = 1; last_running = i; else gyr_norm_flag = 10000; if(last_zupt == 1) last_zupt_index = i-1; last_zupt = 0; end if(output_signal == 1 && pos_result(3,i) < -0.001 && i-last_zupt_index>10 ) if(pos_result(2,i) < - 0.5) disp(["MOVE_LEFT"]); output_return = 3; output_signal = 0; elseif pos_result(2,i) > 0.5 disp(["MOVE_RIGTH"]); i output_return = 4; output_signal = 0; elseif pos_result(3, i) < -0.5 disp(["MOVE_JUMP"]); output_return = 8; output_signal = 0; elseif pos_result(1, i) > 0.5 disp(["MOVE_BACKWARD"]); output_return = 2; output_signal = 0; elseif pos_result(1, i) < -0.5 disp(["MOVE_FORWARD"]); output_return = 5; output_signal = 0; elseif i - last_running > 30 disp(["MOVE_RUNNING"]); output_return = 7; last_running = i; end end end if(output_signal == 1 && stand_num > 100) output_signal = 0; output_return = 9; stand_num = 0; disp(["MOVE_stand"]); end %计算速度的均方值 vel_n_norm(i) = norm(vel_n(:, i)); %保持协方差矩阵的对称性 P = (P+P')/2; C_prev = C; vel_local(:,i) = [cos(yaw_data(i)), -sin(yaw_data(i)) ; sin(yaw_data(i)), cos(yaw_data(i))]*vel_n(1:2, i); C_buff(:,:,i) = C; %输出 end % 其实做这种游戏的姿态估计,能大概判断第K步相对于第K-1偏移多少就好(方向,x,y位置) % 例如判断左、右、前、后,移动的情况,可以根据 % theta_yaw = yaw(k)-yaw(k-1); % R = sqrt((pos_n(1,k)- pos_n(1,k-1))^2 + (pos_n(2,k)- pos_n(2,k-1))^2 ) % [x, y] =[cos(theta),sin(theta)]*R;就可以根据XY的符号判断左右前后了 % 跳的动作根据v_z, p_z来判断,初步是可以的; figure('NumberTitle', 'off', 'Name', 'Offset pos'); hold on; plot(pos_result(1,:), 'r'); plot(pos_result(2,:), 'g'); plot(pos_result(3,:), 'b'); hold on; index = find(zupt==0.9); plot(index,pos_result(3, index), 'ko'); grid; xlabel('sample'); ylabel('m'); title('Offset pos'); legend('X', 'Y', 'Z'); figure('NumberTitle', 'off', 'Name', 'Linear Velocity'); hold on; plot(vel_n(1,:), 'r'); plot(vel_n(2,:), 'g'); plot(vel_n(3,:), 'b'); plot(vel_n_norm(:), 'm') index = find(zupt>0.0); plot(index,vel_n(3, index), 'ko'); grid; xlabel('sample'); ylabel('m/s'); title('local velocity'); legend('X', 'Y', 'Z'); figure('NumberTitle', 'off', 'Name', 'Linear Position'); hold on; plot(pos_n(1,:), 'r'); plot(pos_n(2,:), 'g'); plot(pos_n(3,:), 'b'); grid; xlabel('sample'); ylabel('m'); title('local position'); legend('X', 'Y', 'Z'); % figure % plot3(pos_n(1,:), pos_n(2,:), pos_n(3,:)); % % pose_diff = step_pos(1:2, 2:end) - step_pos(1:2, 1:end-1); % figure % plot( step_pos(2,:)) % figure plot(gyr_norm(1,:),'r') hold on plot((acc_norm(1,:)-9.8)/5,'g'); hold on plot(index,(gyr_norm(1,index)), 'ko'); grid on figure plot3(pos_n(1,:), pos_n(2,:), pos_n(3, :),'r');grid on figure('NumberTitle', 'off', 'Name', 'IMU vel'); hold on; plot(vel_local(1,:), 'r'); plot(vel_local(2,:), 'g'); %% Animation %% The animation code was stolen from xioTechnologies. %% https://github.com/xioTechnologies/Gait-Tracking-With-x-IMU % L = data_size; % SamplePlotFreq = 4; % Spin = 120; % SixDofAnimation(pos_result(1:3,:)', C_buff, ... % 'SamplePlotFreq', SamplePlotFreq, 'Trail', 'All',... % 'Position', [9 39 1280 768],... % 'View', [(100:(Spin/(L-1)):(100+Spin))', 10*ones(L, 1)],... % 'AxisLength', 0.1, 'ShowArrowHead', false, ... % 'Xlabel', 'X (m)', 'Ylabel', 'Y (m)', 'Zlabel', 'Z (m)',... % 'ShowLegend', false,... % 'CreateVideo', false);