eth_playback.m 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. clc;
  2. clear;
  3. close('all');
  4. addpath('../../ref');
  5. addpath('../../ref/check_sum');
  6. FRAME_HEADER = 'RadarEye';
  7. FRAME_END = 'REND';
  8. MAX_LENGTH = 1000000;
  9. isGui = 1;
  10. if isGui
  11. hGui = struct();
  12. hGui.Figure = figure('WindowState', 'maximized');
  13. hGui.Axes_image = subplot(3, 4, [1,2,5,6]);
  14. hGui.hDataImage = imagesc(nan(320, 680, 3));
  15. axis image
  16. set(gca, 'XTick', [], 'YTick', []);
  17. imageT=title('CAMERA', 'FontSize', 16, 'FontWeight', 'bold');
  18. hGui.Axes_vr = subplot(3,4,[9,10]);
  19. hGui.fftv = imagesc(128, 128, nan(128, 128));
  20. grid minor;
  21. axis xy;
  22. box on;
  23. set(gca, 'FontSize', 12);
  24. xlabel('VelIdx', 'FontSize', 14);
  25. ylabel('RngIdx', 'FontSize', 14);
  26. title('fftv', 'FontSize', 16, 'FontWeight', 'bold');
  27. hGui.Axes_xy = subplot(3, 4, [3,7,11]);
  28. hGui.hDataPointZY = scatter(nan, nan, 'oG');
  29. axis xy;
  30. grid minor;
  31. xlim(10*[ -1, +1, ]);
  32. ylim(60*[ 0, 1, ]);
  33. set(gca, 'FontSize', 12);
  34. xlabel('Z (m)', 'FontSize', 14);
  35. ylabel('Y (m)', 'FontSize', 14);
  36. title('Point', 'FontSize', 16);
  37. hGui.Axes_xy = subplot(3, 4, [4,8,12]);
  38. hGui.hDataPointXYPZ = scatter(nan, nan, 'blue','oG');
  39. hold on;
  40. hGui.hDataPointXYNZ = scatter(nan, nan, 'red','oG');
  41. axis xy;
  42. grid minor;
  43. xlim(10*[ -1, +1, ]);
  44. ylim(60*[ 0, 1, ]);
  45. set(gca, 'FontSize', 12);
  46. xlabel('X (m)', 'FontSize', 14);
  47. ylabel('Y (m)', 'FontSize', 14);
  48. title('Point', 'FontSize', 16);
  49. end
  50. if isGui
  51. hGui_ref = struct();
  52. hGui_ref.Figure = figure('WindowState', 'maximized');
  53. hGui_ref.Axes_xy = subplot(3, 4, [2,6,10]);
  54. hGui_ref.hDataPointZY = scatter(nan, nan, 'oG');
  55. axis xy;
  56. grid minor;
  57. xlim(10*[ -1, +1, ]);
  58. ylim(60*[ 0, 1, ]);
  59. set(gca, 'FontSize', 12);
  60. xlabel('Z (m)', 'FontSize', 14);
  61. ylabel('Y (m)', 'FontSize', 14);
  62. title('Rotation Point', 'FontSize', 16);
  63. hGui_ref.Axes_xy = subplot(3, 4, [1,5,9]);
  64. hGui_ref.hDataPointZY_before = scatter(nan, nan, 'oG');
  65. axis xy;
  66. grid minor;
  67. xlim(10*[ -1, +1, ]);
  68. ylim(60*[ 0, 1, ]);
  69. set(gca, 'FontSize', 12);
  70. xlabel('Z (m)', 'FontSize', 14);
  71. ylabel('Y (m)', 'FontSize', 14);
  72. title('Raw Point', 'FontSize', 16);
  73. hGui_ref.Axes_xy = subplot(3, 4, [4,8,12]);
  74. hGui_ref.hDataPointXYPZ = scatter(nan, nan, 'blue','oG');
  75. hold on;
  76. hGui_ref.hDataPointXYNZ = scatter(nan, nan, 'red','oG');
  77. axis xy;
  78. grid minor;
  79. xlim(10*[ -1, +1, ]);
  80. ylim(60*[ 0, 1, ]);
  81. set(gca, 'FontSize', 12);
  82. xlabel('X (m)', 'FontSize', 14);
  83. ylabel('Y (m)', 'FontSize', 14);
  84. title('Rotation Point', 'FontSize', 16);
  85. hGui_ref.Axes_xy = subplot(3, 4, [3,7,11]);
  86. hGui_ref.hDataPointXYPZ_before = scatter(nan, nan, 'blue','oG');
  87. hold on;
  88. hGui_ref.hDataPointXYNZ_before = scatter(nan, nan, 'red','oG');
  89. axis xy;
  90. grid minor;
  91. xlim(10*[ -1, +1, ]);
  92. ylim(60*[ 0, 1, ]);
  93. set(gca, 'FontSize', 12);
  94. xlabel('X (m)', 'FontSize', 14);
  95. ylabel('Y (m)', 'FontSize', 14);
  96. title('Raw Point', 'FontSize', 16);
  97. end
  98. if isGui
  99. hGui_filter = struct();
  100. hGui_filter.Figure = figure('WindowState', 'maximized');
  101. hGui_filter.Axes_raw = subplot(1, 2, 1);
  102. hGui_filter.raw_hDataPointXYPZ = scatter(nan, nan, 'blue','oG');
  103. hold on;
  104. hGui_filter.raw_hDataPointXYNZ = scatter(nan, nan, 'red','oG');
  105. axis xy;
  106. grid minor;
  107. xlim(10*[ -1, +1, ]);
  108. ylim(60*[ 0, 1, ]);
  109. set(gca, 'FontSize', 12);
  110. xlabel('X (m)', 'FontSize', 14);
  111. ylabel('Y (m)', 'FontSize', 14);
  112. title('Raw Point', 'FontSize', 16);
  113. hGui_filter.Axes_filter = subplot(1, 2, 2);
  114. hGui_filter.filter_hDataPointXYPZ = scatter(nan, nan, 'blue','oG');
  115. hold on;
  116. hGui_filter.filter_hDataPointXYNZ = scatter(nan, nan, 'red','oG');
  117. hold on;
  118. hGui_filter.cluster_hDataPointXYNZ = scatter(nan, nan, 'green','filled');
  119. axis xy;
  120. grid minor;
  121. xlim(10*[ -1, +1, ]);
  122. ylim(60*[ 0, 1, ]);
  123. set(gca, 'FontSize', 12);
  124. xlabel('X (m)', 'FontSize', 14);
  125. ylabel('Y (m)', 'FontSize', 14);
  126. title('Filter Point', 'FontSize', 16);
  127. end
  128. % ====== 初始化图像 ======
  129. global t0 time_hist dist_hist dist_hist2 hPlot hPlot2
  130. t0 = tic;
  131. time_hist = [];
  132. dist_hist = [];
  133. dist_hist2 = [];
  134. figure; hold on; grid on;
  135. hPlot = plot(nan, nan, 'r.-', 'LineWidth', 1.5);
  136. hPlot2 = plot(nan, nan, 'b.-', 'LineWidth', 1.5);
  137. xlabel('Time (s)');
  138. ylabel('Nearest Cluster Distance (m)');
  139. title('Obstacle Distance vs Time');
  140. targetFrame = struct();
  141. ori_point = struct();
  142. cfar_point = struct();
  143. tempdata=[];
  144. % axis image
  145. % set(gca, 'XTick', [], 'YTick', []);
  146. % title('CAMERA', 'FontSize', 16, 'FontWeight', 'bold');
  147. path = 'C:\Users\VK\Desktop\nationstech\采样分析专用\采样数据\250929\20250929_163854_340';
  148. listBin = dir(fullfile(path, '*.bin'));
  149. numBin = length(listBin);
  150. fprintf(" numBin %d \r\n",numBin);
  151. pc_hist = cell(10,1); % pc_hist{1}: 上一帧(t-1),pc_hist{2}: 上上帧(t-2)
  152. q_hist = cell(10,1);
  153. pos_hist = cell(10,1);
  154. dataBuffer = [];
  155. idxBin = 1;
  156. %%%第一版聚类参数
  157. cluster_params_1 = [ ...
  158. struct('y_min',0,'y_max',15,'dist_thresh',0.5,'min_points',4); ...
  159. struct('y_min',16,'y_max',25,'dist_thresh',0.8,'min_points',3); ...
  160. struct('y_min',25,'y_max',50,'dist_thresh',1.5,'min_points',3); ...
  161. ];
  162. %%%第二版聚类参数
  163. cluster_params = [ ...
  164. struct('y_min',0,'y_max',10,'dist_thresh',0.5,'min_points',4); ...
  165. struct('y_min',10.1,'y_max',20,'dist_thresh',1,'min_points',3); ...
  166. struct('y_min',20.1,'y_max',35,'dist_thresh',1.8,'min_points',3); ...
  167. ];
  168. % 原始定义追踪参数
  169. params_1.match_thresh = 2; % 匹配阈值 (米)
  170. params_1.inc_val = 1; % 命中 +1
  171. params_1.dec_val = 1; % 丢失 -1
  172. params_1.min_conf = 3; % 置信度 >=3 才显示
  173. % 第二版定义追踪参数
  174. params_2.match_thresh = 1.3; % 匹配阈值 (米)
  175. params_2.inc_val = 1; % 命中 +1
  176. params_2.dec_val = 1; % 丢失 -1
  177. params_2.min_conf = 2; % 置信度 >=2 才显示
  178. all_points = [];
  179. points_body_cur =[];
  180. while(idxBin <= numBin)
  181. % 读取被测雷达数据文件
  182. pathBin = fullfile(path, listBin(idxBin).name);
  183. [ ~, currentTimestr, ~, ] = fileparts(pathBin);
  184. currentTime = datetime(currentTimestr, ...
  185. 'InputFormat', 'uuuuMMdd_HHmmss_SSSSSS');
  186. % fileBin = fopen(pathBin, 'rb');
  187. % dataBin = uint8(fread(fileBin, inf, 'uint8')).';
  188. % fclose(fileBin);
  189. [pose, radarData] = read_bin_with_pose(pathBin);
  190. dataBuffer = cat(2, dataBuffer, radarData);
  191. quat = pose(1:4);
  192. pos = pose(5:7);
  193. euler = quat2euler_enu(quat); % 转换为欧拉角
  194. [ listFrame, dataBuffer] = extract_frame(dataBuffer);
  195. numFrame = length(listFrame);
  196. % disp(currentTime);
  197. idxFrame = 1;
  198. while (idxFrame <= numFrame)
  199. frame = listFrame(idxFrame);
  200. % fprintf("Type %d\r\n",frame.Type);
  201. paramAlg.ResRng = single(0.6958);
  202. paramAlg.ResVel = single(0.3519);
  203. switch frame.Type
  204. case 7 %点云数据
  205. framePayload = struct();
  206. framePayload.Idx = int32(typecast(frame.Raw(4 + (1:4)), 'uint32'));
  207. framePayload.Tick = int32(typecast(frame.Raw(8 + (1:4)), 'uint32'));
  208. framePayload.NumPoint = uint32(typecast(frame.Raw(12 + (1:4)), 'uint32'));
  209. % ori_point.num = framePayload.NumPoint;
  210. ori_point.r=[];
  211. ori_point.v=[];
  212. ori_point.Azi=[];
  213. ori_point.Ele=[];
  214. ori_point.pow=[];
  215. ori_point.x=[];
  216. ori_point.y=[];
  217. ori_point.z=[];
  218. numBytePerPoint = int32(100);
  219. idx = 0;
  220. % fprintf("Type %d, idx %d , tick %d , num %d\r\n" ...
  221. % ,frame.Type,framePayload.Idx,framePayload.Tick,framePayload.NumPoint);
  222. for idxPoint = 1:framePayload.NumPoint
  223. rIdx = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(1:4)), 'int32');
  224. vIdx = (typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(5:8)), 'int32') - 65);
  225. pow = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(97:100)), 'int32');
  226. azi = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(89:92)), 'single');
  227. ele = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(93:96)), 'single');
  228. ele = -(ele + 0.18);
  229. if ele > -15/180*pi && ele < 15/180*pi && powerFilter(rIdx,pow) == 1 %1、去除俯仰角大于20度的点,2、能量过滤
  230. idx = idx + 1;
  231. ori_point.r(idx) = single(rIdx)*paramAlg.ResRng;
  232. ori_point.v(idx) = single(vIdx)*paramAlg.ResVel;
  233. ori_point.Azi(idx) = (-azi); %X值左右反了,水平角取反
  234. ori_point.Ele(idx) = ele+12/180*pi; %补偿雷达安装角度值
  235. % ori_point.Ele(idx) = ele;
  236. ori_point.pow(idx) = pow;
  237. ori_point.x(idx) = ori_point.r((idx))*cos(ori_point.Ele((idx)))*sin(ori_point.Azi((idx)));
  238. ori_point.y(idx) = ori_point.r((idx))*cos(ori_point.Ele((idx)))*cos(ori_point.Azi((idx)));
  239. ori_point.z(idx) = ori_point.r((idx))*sin(ori_point.Ele((idx)));
  240. tempdata = cat(1,tempdata,[single(idxBin),single(idxFrame),single(rIdx),ori_point.r(idx),ori_point.v(idx),single(ori_point.pow(idx)),ori_point.x(idx),ori_point.y(idx),ori_point.z(idx),ori_point.Azi(idx),ori_point.Ele(idx),single(1)]);
  241. % else %记录被过滤掉的点云数据
  242. % r = single(rIdx)*paramAlg.ResRng;
  243. % v = single(vIdx)*paramAlg.ResVel;
  244. % azi = -azi;
  245. % ele = ele+12/180*pi;
  246. % x = r*cos(ele)*sin(azi);
  247. % y = r*cos(ele)*cos(azi);
  248. % z = r*sin(ele);
  249. % tempdata = cat(1,tempdata,[single(idxBin),single(idxFrame),single(rIdx),r,v,single(pow),x,y,z,azi,ele,single(0)]);
  250. end
  251. end
  252. ori_point.num = idx;
  253. %%%%%%%%%%%%%%%%%%%%%%%%% 点云旋转 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  254. if ori_point.num > 0
  255. % 构造点云矩阵
  256. points_body_cur = [ori_point.x(:), ori_point.y(:), ori_point.z(:)]; % N×3
  257. quat_cur = quat(:)'; % pose(1:4) = [w x y z]
  258. pos_cur = pos(:)';
  259. %%%%%% 最新帧点云进行姿态转换
  260. R_body2enu = quat2rotm(quat_cur); % 机体系 -> ENU系
  261. points_enu_raw = (R_body2enu * points_body_cur')';
  262. q_yaw = Quaternion_Enu_ByEuler(-euler(3), 0, 0); % 只保留 yaw
  263. R_yaw = quat2rotm(q_yaw); % Yaw 补偿旋转
  264. % 应用 yaw 补偿
  265. points_enu = (R_yaw * points_enu_raw')';
  266. cur_point_enu.x = points_enu(:,1);
  267. cur_point_enu.y = points_enu(:,2);
  268. cur_point_enu.z = points_enu(:,3);
  269. cur_point_enu.num = size(points_enu,1);
  270. % filter_condition_cur = (points_enu(:,3) > -1.8) & (points_enu(:,1) < 10.0) ...
  271. % & (points_enu(:,1) > -10.0) & (points_enu(:,2) < 30.0);
  272. % filtered_points = points_enu(filter_condition_cur, :);
  273. % all_points = points_enu;
  274. if euler(2) < -0.16
  275. ratio_posz = sum(points_enu(:,3) < 0) / cur_point_enu.num;
  276. if ratio_posz >= 0.6
  277. % 过滤掉当前点云
  278. % disp('当前帧点云被丢弃: z>0 占比过高');
  279. % cur_point_enu.num = 0;
  280. % cur_point_enu.x = [];
  281. % cur_point_enu.y = [];
  282. % cur_point_enu.z = [];
  283. points_body_cur = 0;
  284. all_points = [];
  285. else
  286. all_points = points_enu;
  287. end
  288. else
  289. all_points = points_enu;
  290. end
  291. %%%%%% 历史帧点云进行旋转映射
  292. %%% 第一帧历史点云
  293. if ~isempty(pc_hist{1})
  294. quat_hist1 = q_hist{1};
  295. R_body2enu_hist1 = quat2rotm(quat_hist1); % 机体系 -> ENU系
  296. points_hist1 = (R_body2enu_hist1 * pc_hist{1}')';
  297. points_enu_hist1 = points_hist1 + pos_hist{1} - pos_cur;
  298. points_rfu_hist1 = (R_yaw * points_enu_hist1')';
  299. all_points = [all_points; points_rfu_hist1];
  300. end
  301. %%% 第二帧历史点云
  302. if ~isempty(pc_hist{2})
  303. quat_hist2 = q_hist{2};
  304. R_body2enu_hist2 = quat2rotm(quat_hist2); % 机体系 -> ENU系
  305. points_hist2 = (R_body2enu_hist2 * pc_hist{2}')';
  306. points_enu_hist2 = points_hist2 + pos_hist{2} - pos_cur;
  307. points_rfu_hist2 = (R_yaw * points_enu_hist2')';
  308. all_points = [all_points; points_rfu_hist2];
  309. end
  310. %%% 第三帧历史点云
  311. if ~isempty(pc_hist{3})
  312. quat_hist3 = q_hist{3};
  313. R_body2enu_hist3 = quat2rotm(quat_hist3); % 机体系 -> ENU系
  314. points_hist3 = (R_body2enu_hist3 * pc_hist{3}')';
  315. points_enu_hist3 = points_hist3 + pos_hist{3} - pos_cur;
  316. points_rfu_hist3 = (R_yaw * points_enu_hist3')';
  317. hist3_point.num = size(points_rfu_hist3,1);
  318. if(hist3_point.num > 0)
  319. all_points = [all_points; points_rfu_hist3];
  320. end
  321. end
  322. %%% 第四帧历史点云
  323. if ~isempty(pc_hist{4})
  324. quat_hist4 = q_hist{4};
  325. R_body2enu_hist4 = quat2rotm(quat_hist4); % 机体系 -> ENU系
  326. points_hist4 = (R_body2enu_hist4 * pc_hist{4}')';
  327. points_enu_hist4 = points_hist4 + pos_hist{4} - pos_cur;
  328. points_rfu_hist4 = (R_yaw * points_enu_hist4')';
  329. hist4_point.num = size(points_rfu_hist4,1);
  330. if(hist4_point.num > 0)
  331. all_points = [all_points; points_rfu_hist4];
  332. end
  333. end
  334. %%% 第五帧历史点云
  335. if ~isempty(pc_hist{5})
  336. quat_hist5 = q_hist{5};
  337. R_body2enu_hist5 = quat2rotm(quat_hist5); % 机体系 -> ENU系
  338. points_hist5 = (R_body2enu_hist5 * pc_hist{5}')';
  339. points_enu_hist5 = points_hist5 + pos_hist{5} - pos_cur;
  340. points_rfu_hist5 = (R_yaw * points_enu_hist5')';
  341. hist5_point.num = size(points_rfu_hist5,1);
  342. if(hist5_point.num > 0)
  343. all_points = [all_points; points_rfu_hist5];
  344. end
  345. end
  346. %%% 第六帧历史点云
  347. if ~isempty(pc_hist{6})
  348. quat_hist6 = q_hist{6};
  349. R_body2enu_hist6 = quat2rotm(quat_hist6); % 机体系 -> ENU系
  350. points_hist6 = (R_body2enu_hist6 * pc_hist{6}')';
  351. points_enu_hist6 = points_hist6 + pos_hist{6} - pos_cur;
  352. points_rfu_hist6 = (R_yaw * points_enu_hist6')';
  353. hist6_point.num = size(points_rfu_hist6,1);
  354. if(hist6_point.num > 0)
  355. all_points = [all_points; points_rfu_hist6];
  356. end
  357. end
  358. %%% 第七帧历史点云
  359. if ~isempty(pc_hist{7})
  360. quat_hist7 = q_hist{7};
  361. R_body2enu_hist7 = quat2rotm(quat_hist7); % 机体系 -> ENU系
  362. points_hist7 = (R_body2enu_hist7 * pc_hist{7}')';
  363. points_enu_hist7 = points_hist7 + pos_hist{7} - pos_cur;
  364. points_rfu_hist7 = (R_yaw * points_enu_hist7')';
  365. hist7_point.num = size(points_rfu_hist7,1);
  366. if(hist7_point.num > 0)
  367. all_points = [all_points; points_rfu_hist7];
  368. end
  369. end
  370. %%% 第八帧历史点云
  371. if ~isempty(pc_hist{8})
  372. quat_hist8 = q_hist{8};
  373. R_body2enu_hist8 = quat2rotm(quat_hist8); % 机体系 -> ENU系
  374. points_hist8 = (R_body2enu_hist8 * pc_hist{8}')';
  375. points_enu_hist8 = points_hist8 + pos_hist{8} - pos_cur;
  376. points_rfu_hist8 = (R_yaw * points_enu_hist8')';
  377. hist8_point.num = size(points_rfu_hist8,1);
  378. if(hist8_point.num > 0)
  379. all_points = [all_points; points_rfu_hist8];
  380. end
  381. end
  382. %%% 第九帧历史点云
  383. if ~isempty(pc_hist{9})
  384. quat_hist9 = q_hist{9};
  385. R_body2enu_hist9 = quat2rotm(quat_hist9); % 机体系 -> ENU系
  386. points_hist9 = (R_body2enu_hist9 * pc_hist{9}')';
  387. points_enu_hist9 = points_hist9 + pos_hist{9} - pos_cur;
  388. points_rfu_hist9 = (R_yaw * points_enu_hist9')';
  389. hist9_point.num = size(points_rfu_hist9,1);
  390. if(hist9_point.num > 0)
  391. all_points = [all_points; points_rfu_hist9];
  392. end
  393. end
  394. % %%% 第十帧历史点云
  395. % if ~isempty(pc_hist{10})
  396. % quat_hist10 = q_hist{10};
  397. % R_body2enu_hist10 = quat2rotm(quat_hist10); % 机体系 -> ENU系
  398. % points_hist10 = (R_body2enu_hist10 * pc_hist{10}')';
  399. % points_enu_hist10 = points_hist10 + pos_hist{10} - pos_cur;
  400. % points_rfu_hist10 = (R_yaw * points_enu_hist10')';
  401. % hist10_point.num = size(points_rfu_hist10,1);
  402. % if(hist10_point.num > 0)
  403. % all_points = [all_points; points_rfu_hist10];
  404. % end
  405. % end
  406. if ~isempty(all_points)
  407. filter_condition_cur = true(size(all_points,1),1);
  408. for i = 1:size(all_points, 1)
  409. x_value = all_points(i, 1); % 获取当前点的 x 值
  410. y_value = all_points(i, 2); % 获取当前点的 y 值
  411. z_value = all_points(i, 3); % 获取当前点的 z 值
  412. if y_value >= 35 || y_value <= 0
  413. filter_condition_cur(i) = false;
  414. elseif x_value >=10 || x_value <= -10
  415. filter_condition_cur(i) = false;
  416. elseif y_value < 5
  417. % 如果 y < 5,则 z > -2
  418. if x_value >= 4 || x_value <= -4
  419. filter_condition_cur(i) = false;
  420. end
  421. if z_value <= -1.0
  422. filter_condition_cur(i) = false;
  423. end
  424. elseif y_value >= 5 && y_value < 10
  425. % 如果 5 <= y < 10,则 z > -1.5
  426. if x_value >= 7 || x_value <= -7
  427. filter_condition_cur(i) = false;
  428. end
  429. if z_value <= -1.2
  430. filter_condition_cur(i) = false;
  431. end
  432. elseif y_value >= 10
  433. % 如果 y >= 20,则 z > -1
  434. if z_value <= -1.0
  435. filter_condition_cur(i) = false;
  436. end
  437. end
  438. end
  439. filtered_points = all_points(filter_condition_cur, :);
  440. filtered_point_rfu.x = filtered_points(:, 1);
  441. filtered_point_rfu.y = filtered_points(:, 2);
  442. filtered_point_rfu.z = filtered_points(:, 3);
  443. filtered_point_rfu.num = size(filtered_points, 1);
  444. %%% 聚类
  445. clusters_all_1 = multiRangeClustering(filtered_point_rfu, cluster_params_1);
  446. clusters_all = multiRangeClustering(filtered_point_rfu, cluster_params);
  447. [cx, cy] = updateObstacleTracking(clusters_all, params_2);
  448. [cx_1, cy_1] = updateObstacleTracking(clusters_all_1, params_1);
  449. % 更新 scatter 数据
  450. set(hGui_filter.cluster_hDataPointXYNZ, 'XData', cx, 'YData', cy);
  451. % 找到 y 最小的聚类点
  452. if ~isempty(cy)
  453. nearestDist = min(cy);
  454. else
  455. nearestDist = 0;
  456. end
  457. if ~isempty(cy_1)
  458. nearestDist2 = min(cy_1);
  459. else
  460. nearestDist2 = 0;
  461. end
  462. % 当前时间
  463. t = toc(t0);
  464. % 追加数据
  465. global time_hist dist_hist dist_hist2 hPlot hPlot2
  466. time_hist(end+1) = t;
  467. dist_hist(end+1) = nearestDist;
  468. dist_hist2(end+1) = nearestDist2;
  469. % 更新曲线
  470. set(hPlot, 'XData', time_hist, 'YData', dist_hist);
  471. set(hPlot2, 'XData', time_hist, 'YData', dist_hist2);
  472. drawnow limitrate;
  473. if(filtered_point_rfu.num > 0)
  474. posIdx_filter = filtered_point_rfu.z>=0;
  475. negIdx_filter = filtered_point_rfu.z<0;
  476. if sum(posIdx_filter) > 0
  477. posZX_filter = filtered_point_rfu.x(posIdx_filter);
  478. posZY_filter = filtered_point_rfu.y(posIdx_filter);
  479. posSize = 10;
  480. else
  481. posZX_filter = nan;
  482. posZY_filter = nan;
  483. posSize = nan;
  484. end
  485. if sum(negIdx_filter)
  486. negZX_filter = filtered_point_rfu.x(negIdx_filter);
  487. negZY_filter = filtered_point_rfu.y(negIdx_filter);
  488. negSize = 10;
  489. else
  490. negZX_filter = nan;
  491. negZY_filter = nan;
  492. negSize = nan;
  493. end
  494. %显示Z值大于等于0的点
  495. hGui_filter.filter_hDataPointXYPZ.XData = posZX_filter;
  496. hGui_filter.filter_hDataPointXYPZ.YData = posZY_filter;
  497. hGui_filter.filter_hDataPointXYPZ.SizeData = posSize;
  498. %显示Z值小于0的点
  499. hGui_filter.filter_hDataPointXYNZ.XData = negZX_filter;
  500. hGui_filter.filter_hDataPointXYNZ.YData = negZY_filter;
  501. hGui_filter.filter_hDataPointXYNZ.SizeData = negSize;
  502. else
  503. hGui_filter.filter_hDataPointXYPZ.XData = nan;
  504. hGui_filter.filter_hDataPointXYPZ.YData = nan;
  505. hGui_filter.filter_hDataPointXYPZ.SizeData = nan;
  506. hGui_filter.filter_hDataPointXYNZ.XData = nan;
  507. hGui_filter.filter_hDataPointXYNZ.YData = nan;
  508. hGui_filter.filter_hDataPointXYNZ.SizeData = nan;
  509. end
  510. end
  511. %%%%%% 更新历史信息
  512. % pc_hist{10} = pc_hist{9};
  513. pc_hist{9} = pc_hist{8};
  514. pc_hist{8} = pc_hist{7};
  515. pc_hist{7} = pc_hist{6};
  516. pc_hist{6} = pc_hist{5};
  517. pc_hist{5} = pc_hist{4};
  518. pc_hist{4} = pc_hist{3};
  519. pc_hist{3} = pc_hist{2};
  520. pc_hist{2} = pc_hist{1}; % t-2 <- t-1
  521. pc_hist{1} = points_body_cur; % t-1 <- 当前帧body坐标系点云
  522. % q_hist{10} = q_hist{9};
  523. q_hist{9} = q_hist{8};
  524. q_hist{8} = q_hist{7};
  525. q_hist{7} = q_hist{6};
  526. q_hist{6} = q_hist{5};
  527. q_hist{5} = q_hist{4};
  528. q_hist{4} = q_hist{3};
  529. q_hist{3} = q_hist{2};
  530. q_hist{2} = q_hist{1}; % t-2 <- t-1
  531. q_hist{1} = quat_cur; % t-1 <- 当前帧 Q
  532. % pos_hist{10} = pos_hist{9};
  533. pos_hist{9} = pos_hist{8};
  534. pos_hist{8} = pos_hist{7};
  535. pos_hist{7} = pos_hist{6};
  536. pos_hist{6} = pos_hist{5};
  537. pos_hist{5} = pos_hist{4};
  538. pos_hist{4} = pos_hist{3};
  539. pos_hist{3} = pos_hist{2};
  540. pos_hist{2} = pos_hist{1}; % t-2 <- t-1
  541. pos_hist{1} = pos_cur; % t-1 <- 当前帧 pos
  542. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  543. % 更新旋转后点云信息
  544. if isGui
  545. if(cur_point_enu.num > 0)
  546. posIdx = cur_point_enu.z>=0;
  547. negIdx = cur_point_enu.z<0;
  548. if sum(posIdx) > 0
  549. posZX = cur_point_enu.x(posIdx);
  550. posZY = cur_point_enu.y(posIdx);
  551. posSize = 10;
  552. else
  553. posZX = nan;
  554. posZY = nan;
  555. posSize = nan;
  556. end
  557. if sum(negIdx)
  558. negZX = cur_point_enu.x(negIdx);
  559. negZY = cur_point_enu.y(negIdx);
  560. negSize = 10;
  561. else
  562. negZX = nan;
  563. negZY = nan;
  564. negSize = nan;
  565. end
  566. %显示Z值大于等于0的点
  567. hGui_ref.hDataPointXYPZ.XData = posZX;
  568. hGui_ref.hDataPointXYPZ.YData = posZY;
  569. hGui_ref.hDataPointXYPZ.SizeData = posSize;
  570. %显示Z值小于0的点
  571. hGui_ref.hDataPointXYNZ.XData = negZX;
  572. hGui_ref.hDataPointXYNZ.YData = negZY;
  573. hGui_ref.hDataPointXYNZ.SizeData = negSize;
  574. hGui_ref.hDataPointZY.XData = cur_point_enu.z;
  575. hGui_ref.hDataPointZY.YData = cur_point_enu.y;
  576. hGui_ref.hDataPointZY.SizeData = 10;%1e-4*double([ ori_point.pow, ] + 1000);
  577. else
  578. hGui_ref.hDataPointXYPZ.XData = nan;
  579. hGui_ref.hDataPointXYPZ.YData = nan;
  580. hGui_ref.hDataPointXYPZ.SizeData = nan;
  581. hGui_ref.hDataPointXYNZ.XData = nan;
  582. hGui_ref.hDataPointXYNZ.YData = nan;
  583. hGui_ref.hDataPointXYNZ.SizeData = nan;
  584. hGui_ref.hDataPointZY.XData = nan;
  585. hGui_ref.hDataPointZY.YData = nan;
  586. hGui_ref.hDataPointZY.SizeData = nan;
  587. end
  588. end
  589. end
  590. if isGui
  591. if(ori_point.num > 0)
  592. posIdx = ori_point.z>=0;
  593. negIdx = ori_point.z<0;
  594. if sum(posIdx) > 0
  595. posZX = ori_point.x(posIdx);
  596. posZY = ori_point.y(posIdx);
  597. posSize = 10;
  598. else
  599. posZX = nan;
  600. posZY = nan;
  601. posSize = nan;
  602. end
  603. if sum(negIdx)
  604. negZX = ori_point.x(negIdx);
  605. negZY = ori_point.y(negIdx);
  606. negSize = 10;
  607. else
  608. negZX = nan;
  609. negZY = nan;
  610. negSize = nan;
  611. end
  612. %显示Z值大于等于0的点
  613. hGui.hDataPointXYPZ.XData = posZX;
  614. hGui.hDataPointXYPZ.YData = posZY;
  615. hGui.hDataPointXYPZ.SizeData = posSize;
  616. hGui_ref.hDataPointXYPZ_before.XData = posZX;
  617. hGui_ref.hDataPointXYPZ_before.YData = posZY;
  618. hGui_ref.hDataPointXYPZ_before.SizeData = posSize;
  619. %显示Z值小于0的点
  620. hGui.hDataPointXYNZ.XData = negZX;
  621. hGui.hDataPointXYNZ.YData = negZY;
  622. hGui.hDataPointXYNZ.SizeData = negSize;
  623. hGui_ref.hDataPointXYNZ_before.XData = negZX;
  624. hGui_ref.hDataPointXYNZ_before.YData = negZY;
  625. hGui_ref.hDataPointXYNZ_before.SizeData = negSize;
  626. hGui.hDataPointZY.XData = ori_point.z;
  627. hGui.hDataPointZY.YData = ori_point.y;
  628. hGui.hDataPointZY.SizeData = 10;%1e-4*double([ ori_point.pow, ] + 1000);
  629. hGui_ref.hDataPointZY_before.XData = ori_point.z;
  630. hGui_ref.hDataPointZY_before.YData = ori_point.y;
  631. hGui_ref.hDataPointZY_before.SizeData = 10;%1e-4*double([ ori_point.pow, ] + 1000);
  632. else
  633. hGui.hDataPointXYPZ.XData = nan;
  634. hGui.hDataPointXYPZ.YData = nan;
  635. hGui.hDataPointXYPZ.SizeData = nan;
  636. hGui_ref.hDataPointXYPZ_before.XData = nan;
  637. hGui_ref.hDataPointXYPZ_before.YData = nan;
  638. hGui_ref.hDataPointXYPZ_before.SizeData = nan;
  639. hGui.hDataPointXYNZ.XData = nan;
  640. hGui.hDataPointXYNZ.YData = nan;
  641. hGui.hDataPointXYNZ.SizeData = nan;
  642. hGui_ref.hDataPointXYNZ_before.XData = nan;
  643. hGui_ref.hDataPointXYNZ_before.YData = nan;
  644. hGui_ref.hDataPointXYNZ_before.SizeData = nan;
  645. hGui.hDataPointZY.XData = nan;
  646. hGui.hDataPointZY.YData = nan;
  647. hGui.hDataPointZY.SizeData = nan;
  648. hGui_ref.hDataPointZY_before.XData = nan;
  649. hGui_ref.hDataPointZY_before.YData = nan;
  650. hGui_ref.hDataPointZY_before.SizeData = nan;
  651. end
  652. end
  653. case 6
  654. framePayload = struct();
  655. framePayload.Idx = int32(typecast(frame.Raw(4 + (1:4)), 'uint32'));
  656. framePayload.Tick = int32(typecast(frame.Raw(8 + (1:4)), 'uint32'));
  657. framePayload.NumPoint = uint32(typecast(frame.Raw(12 + (1:4)), 'uint32'));
  658. cfar_point.num = framePayload.NumPoint;
  659. cfar_point.r = [];
  660. cfar_point.v = [];
  661. cfar_point.pow = [];
  662. numBytePerPoint = int32(100);
  663. for idxPoint = 1:framePayload.NumPoint
  664. cfar_point.r(idxPoint) = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(1:4)), 'int32');
  665. cfar_point.v(idxPoint) = typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(5:8)), 'int32') - int32(64);
  666. cfar_point.pow(idxPoint) = single(typecast(frame.Raw(int32(16) + (int32(idxPoint) - 1)*numBytePerPoint + int32(97:100)), 'int32'));
  667. end
  668. case 5
  669. DataBuffer = frame.Raw(13:frame.Length - 8);
  670. total_len = 128 * 8 * 128;
  671. nt2 = uint8(reshape(DataBuffer,[4,total_len/4]));
  672. for i=1:total_len/4
  673. nt3(i) = typecast(fliplr(squeeze(nt2(:,i))),'single');
  674. end
  675. float_single = complex(nt3(1:2:end),nt3(2:2:end));
  676. ntt4 = reshape(float_single,[128, 128]);
  677. for k=1:128
  678. fft2d_abs(:,k)= abs(ntt4(:,k));
  679. end
  680. if isGui
  681. hGui.fftv.CData = fft2d_abs;
  682. end
  683. end
  684. idxFrame = idxFrame + 1;
  685. %显示图片
  686. oriImage = imread(fullfile(path, ...
  687. sprintf("%s.jpg", currentTimestr)));
  688. % rotatedImage = imrotate(oriImage, 180, 'bilinear', 'crop');
  689. % hGui.hDataImage.CData = rotatedImage;
  690. hGui.hDataImage.CData = oriImage;
  691. drawnow;
  692. end
  693. idxBin = idxBin + 1;
  694. pause(0.05);
  695. end
  696. function [pose, radarData] = read_bin_with_pose(filename)
  697. % 读取整个bin文件
  698. fid = fopen(filename, 'rb');
  699. rawData = uint8(fread(fid, inf,'*uint8')).';
  700. fclose(fid);
  701. % 文件至少要有 32 字节才能包含位姿
  702. if numel(rawData) < 32
  703. error('文件太短,没有位姿数据');
  704. end
  705. % 末尾 32 字节是 float32 位姿
  706. poseBytes = rawData(end-31:end);
  707. pose = typecast(poseBytes, 'single'); % [q0,q1,q2,q3,x,y,z,valid]
  708. % 剩余部分是雷达数据
  709. radarData = rawData(1:end-32);
  710. end
  711. function [listFrame, dataBuffer] = extract_frame(dataBuffer)
  712. FRAME_HEADER = 'RadarEye';
  713. FRAME_END = 'REND';
  714. listFrame = [];
  715. while true
  716. lenBuffer = length(dataBuffer);
  717. % 必须至少有 16 字节(8B帧头 + 4B长度 + 4B类型)
  718. if lenBuffer < 16
  719. return; % 等待更多数据
  720. end
  721. % 找帧头
  722. if ~strcmp(char(dataBuffer(1:8)), FRAME_HEADER)
  723. % 没找到帧头 → 丢掉一个字节继续找
  724. dataBuffer = dataBuffer(2:end);
  725. continue;
  726. end
  727. % 读取 Length
  728. frame = struct();
  729. frame.Header = FRAME_HEADER;
  730. frame.Length = double(typecast(dataBuffer(9:12), 'uint32'));
  731. % 合理性检查(防止 Length 乱值)
  732. if frame.Length <= 0 || frame.Length > 200000
  733. % 丢掉帧头继续
  734. dataBuffer = dataBuffer(9:end);
  735. continue;
  736. end
  737. % 数据不足整个帧,等下一轮再处理
  738. if lenBuffer < 12 + frame.Length
  739. return;
  740. end
  741. % 读取 Type
  742. frame.Type = typecast(dataBuffer(13:16), 'uint32');
  743. % 根据 Type 决定校验数据范围
  744. if frame.Type == 6 || frame.Type == 7
  745. frame.Check = uint32(0);
  746. dataToCheck = typecast(dataBuffer(13:(12 + frame.Length - 4)), 'uint32');
  747. elseif frame.Type == 5
  748. frame.Length = frame.Length + 12;
  749. % 再次检查长度
  750. if frame.Length > 200000 || lenBuffer < 12 + frame.Length
  751. dataBuffer = dataBuffer(9:end);
  752. continue;
  753. end
  754. frame.Check = uint32(0);
  755. dataToCheck = typecast(dataBuffer(13:(12 + frame.Length - 4)), 'uint32');
  756. else
  757. % 未知类型,丢掉帧头继续
  758. dataBuffer = dataBuffer(9:end);
  759. continue;
  760. end
  761. % 计算校验和
  762. for i = 1:length(dataToCheck)
  763. frame.Check = bitxor(frame.Check, dataToCheck(i));
  764. end
  765. % 检查帧尾
  766. frame.End = char(dataBuffer((12 + frame.Length - 3):(12 + frame.Length)));
  767. if frame.Check ~= 0 || ~strcmp(frame.End, FRAME_END)
  768. % 校验失败 → 丢掉一个字节继续
  769. dataBuffer = dataBuffer(2:end);
  770. continue;
  771. end
  772. % 保存原始数据
  773. frame.Raw = dataBuffer(13:(12 + frame.Length - 8));
  774. frame.bin = dataBuffer(1:(12 + frame.Length));
  775. % 存到结果
  776. listFrame = [listFrame; frame];
  777. % 移除已解析的帧
  778. dataBuffer = dataBuffer((12 + frame.Length + 1):end);
  779. end
  780. end
  781. %根据不同距离不同能量过滤数据
  782. function isFlag=powerFilter(range,pow)
  783. isFlag = 0;
  784. if range < 10 && pow > 1200000
  785. isFlag = 1;
  786. elseif range >= 10 && range < 20 && pow > 800000
  787. isFlag = 1;
  788. elseif range >= 20 && range < 30 && pow > 500000
  789. isFlag = 1;
  790. elseif range >= 30 && range < 40 && pow > 400000
  791. isFlag = 1;
  792. elseif range >= 40 && range < 50 && pow > 300000
  793. isFlag = 1;
  794. elseif range >= 50 && range < 60 && pow > 200000
  795. isFlag = 1;
  796. elseif range >= 60 && range < 70 && pow > 200000
  797. isFlag = 1;
  798. elseif range >= 70 && range < 80 && pow > 200000
  799. isFlag = 1;
  800. elseif range >= 80 && pow > 200000
  801. isFlag = 1;
  802. end
  803. end
  804. function euler = quat2euler_enu(Q)
  805. % Q = [Q0 Q1 Q2 Q3] (标量在前, 与C代码一致)
  806. q0 = Q(1); q1 = Q(2); q2 = Q(3); q3 = Q(4);
  807. % 方向余弦矩阵 DCM
  808. dcm = zeros(3,3);
  809. q0s = q0*q0; q1s = q1*q1; q2s = q2*q2; q3s = q3*q3;
  810. dcm(1,1) = q0s + q1s - q2s - q3s;
  811. dcm(1,2) = 2*(q1*q2 - q0*q3);
  812. dcm(1,3) = 2*(q0*q2 + q1*q3);
  813. dcm(2,1) = 2*(q1*q2 + q0*q3);
  814. dcm(2,2) = q0s - q1s + q2s - q3s;
  815. dcm(2,3) = 2*(q2*q3 - q0*q1);
  816. dcm(3,1) = 2*(q1*q3 - q0*q2);
  817. dcm(3,2) = 2*(q0*q1 + q2*q3);
  818. dcm(3,3) = q0s - q1s - q2s + q3s;
  819. % 欧拉角 (ENU 系统,弧度制)
  820. phi = atan2(-dcm(3,1), dcm(3,3)); % roll
  821. theta = asin(dcm(3,2)); % pitch
  822. psi = atan2(-dcm(1,2), dcm(2,2)); % yaw
  823. % 处理 pitch = ±90° 奇异点
  824. eps = 1e-4;
  825. if abs(theta - pi/2) < eps
  826. phi = 0;
  827. psi = atan2(-dcm(1,3), -dcm(2,3));
  828. elseif abs(theta + pi/2) < eps
  829. phi = 0;
  830. psi = atan2(dcm(1,3), dcm(2,3));
  831. end
  832. %弧度制
  833. euler = [phi, theta, psi]; % [roll pitch yaw]
  834. % 转换为角度制
  835. % euler = rad2deg([phi, theta, psi]); % [roll pitch yaw] in degree
  836. end
  837. function Q = Quaternion_Enu_ByEuler(yaw, pitch, roll)
  838. % Quaternion_Enu_ByEuler 将欧拉角(yaw, pitch, roll)转换为四元数
  839. % 输入角度单位为弧度 (rad)
  840. % Q = [q0 q1 q2 q3] (标量在前,向量在后)
  841. % 计算半角的正弦和余弦
  842. cy = cos(yaw/2);
  843. cp = cos(pitch/2);
  844. cr = cos(roll/2);
  845. sy = sin(yaw/2);
  846. sp = sin(pitch/2);
  847. sr = sin(roll/2);
  848. q0 = cy*cp*cr - sy*sp*sr;
  849. q1 = cy*sp*cr - sy*cp*sr;
  850. q2 = cy*cp*sr + sy*sp*cr;
  851. q3 = cy*sp*sr + sy*cp*cr;
  852. % 组合成向量
  853. Q = [q0 q1 q2 q3];
  854. end
  855. function clusters = euclideanClusterExtraction(filtered_point_rfu, dist_thresh, min_points)
  856. % filtered_point_rfu: 结构体,包含 x, y, z, num
  857. % dist_thresh: 聚类的阈值 (比如 0.5 m)
  858. % min_points: 最小点数阈值 (比如 3)
  859. % 转换为矩阵 Nx3
  860. points = [filtered_point_rfu.x, filtered_point_rfu.y, filtered_point_rfu.z];
  861. N = size(points, 1);
  862. visited = false(N, 1); % 标记访问
  863. cluster_list = {}; % 保存聚类结果
  864. for i = 1:N
  865. if ~visited(i)
  866. % 新的聚类
  867. cluster_points = points(i, :);
  868. visited(i) = true;
  869. % 广度优先搜索(BFS)
  870. queue = i;
  871. while ~isempty(queue)
  872. idx = queue(1);
  873. queue(1) = []; % 出队
  874. % 找邻居
  875. diffs = points - points(idx, :);
  876. dists = sum(diffs.^2, 2);
  877. neighbors = find(dists < dist_thresh^2 & ~visited);
  878. if ~isempty(neighbors)
  879. visited(neighbors) = true;
  880. cluster_points = [cluster_points; points(neighbors, :)];
  881. queue = [queue; neighbors]; %#ok<AGROW>
  882. end
  883. end
  884. % 只保留点数超过 min_points 的聚类
  885. if size(cluster_points, 1) >= min_points
  886. cluster_list{end+1} = cluster_points; %#ok<AGROW>
  887. end
  888. end
  889. end
  890. % 输出格式化结果
  891. clusters = struct('points', {}, 'center', {}, 'num', {});
  892. for k = 1:numel(cluster_list)
  893. pts = cluster_list{k};
  894. clusters(k).points = pts;
  895. clusters(k).center = mean(pts, 1);
  896. clusters(k).num = size(pts, 1);
  897. end
  898. end
  899. function clusters_all = multiRangeClustering(points, params)
  900. % points: struct,包含 x,y,z,num
  901. % params: 每个距离区间的聚类参数设置
  902. clusters_all = [];
  903. for i = 1:length(params)
  904. % 提取该区间的点
  905. rangeIdx = points.y >= params(i).y_min & points.y < params(i).y_max;
  906. pts_range = [points.x(rangeIdx), points.y(rangeIdx), points.z(rangeIdx)];
  907. if isempty(pts_range)
  908. continue;
  909. end
  910. % 在该区间用对应参数做聚类
  911. clusters = euclideanClusterExtraction(...
  912. struct('x',pts_range(:,1),'y',pts_range(:,2),'z',pts_range(:,3),'num',size(pts_range,1)), ...
  913. params(i).dist_thresh, ...
  914. params(i).min_points);
  915. % 合并结果
  916. clusters_all = [clusters_all, clusters];
  917. end
  918. end
  919. function [cx, cy] = updateObstacleTracking(clusters, params)
  920. persistent trackedTargets
  921. if isempty(trackedTargets)
  922. trackedTargets = struct('x', {}, 'y', {}, 'confidence', {}, 'updated', {});
  923. end
  924. % --- 提取每个簇的代表点 ---
  925. numClusters = numel(clusters);
  926. cx_new = nan(1,numClusters);
  927. cy_new = nan(1,numClusters);
  928. for k = 1:numClusters
  929. pts = clusters(k).points;
  930. if isempty(pts), continue; end
  931. [min_y, idx] = min(pts(:,2));
  932. cx_new(k) = pts(idx,1);
  933. cy_new(k) = min_y;
  934. end
  935. cur_points = [cx_new(:), cy_new(:)];
  936. % --- 标记所有目标未更新 ---
  937. for i = 1:numel(trackedTargets)
  938. trackedTargets(i).updated = false;
  939. end
  940. % --- 遍历检测点,更新或新建目标 ---
  941. for i = 1:size(cur_points,1)
  942. px = cur_points(i,1);
  943. py = cur_points(i,2);
  944. if isnan(px) || isnan(py), continue; end
  945. % 动态参数选择
  946. if py <= 10
  947. match_thresh = 1;
  948. min_conf = 4;
  949. max_conf = 6;
  950. elseif py <= 20
  951. match_thresh = 1.3;
  952. min_conf = 2;
  953. max_conf = 5;
  954. elseif py <= 35
  955. match_thresh = 1.8;
  956. min_conf = 2;
  957. max_conf = 5;
  958. else
  959. continue;
  960. end
  961. % --- 匹配已有目标 ---
  962. dists = arrayfun(@(t) hypot(t.x - px, t.y - py), trackedTargets);
  963. if isempty(dists)
  964. minDist = inf;
  965. else
  966. [minDist, minIdx] = min(dists);
  967. end
  968. if ~isempty(dists) && minDist < match_thresh
  969. % 命中已有目标
  970. trackedTargets(minIdx).x = px;
  971. trackedTargets(minIdx).y = py;
  972. trackedTargets(minIdx).confidence = ...
  973. min(trackedTargets(minIdx).confidence + params.inc_val, max_conf);
  974. trackedTargets(minIdx).updated = true;
  975. else
  976. % 新建目标
  977. newTarget.x = px;
  978. newTarget.y = py;
  979. newTarget.confidence = 1; % 初始值
  980. newTarget.updated = true;
  981. trackedTargets = [trackedTargets, newTarget];
  982. end
  983. end
  984. % --- 更新置信度 ---
  985. for i = 1:numel(trackedTargets)
  986. if ~trackedTargets(i).updated
  987. trackedTargets(i).confidence = trackedTargets(i).confidence - params.dec_val;
  988. end
  989. end
  990. % 仅保留置信度 > 0 的目标
  991. if ~isempty(trackedTargets)
  992. trackedTargets = trackedTargets([trackedTargets.confidence] > 0);
  993. end
  994. % --- 生成输出点 ---
  995. cx = []; cy = [];
  996. for i = 1:numel(trackedTargets)
  997. py = trackedTargets(i).y;
  998. if py <= 10
  999. min_conf = 4;
  1000. elseif py <= 20
  1001. min_conf = 2;
  1002. elseif py <= 35
  1003. min_conf = 2;
  1004. else
  1005. continue;
  1006. end
  1007. if trackedTargets(i).confidence >= min_conf
  1008. cx(end+1) = trackedTargets(i).x; %#ok<AGROW>
  1009. cy(end+1) = py; %#ok<AGROW>
  1010. end
  1011. end
  1012. if isempty(cx)
  1013. cx = 0; cy = 0;
  1014. end
  1015. end