В этом примере показано, как комбинировать данные роботизированной одометрии и наблюдаемые реперные маркеры, называемые тегами RotalTags, для лучшей оценки траектории робота и положения ориентиров в окружающей среде.
Загрузить .mat файл, который содержит исходные данные, зарегистрированные от rosbag на ClearPath Robotics™ Jackal™. Исходные данные включают в себя:
Изображения, взятые, используя камеру сети Axis™ M1013 в разрешении 640x480.
Данные одометрии предварительно обработаны и синхронизированы с данными изображения.
Розбаг импортируется в MATLAB™ и хранится как .mat файл. Чтобы увидеть, как данные извлекаются из rosbag и предварительно обрабатываются, exampleHelperExtractDataFromRosbag в конце этого примера приводится пример помощника. Для использования функции помощника необходима панель инструментов ROS.
filename = matlab.internal.examples.downloadSupportFile("spc/robotics","apriltag_odometry_slam_dataset.zip"); unzip(filename);
В этом примере ряд маркеров AprilTag напечатан и случайным образом помещен в условия испытаний. Теги рассматриваются как ориентиры на графике позы. Учитывая особенности камеры и размер тега, можно преобразовать изображения с наблюдениями с помощью функции «Метка» в точечные измерения ориентира, используя readAprilTag (Панель инструментов компьютерного зрения). Эти измерения ориентира относятся к текущему каркасу робота. Чтобы узнать внутренние параметры Вашей камеры, выполните шаги в Калибровке Камеры Используя Маркеры AprilTag (Компьютерный Комплект инструментов Видения) пример или используйте образец шахматной доски с Калибратором Камеры (Компьютерный Комплект инструментов Видения) приложение. Маркеры AprilTag, используемые в этом примере, взяты от»tag46h11"семейство без повторяющихся идентификаторов. Напечатанный размер ярлыка составляет 136,65 мм.

tagFamily = "tag36h11"; tagSize = 136.65; % mm load apriltag_odometry_slam_dataset/apriltag_odometry_slam_dataset.mat load cameraParams.mat
После импорта синхронизированных данных датчика и параметров камеры создайте график позы оценок узлов из измерений в данных датчика. График позы содержит оценки узлов, ограничения кромок и замыкания петель, которые оценивают позы робота.
% Create a pose graph object
pg = poseGraph3D;Используя маркеры реперных знаков, такие как «Метки», узор блока на изображении предоставляет уникальные идентификаторы для каждого наблюдения ориентира. Эта идентификационная информация уменьшает потребность в сложных алгоритмах ассоциации данных при выполнении одновременной локализации и отображения (SLAM).
Создать poseGraph3D объект. Создайте переменные для хранения предыдущей позы и идентификатора узла.
lastTform = []; lastPoseNodeId = 1;
Создать containers.Map структура данных для поддержания соответствия между идентификаторами тегов и их идентификаторами узлов в графе позы.
tagToNodeIDMap = containers.Map('KeyType','double','ValueType','double'); % Offset transformations for AprilTag observations % The odometry data recorded in the .mat file is measured from 'odom' % frame (i.e. fixed world frame) to the 'laser' frame that moves with % the robot. There is a fixed transformation between the 'laser' % frame and the camera frame applied manually. R1 = [0 0 1; -1 0 0; 0 -1 0]; Ta = blkdiag(R1,1); % The camera frame has z axis pointing forward and y axis pointing down Tb = eye(4); T2(1,3) = -0.11; T(3,3) = 0.146; % Fixed translation of camera frame origin to 'laser' frame
В этом примере оценивается траектория робота на основе результатов измерений ориентира, полученных в ходе наблюдений одометрии и при наблюдениях с помощью тега. Выполните итерацию данных датчика и выполните следующие действия.
Добавление данных одометрии для представления графика с помощью addRelativePose функция. Вычислите относительные положения между каждой одометрией перед добавлением к графу позы.
Добавление знаковых измерений из результатов измерений TarmingTag на изображениях с помощью readAprilTag функция. Поскольку изображение может содержать несколько тегов, выполните итерацию через все возвращенные идентификаторы. Добавление ориентиров точек относительно текущего узла позы с помощью addPointLandMark функция.
Отображение изображения с маркерами вокруг наблюдений TarchedTag. Изображение обновляется при итерации данных.
figure('Visible','on') for i = 1:numel(imageData) % Add odometry data to pose graph T = odomData{i}; if isempty(lastTform) nodePair = addRelativePose(pg,[0 0 0 1 0 0 0],[],lastPoseNodeId); else relPose = exampleHelperComputeRelativePose(lastTform,T); nodePair = addRelativePose(pg,relPose,[],lastPoseNodeId); end currPoseNodeId = nodePair(2); % Add landmark measurement based on AprilTag observation in the image. I = imageData{i}; [id,loc,poseRigid3d,detectedFamily] = readAprilTag(I,tagFamily,cameraParams.Intrinsics,tagSize); for j = 1:numel(id) % Insert observation markers to image. markerRadius = 6; numCorners = size(loc,1); markerPosition = [loc(:,:,j),repmat(markerRadius,numCorners,1)]; I = insertShape(I, "FilledCircle", markerPosition, "Color", "red", "Opacity", 1); t = poseRigid3d(j).Translation/1000; % change from mm to meter po = [t(:);1]; p = Tb*Ta*po; if tagToNodeIDMap.isKey(id(j)) lmkNodeId = tagToNodeIDMap(id(j)); addPointLandmark(pg, p(1:3)', [], currPoseNodeId, lmkNodeId); else nodePair = addPointLandmark(pg, p(1:3)', [], currPoseNodeId); tagToNodeIDMap(id(j)) = nodePair(2); end end % Show the image with AprilTag observation markers. imshow(I) drawnow lastTform = T; lastPoseNodeId = currPoseNodeId; end

