test.m 10 KB

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