Выполните слияние датчиков уровня дорожки на записанных данных лидарного датчика для сценария вождения, записанного на росбэг. В этом примере используется тот же сценарий вождения и слияние датчиков, что и в примере «Слияние радаров на уровне дорожек» и «Lidar Data» (Sensor Fusion and Tracking Toolbox), но вместо моделирования сценария вождения используется предварительно записанный rosbag.
Это обеспечивает пример rosbag, содержащего данные лидара, радара и транспортного средства, и имеет приблизительно 33MB размер. Загрузите rosbag с веб-сайта MathWorks.
bagFile = matlab.internal.examples.downloadSupportFile("ros","rosbags/simulated_lidar_radar_driving_798.bag");
Откройте росбаг и просмотрите доступные разделы.
bag = rosbag(bagFile); disp(bag.AvailableTopics(:,["NumMessages", "MessageType"]))
NumMessages MessageType
___________ ________________________________________
/clock 114 rosgraph_msgs/Clock
/ego/lidar_scan 114 sensor_msgs/PointCloud2
/ego/radar_1/detections 114 cob_object_detection_msgs/DetectionArray
/ego/radar_2/detections 114 cob_object_detection_msgs/DetectionArray
/ego/radar_3/detections 114 cob_object_detection_msgs/DetectionArray
/ego/radar_4/detections 114 cob_object_detection_msgs/DetectionArray
/ego/state 114 nav_msgs/Odometry
Эго-автомобиль имеет один лидар и четыре радиолокационных датчика, а также абсолютную позиционную информацию для себя. Эти сообщения могут быть извлечены в отдельные массивы для последующего слияния. Поскольку эта rosbag сжата для уменьшения размера файла, чтение сообщений может занять несколько секунд. Извлеките сообщения как структуры с помощью DataFormat аргумент «имя-значение», который повышает общую производительность для сообщений ROS.
lidarBagSel = select(bag,"Topic","/ego/lidar_scan"); lidarMsgs = readMessages(lidarBagSel,"DataFormat","struct"); stateBagSel = select(bag,"Topic","/ego/state"); stateMsgs = readMessages(stateBagSel,"DataFormat","struct"); radarMsgs = cell(bag.AvailableTopics{"/ego/radar_1/detections","NumMessages"},4); for idxRadar = 1:4 radarBagSel = select(bag,"Topic",sprintf("/ego/radar_%d/detections",idxRadar)); radarMsgs(:,idxRadar) = readMessages(radarBagSel,"DataFormat","struct"); end
Создайте временные метки для использования с объединением относительно первой временной метки сообщения. Обратите внимание, что сумка StartTime не может использоваться, так как это временная метка, в которую было записано первое сообщение, более поздняя, чем временная метка сообщения.
clockBagSel = select(bag,"Topic","/clock"); clockMsg = readMessages(bag,1,"DataFormat","struct"); startTime = double(clockMsg{1}.Clock_.Sec) + double(clockMsg{1}.Clock_.Nsec)*1e-9;
Информация о датчиках известна на основе настройки транспортного средства и должна быть помещена в формат, используемый алгоритмом слежения.
[lidarInfo, radarInfo, radarParameters] = helperRadarLidarInfo;
Алгоритмы радиолокационного и лидарного слежения необходимы для обработки сканирований с высоким разрешением и определения объектов, просматриваемых в сканированиях, без повторов. Эти алгоритмы определяются как вспомогательные функции. Более подробную информацию о алгоритмах можно найти в примере Track-Level Fusion of Radar и Lidar Data (Sensor Fusion and Tracking Toolbox).
radarTrackingAlgorithm = helperROSRadarTracker(radarInfo); radarConfig = fuserSourceConfiguration("SourceIndex",1,... "IsInitializingCentralTracks",true,... "CentralToLocalTransformFcn",@central2radar,... "LocalToCentralTransformFcn",@radar2central); lidarTrackingAlgorithm = helperLidarTrackingAlgorithm(lidarInfo); lidarConfig = fuserSourceConfiguration("SourceIndex",2,... "IsInitializingCentralTracks",true); fuser = trackFuser("SourceConfigurations",{radarConfig;lidarConfig},... "StateTransitionFcn",lidarTrackingAlgorithm.StateTransitionFcn,... "ProcessNoise",diag([1 3 1]),... "HasAdditiveProcessNoise",false,... "AssignmentThreshold",[250 inf],... "ConfirmationThreshold",[3 5],... "DeletionThreshold",[5 5],... "StateFusion","Custom",... "CustomStateFusionFcn",@helperRadarLidarFusionFcn);
Настройка фигуры и свойств для визуализации данных датчика с помощью вспомогательной функции.
displayHelper = helperROSSensorFusionDisplay;
Выполните итерацию сообщений и запустите алгоритм слияния датчиков. Следите за визуализацией, чтобы увидеть воспроизводимую росбэг и отслеживание транспортных средств с помощью слияния датчиков.
Датчики запускались для всех измерений одновременно, и все радиолокационные датчики публиковали сообщение при каждом срабатывании, даже если никаких обнаружений не было. Поскольку все массивы сообщений имеют одинаковую длину, обработка данных датчика намного проще. Если датчики срабатывают на разных скоростях, возникает проблема с подключением или сообщения принимаются не по порядку, может потребоваться промежуточный этап синхронизации данных датчика.
for idx = 1:numel(lidarMsgs) % Extract time on first sensor reading. % Make time relative to rosbag start time for easier tracking. lidarTimeStamp = lidarMsgs{idx}.Header.Stamp; lidarTime = double(lidarTimeStamp.Sec) + ... double(lidarTimeStamp.Nsec)*1e-9 - startTime; radarTimeStamp = radarMsgs{idx,1}.Header.Stamp; radarTime = double(radarTimeStamp.Sec) + ... double(radarTimeStamp.Nsec)*1e-9 - startTime; % Extract vehicle state and modify structures for processing. egoPose = struct; stateMsg = stateMsgs{idx}; positionMsg = stateMsg.Pose.Pose.Position; egoPose.Position = [positionMsg.X ; positionMsg.Y ; positionMsg.Z]; % Orientation in degrees. orientQuat = rosReadQuaternion(stateMsg.Pose.Pose.Orientation); orientEul = eulerd(orientQuat,"XYZ","point"); egoPose.Roll = orientEul(1); egoPose.Pitch = orientEul(2); egoPose.Yaw = orientEul(3); % By convension, nav_msgs/Odometry velocity is provided in the child % reference frame (the vehicle). The fusion requires velocity in the world % reference frame. velMsg = stateMsg.Twist.Twist.Linear; egoPose.Velocity = rotatepoint(orientQuat,... [velMsg.X velMsg.Y velMsg.Z]); % Extract point cloud from lidar for processing % This lidar provided no RGB or intensity information lidarXYZPoints = rosReadXYZ(lidarMsgs{idx}); ptCloud = pointCloud(lidarXYZPoints); % Extract radar detections into a single array using metadata to % specify the source sensor. nDetections = sum(cellfun(@(msg) numel(msg.Detections),radarMsgs(idx,:))); radarDetections = cell(nDetections,1); % Preallocate idxDetection = 1; for idxRadar = 1:size(radarMsgs,2) for idxRadarDetection = 1:numel(radarMsgs{idx,idxRadar}.Detections) detMsg = radarMsgs{idx,idxRadar}.Detections(idxRadarDetection); detTime = double(detMsg.Header.Stamp.Sec) + ... double(detMsg.Header.Stamp.Nsec)*1e-9 - startTime; measureMsg = detMsg.Pose.Pose.Position; measurement = [measureMsg.X ; measureMsg.Y ; measureMsg.Z]; % Measurement noise is stored in the bounding box field due to % this message type containing Pose instead of PoseCovariance. measureNoise = diag([detMsg.BoundingBoxLwh.X detMsg.BoundingBoxLwh.Y detMsg.BoundingBoxLwh.Z]); % Store signal-to-noise ratio in Score field. objectAttributes = struct("TargetIndex",detMsg.Id,"SNR",detMsg.Score); radarDetections{idxDetection} = objectDetection(detTime,measurement,... "MeasurementNoise",measureNoise,... "SensorIndex",idxRadar,... "ObjectClassID",0,... "ObjectAttributes",{objectAttributes},... "MeasurementParameters",{radarParameters(idxRadar)}); idxDetection = idxDetection + 1; end end % Generate sensor tracks and analysis information like the bounding box % detections and point cloud segmentation information. radarTracks = radarTrackingAlgorithm(egoPose, radarDetections, radarTime); [lidarTracks, lidarDetections, segmentationInfo] = ... lidarTrackingAlgorithm(egoPose, ptCloud, lidarTime); localTracks = [radarTracks ; lidarTracks]; % Update the fuser. The first call must contain one local track. if ~(isempty(localTracks) && ~isLocked(fuser)) fusedTracks = fuser(localTracks,lidarTime); else fusedTracks = objectTrack.empty(0,1); end % Update the display updateSensorData(displayHelper,ptCloud,radarDetections) plotTracks(displayHelper,radarTracks,lidarTracks,fusedTracks,egoPose) end