После построения графика позы оптимизируйте его с помощью optimizePoseGraph функция.
pgUpd = optimizePoseGraph(pg);
Визуализируйте как начальный, так и оптимизированный график позы. Оптимизированный график позы показывает следующие улучшения:
Исходное положение робота относительно ориентиров было скорректировано.
Ориентиры на каждой стене лучше выровнены.
Вертикальный дрейф в траектории робота вдоль направления z был скорректирован.
figure('Visible','on'); show(pg); axis equal; xlim([-10.0 5.0]); ylim([-2.0 12.0]); title('Before Optimization XY View')

figure('Visible','on'); show(pgUpd); axis equal; xlim([-10.0 5.0]); ylim([-2.0 12.0]); title('After Optimization XY View')

% Vertical drift. figure('Visible','on'); subplot(2,1,1) show(pg); xlim([-10.0 5.0]); view(0, 0) title('Before Optimization XZ View') subplot(2,1,2) show(pgUpd); xlim([-10.0 5.0]); view(0, 0) title('After Optimization XZ View')

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

Обратите внимание на то, что на следующих изображениях камера не захватывает один тег, отмеченный красным кружком. На этих изображениях показано положение тегов TressingTags для справки.

exampleHelperExtractDataFromRosbag функция извлекает синхронизированные данные изображения и одометрии из росбага, записанного на робота Jackal UGV (apriltag_odometry_slam_dataset.bag находится в той же папке, что и .mat файл). Импорт из rosbag (ROS Toolbox) требуется ROS Toolbox.
function [imageData, odomData] = exampleHelperExtractDataFromRosbag(bagName) %exampleHelperExtractDataFromRosbag Extract synchronized image and odometry % data from a rosbag recorded on a Jackal UGV robot. ROS Toolbox license % is required to use this function. % Copyright 2020 The MathWorks, Inc. bagSel = rosbag(bagName); imgSel = bagSel.select('Topic','axis/image_raw_out/compressed'); % Image topic % Needed for computing change between SE(2) poses ss = stateSpaceSE2; ss.WeightTheta = 1; lastPoseSE2 = []; lastT = []; imageData = {}; odomData = {}; % Grab all the image messages imgObjs = imgSel.readMessages; for i = 1:numel(imgObjs) % Odom data is extracted from tf if bagSel.canTransform('odom', 'laser', imgObjs{i}.Header.Stamp) % From odom frame to laser frame % ith odom reading is extracted at the time of the ith image Tstamped = bagSel.getTransform('odom', 'laser', imgObjs{i}.Header.Stamp); [T, poseSE2] = translateTransformStampedMsg(Tstamped); if isempty(lastT) takeThisImage = true; else takeThisImage = true; % Only accept a new pair of sensor measurements if the robot odom pose % has changed more than this threshold since last accepted one % Here we use the stateSpaceSE2's distance function to calculate the 2D pose difference if ss.distance(poseSE2, lastPoseSE2) < 0.06 takeThisImage = false; end end if takeThisImage I = readImage(imgObjs{i}); % An alternative is to use the newer convenience function "rosReadImage" imageData{end+1} = I; odomData{end+1} = T; lastPoseSE2 = poseSE2; lastT = T; end end end end function [ToutSE3, poseSE2] = translateTransformStampedMsg(Tin) %translateTransformMsg Extract the 4x4 homogeneous transformation matrix, % ToutSE3, from a TransformStamped message object. This function also % returns a second output, poseSE2, which is an SE(2) pose vector % computed by projecting ToutSE2 to the XY plane. Note the formulation % used is approximate and relies on the assumption that the robot mostly % moves on the flat ground. % Copyright 2020 The MathWorks, Inc. %Tin - TransformStamped message object x = Tin.Transform.Translation.X; y = Tin.Transform.Translation.Y; z = Tin.Transform.Translation.Z; qx = Tin.Transform.Rotation.X; qy = Tin.Transform.Rotation.Y; qz = Tin.Transform.Rotation.Z; qw = Tin.Transform.Rotation.W; q = [ qw, qx, qy, qz]; % Note the sequence for quaternion in MATLAB ToutSE3 = robotics.core.internal.SEHelpers.poseToTformSE3([x,y,z,q]); YPR = quat2eul(q); poseSE2 = [x,y,YPR(1)]; end