main.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. clear ;%清除工作区的缓存变量
  2. close all; % 关闭当前的所有窗口
  3. clc; %清除命令行的数据
  4. g = 9.7833;
  5. %尝试读取csv文件
  6. data_file = csvread('LoggedData2_CalInertialAndMag.csv',1 ,0);
  7. %把陀螺仪数据,加速度数据拿出来
  8. gyr_data = data_file(:, 2:4) *pi/180;
  9. acc_data = data_file(:, 5:7) * g;
  10. press_data = data_file(:,8:9)';
  11. %参数设置
  12. data_size = size(gyr_data,1);
  13. dt = 1/100;
  14. %当地重力加速度
  15. %设置过程噪声
  16. % 状态量为【姿态误差,速度误差,位置误差】
  17. % 相应的过程噪声协方差
  18. sigma_omega = 1e-2; sigma_a = 1e-2;
  19. % 观测噪声协方差,只观测速度误差
  20. sigma_v = 1e-2;
  21. R = diag([sigma_v sigma_v sigma_v*0.1]).^2;
  22. R_xy = diag([sigma_v sigma_v sigma_v sigma_v sigma_v sigma_v]).^2;
  23. %初始化姿态矩阵
  24. %先根据加速度计算俯仰角,翻滚角,航向角
  25. pitch = asin(-acc_data(1,1)/g);
  26. roll = atan2(acc_data(1,2), acc_data(1,3));
  27. yaw = 0;
  28. 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);
  29. sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(yaw)*sin(pitch)*sin(roll), -cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)* cos(roll);
  30. -sin(pitch), cos(pitch)* sin(roll), cos(pitch)*cos(roll);];
  31. C = C_prev;
  32. acc_n = zeros(3, data_size);
  33. acc_n(:,1) = C*acc_data(1,:)'-[0,0,g]';
  34. vel_n = zeros(3, data_size);
  35. vel_n_norm = zeros(1,data_size);
  36. pos_n = zeros(3, data_size);
  37. P = zeros(9,9);
  38. H = [zeros(3,3), zeros(3,3), eye(3);
  39. ];
  40. H_xy= [zeros(3,3), zeros(3,3), eye(3);
  41. zeros(3,3),eye(3),zeros(3,3);
  42. ];
  43. gyr_norm = nan(1,data_size);
  44. acc_norm = nan(1,data_size);
  45. zupt = zeros(1,data_size);
  46. pitch_data = nan(1,data_size);
  47. roll_data = nan(1, data_size);
  48. yaw_data = nan(1, data_size);
  49. C_buff = zeros(3,3,data_size);
  50. C_buff(:,:,1) = C;
  51. last_zupt = 0;
  52. mini_update = 0;
  53. step_pos = zeros(3,1);
  54. has_zupt = 0;
  55. zupt_pos.x = 0;
  56. zupt_pos.y = 0;
  57. zupt_pos.z = 0;
  58. zupt_pos.yaw = 0;
  59. last_step_xy = [0;0];
  60. last_step_xy_tmp = [0;0];
  61. pos_result = zeros(3,data_size);
  62. last_pos = [0,0,0]';
  63. last_zupt_index = 30;
  64. %以下为记录输出所用
  65. last_record = 0;
  66. output_signal = 0;
  67. output_return = 0;
  68. stand_num = 0;
  69. vel_local = zeros(2, data_size);
  70. last_running = 1;
  71. for i = 2 : data_size
  72. %姿态矩阵更新, 其实有没有陀螺仪零片都没多大问题
  73. % w= gyrData - gyrBias
  74. w = gyr_data(i,:)';
  75. %陀螺仪K时刻求模,分析数据时候用
  76. gyr_norm(i) = norm(w);
  77. %当前时刻陀螺仪生成的叉乘矩阵,姿态矩阵迭代需要
  78. temp = [0, -w(3), w(2);
  79. w(3), 0, -w(1);
  80. -w(2), w(1), 0;]* dt;
  81. % 姿态矩阵迭代的经典公示了,也可以用其他的迭代公式
  82. C= C_prev * (2*eye(3)+temp)/(2*eye(3) - temp);
  83. %速度位置初步更新
  84. acc = acc_data(i,:)';
  85. %加速度计,分析数据时候用
  86. acc_norm(i) = norm(acc);
  87. %acc_n为当地的坐标系下的加速度表示,并减去了自身的重力加速度计
  88. acc_n(:,i) = C * acc;
  89. %导航坐标系下的速度向量迭代公式
  90. vel_n(:,i) = vel_n(:, i-1) + (acc_n(:,i)- [0,0,g]')*dt;
  91. %导航坐标系下的位置更新公式
  92. pos_n(:,i) = pos_n(:,i-1) + vel_n(:,i) *dt ;
  93. %状态转移矩阵
  94. S = [0, -acc_n(3,i), acc_n(2,i);
  95. acc_n(3,i), 0, -acc_n(1,i);
  96. -acc_n(2,i), acc_n(1,i), 0;];
  97. %状态向量的顺序为[delta_pitch, delta_roll, delta_yaw,
  98. % delta_px, delta_py, delta_pz,]
  99. % delta_vx, delta_vy, delta_vz,]
  100. % xk = F*x(k-1);
  101. % 式子的意义 第一行代表 姿态角的误差预测没有改变
  102. % 第二行代表 当前的位置误差 = 上一个时刻位置误差 + 上一个时刻速度误差*时间
  103. % 第三行代表 当前的速度误差 = 上一个时刻速度误差 - 加速度X姿态角误差*时间
  104. % 简单来说是由于姿态误差引起的加速度测量误差,再乘以时间,就得到额外的速度误差
  105. F = [eye(3), zeros(3,3), zeros(3,3);
  106. zeros(3,3), eye(3), dt*eye(3);
  107. -dt*S, zeros(3,3), eye(3)];
  108. %过程噪声的协方差矩阵
  109. Q = diag([sigma_omega sigma_omega sigma_omega sigma_a sigma_a sigma_a sigma_a sigma_a sigma_a]*dt).^2;
  110. %卡尔曼里面的协方差矩阵迭代公式了
  111. P = F*P*F' + Q;
  112. % 这里说明一下, if(norm(w)< 1.0) 是来判断IMU触碰到地上用的
  113. % 一般走路 用不着达到1.0,0.35就可以了,但是剧烈运动是达到1.0的
  114. % 用固定阈值来判断不是恰当的,可以根据前面一段(t-k:k) 陀螺仪数据是预测
  115. % 或者根据一步间的IMU数据来衡量跑步还是普通走路
  116. % 这里为了方便测试,用固定值1.0来算好了
  117. if(i > 15)
  118. max_window = max(gyr_norm(i-14:i));
  119. min_window = min(gyr_norm(i-14:i));
  120. end
  121. output_return = 0;
  122. yaw_data(i) = atan2(C(2,1),C(1,1));
  123. pitch_data(i) = asin(-C(3,1));
  124. roll_data(i) = atan2(C(3,2),C(3,3));
  125. pos_result(:,i) = pos_n(:,i) - last_pos;
  126. if( (((gyr_norm(i)< 0.6) && (i > 15) && ((max_window - min_window) < 0.6))...
  127. || ((gyr_norm(i)< 1.5) && (i > 15) && (i- last_zupt_index > 25) &&(max_window - min_window) > 6.0))...
  128. || ((gyr_norm(i)< 1.5) && (i > 15) && (i- last_zupt_index > 50) && (max_window - min_window) > 5.0))
  129. if(last_zupt == 1)
  130. stand_num = stand_num + 1;
  131. output_signal = 1;
  132. %用来记录零速的时刻, 可以忽略
  133. zupt(i) = 0.7;
  134. %计算卡尔曼滤波增益,公式公式
  135. % 说明一下H矩阵为观测矩阵,在线性系统下就是值1.观测量需要用到哪一个就在zeros(1,9)中对应的位置为1
  136. K = P*H'/(H*P*H' + R);
  137. delta_x = K * [vel_n(:,i)];
  138. P = P - K*H*P;
  139. %姿态矩阵补偿
  140. C_fix = [0, -delta_x(3), delta_x(2);
  141. delta_x(3), 0, -delta_x(1);
  142. -delta_x(2), delta_x(1), 0];
  143. %姿态矩阵补偿
  144. C = ((2*eye(3)-C_fix)/(2*eye(3) + C_fix))* C;
  145. %位置补偿
  146. pos_n(:,i) = pos_n(:,i) - delta_x(4:6);
  147. vel_n(:,i) = vel_n(:,i) - delta_x(7:9);
  148. %防止脚地上的时候,Z方向的速度过快
  149. yaw = 0.0;
  150. pitch = asin(-C(3,1));
  151. roll = roll_data(i);
  152. C = [cos(pitch),sin(pitch)*sin(roll), sin(pitch)*cos(roll);
  153. 0.0, cos(roll), -1*sin(roll);
  154. -sin(pitch), cos(pitch)* sin(roll), cos(pitch)*cos(roll);];
  155. %记录一步一步的位置移动
  156. end
  157. last_zupt = 1;
  158. last_running = i;
  159. else
  160. if(last_zupt == 1)
  161. last_pos = pos_n(:, i);
  162. last_zupt_index = i-1;
  163. last_zupt = 0;
  164. end
  165. if(output_signal == 1 && pos_result(3,i) < 0)
  166. if(vel_n(2,i) < - 2.0)
  167. disp(["MOVE_LEFT"]);
  168. output_return = 3;
  169. output_signal = 0;
  170. elseif vel_n(2,i) > 2.0
  171. disp(["MOVE_RIGTH"]);
  172. output_return = 4;
  173. output_signal = 0;
  174. elseif vel_n(1, i) < -2.0
  175. disp(["MOVE_BACKWARD"]);
  176. output_return = 2;
  177. output_signal = 0;
  178. elseif vel_n(1, i) > 2.0
  179. disp(["MOVE_FORWARD"]);
  180. output_return = 5;
  181. output_signal = 0;
  182. elseif vel_n(3, i) < -2.5
  183. disp(["MOVE_JUMP"]);
  184. output_return = 8;
  185. output_signal = 0;
  186. elseif i - last_running > 30
  187. disp(["MOVE_RUNNING"]);
  188. output_return = 7;
  189. last_running = i;
  190. end
  191. end
  192. end
  193. if(output_signal == 1 && stand_num > 100)
  194. output_signal = 0;
  195. output_return = 9;
  196. stand_num = 0;
  197. % disp(["MOVE_stand"]);
  198. end
  199. %计算速度的均方值
  200. vel_n_norm(i) = norm(vel_n(:, i));
  201. %保持协方差矩阵的对称性
  202. P = (P+P')/2;
  203. C_prev = C;
  204. vel_local(:,i) = [cos(yaw_data(i)), -sin(yaw_data(i)) ;
  205. sin(yaw_data(i)), cos(yaw_data(i))]*vel_n(1:2, i);
  206. C_buff(:,:,i) = C;
  207. %输出
  208. end
  209. % 其实做这种游戏的姿态估计,能大概判断第K步相对于第K-1偏移多少就好(方向,x,y位置)
  210. % 例如判断左、右、前、后,移动的情况,可以根据
  211. % theta_yaw = yaw(k)-yaw(k-1);
  212. % R = sqrt((pos_n(1,k)- pos_n(1,k-1))^2 + (pos_n(2,k)- pos_n(2,k-1))^2 )
  213. % [x, y] =[cos(theta),sin(theta)]*R;就可以根据XY的符号判断左右前后了
  214. % 跳的动作根据v_z, p_z来判断,初步是可以的;
  215. figure('NumberTitle', 'off', 'Name', 'Offset pos');
  216. hold on;
  217. plot(pos_result(1,:), 'r');
  218. plot(pos_result(2,:), 'g');
  219. plot(pos_result(3,:), 'b');
  220. hold on;
  221. index = find(zupt==0.9);
  222. plot(index,pos_result(3, index), 'ko');
  223. grid;
  224. xlabel('sample');
  225. ylabel('m');
  226. title('Offset pos');
  227. legend('X', 'Y', 'Z');
  228. figure('NumberTitle', 'off', 'Name', 'Linear Velocity');
  229. hold on;
  230. plot(vel_n(1,:), 'r');
  231. plot(vel_n(2,:), 'g');
  232. plot(vel_n(3,:), 'b');
  233. plot(vel_n_norm(:), 'm')
  234. index = find(zupt>0.0);
  235. plot(index,vel_n(3, index), 'ko');
  236. grid;
  237. xlabel('sample');
  238. ylabel('m/s');
  239. title('local velocity');
  240. legend('X', 'Y', 'Z');
  241. figure('NumberTitle', 'off', 'Name', 'Linear Position');
  242. hold on;
  243. plot(pos_n(1,:), 'r');
  244. plot(pos_n(2,:), 'g');
  245. plot(pos_n(3,:), 'b');
  246. grid;
  247. xlabel('sample');
  248. ylabel('m');
  249. title('local position');
  250. legend('X', 'Y', 'Z');
  251. % figure
  252. % plot3(pos_n(1,:), pos_n(2,:), pos_n(3,:));
  253. %
  254. % pose_diff = step_pos(1:2, 2:end) - step_pos(1:2, 1:end-1);
  255. % figure
  256. % plot( step_pos(2,:))
  257. %
  258. figure
  259. plot(gyr_norm(1,:),'r')
  260. hold on
  261. plot((acc_norm(1,:)-9.8)/5,'g');
  262. hold on
  263. plot(index,(gyr_norm(1,index)), 'ko');
  264. grid on
  265. figure
  266. plot3(pos_n(1,:), pos_n(2,:), pos_n(3, :),'r');grid on
  267. figure('NumberTitle', 'off', 'Name', 'IMU vel');
  268. hold on;
  269. plot(vel_local(1,:), 'r');
  270. plot(vel_local(2,:), 'g');
  271. %% Animation
  272. %% The animation code was stolen from xioTechnologies.
  273. %% https://github.com/xioTechnologies/Gait-Tracking-With-x-IMU
  274. % L = data_size;
  275. % SamplePlotFreq = 4;
  276. % Spin = 120;
  277. % SixDofAnimation(pos_result(1:3,:)', C_buff, ...
  278. % 'SamplePlotFreq', SamplePlotFreq, 'Trail', 'All',...
  279. % 'Position', [9 39 1280 768],...
  280. % 'View', [(100:(Spin/(L-1)):(100+Spin))', 10*ones(L, 1)],...
  281. % 'AxisLength', 0.1, 'ShowArrowHead', false, ...
  282. % 'Xlabel', 'X (m)', 'Ylabel', 'Y (m)', 'Zlabel', 'Z (m)',...
  283. % 'ShowLegend', false,...
  284. % 'CreateVideo', false);