Слияние данных радара и лидара с использованием ROS

Выполните слияние датчика уровня дорожки по зарегистрированным данным датчика лидара для сценария вождения, записанного на rosbag. Этот пример использует тот же сценарий вождения и слияние датчиков, что и пример Track-Level Fusion of Radar and 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");

Получите доступ к rosbag и просмотрите доступные темы.

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 and 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;

Выполните слияние с сообщениями датчика

Выполните итерацию сообщений и запустите алгоритм слияния датчиков. Следите за визуализацией, чтобы увидеть проигрывание rosbag и отслеживание транспортных средств с помощью слияния датчиков.

Датчики срабатывали для всех измерений одновременно, и все радарные датчики публиковали сообщение при каждом срабатывании, даже если никаких обнаружений не было. Поскольку все массивы сообщений имеют одинаковую длину, обработка данных о датчике намного проще. Если датчики запускаются с различными скоростями, возникает проблема с соединением или сообщения были получены вне порядка, может потребоваться промежуточный шаг синхронизации данных о датчике.

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