В приведенном выше сценарии используются следующие определения функций.
radar2central
function centralTrack = radar2central(radarTrack) % Initialize a track of the correct state size centralTrack = objectTrack('State',zeros(10,1),... 'StateCovariance',eye(10)); % Sync properties of radarTrack except State and StateCovariance with % radarTrack See syncTrack defined below. centralTrack = syncTrack(centralTrack,radarTrack); xRadar = radarTrack.State; PRadar = radarTrack.StateCovariance; H = zeros(10,7); % Radar to central linear transformation matrix H(1,1) = 1; H(2,2) = 1; H(3,3) = 1; H(4,4) = 1; H(5,5) = 1; H(8,6) = 1; H(9,7) = 1; xCentral = H*xRadar; % Linear state transformation PCentral = H*PRadar*H'; % Linear covariance transformation PCentral([6 7 10],[6 7 10]) = eye(3); % Unobserved states % Set state and covariance of central track centralTrack.State = xCentral; centralTrack.StateCovariance = PCentral; end
central2radar
function radarTrack = central2radar(centralTrack) % Initialize a track of the correct state size radarTrack = objectTrack('State',zeros(7,1),... 'StateCovariance',eye(7)); % Sync properties of centralTrack except State and StateCovariance with % radarTrack See syncTrack defined below. radarTrack = syncTrack(radarTrack,centralTrack); xCentral = centralTrack.State; PCentral = centralTrack.StateCovariance; H = zeros(7,10); % Central to radar linear transformation matrix H(1,1) = 1; H(2,2) = 1; H(3,3) = 1; H(4,4) = 1; H(5,5) = 1; H(6,8) = 1; H(7,9) = 1; xRadar = H*xCentral; % Linear state transformation PRadar = H*PCentral*H'; % Linear covariance transformation % Set state and covariance of radar track radarTrack.State = xRadar; radarTrack.StateCovariance = PRadar; end
syncTrack
function tr1 = syncTrack(tr1,tr2) props = properties(tr1); notState = ~strcmpi(props,'State'); notCov = ~strcmpi(props,'StateCovariance'); props = props(notState & notCov); for i = 1:numel(props) tr1.(props{i}) = tr2.(props{i}); end end