В этом примере показано, как распределить приложение Automated Parking Valet (Automated Driving Toolbox) между различными узлами в сети ROS. В зависимости от системы этот пример приведен для сетей ROS и ROS 2, использующих MATLAB ® или Simulink ®. В приведенном примере используются ROS и MATLAB. Другие примеры см. в:
Этот пример является расширением примера автоматизированного парковочного шкафа (Automated Driving Toolbox) в Automated Driving Toolbox™. Типичное автономное приложение имеет следующие компоненты.

Для простоты в этом примере основное внимание уделяется планированию, управлению и упрощенной модели транспортного средства. В примере для замены информации о локализации используются предварительно записанные данные.
Это приложение демонстрирует типичное разделение различных функций на узлы ROS. На следующем рисунке показано разделение приведенного выше примера на различные узлы. Каждый узел: планирование, управление и транспортное средство - это узел АФК, реализующий функции, показанные ниже. Взаимосвязи между узлами показывают темы, используемые в каждом соединении узлов.

Сначала загрузите план маршрута и заданную карту затрат, используемую планировщиком поведения и анализатором пути. Планировщик поведения, планировщик пути, анализатор пути, боковой и логарифмический контроллеры реализуются классами-помощниками, которые настраиваются с помощью этого примера вызова функции-помощника.
exampleHelperROSValetSetupGlobals;
Инициализированные глобальные показатели организованы как поля в глобальной структуре, valet.
disp(valet)
mapLayers: [1×1 struct]
costmap: [1×1 vehicleCostmap]
vehicleDims: [1×1 vehicleDimensions]
maxSteeringAngle: 35
data: [1×1 struct]
routePlan: [4×3 table]
currentPose: [4 12 0]
vehicleSim: [1×1 ExampleHelperROSValetVehicleSimulator]
behavioralPlanner: [1×1 ExampleHelperROSValetBehavioralPlanner]
motionPlanner: [1×1 pathPlannerRRT]
goalPose: [56 11 0]
refPath: [1×1 driving.Path]
transitionPoses: [14×3 double]
directions: [522×1 double]
currentVel: 0
approxSeparation: 0.1000
numSmoothPoses: 522
maxSpeed: 5
startSpeed: 0
endSpeed: 0
refPoses: [522×3 double]
cumLengths: [522×1 double]
curvatures: [522×1 double]
refVelocities: [522×1 double]
sampleTime: 0.1000
lonController: [1×1 ExampleHelperROSValetLongitudinalController]
controlRate: [1×1 ExampleHelperROSValetFixedRate]
pathAnalyzer: [1×1 ExampleHelperROSValetPathAnalyzer]
parkPose: [36 44 90]
Инициализируйте сеть ROS.
rosinit;
Launching ROS Core... ..................................................................................Done in 5.3625 seconds.

Initializing ROS master on http://192.168.0.10:60903. Initializing global node /matlab_global_node_49911 with NodeURI http://sbd508773glnxa64:42335/
masterHost = 'localhost';Функции приложения распределены между узлами ROS. В этом примере используются три узла ROS: planningNode, controlNode, и vehicleNode.
Узел планирования вычисляет каждый сегмент пути на основе текущей позиции транспортного средства. Этот узел отвечает за формирование плавного пути и публикует его в сети.
Этот узел публикует следующие разделы:
/smoothpath
/velprofile
/directions
/speed
/nextgoal
Узел подписывается на следующие разделы:
/currentvel
/currentpose
/desiredvel
/reachgoal
При получении /reachgoal сообщение, узел запускает exampleHelperROS2ValetPlannerCallback обратный вызов, который планирует следующий сегмент.

Создание узла планирования
planningNode = ros.Node('planning', masterHost);Создание издателей для узла планирования. Укажите типы сообщений для издателя или подписчика для раздела, отсутствующего в сети ROS.
planning.PathPub = ros.Publisher(planningNode, '/smoothpath', 'std_msgs/Float64MultiArray'); planning.VelPub = ros.Publisher(planningNode, '/velprofile', 'std_msgs/Float64MultiArray'); planning.DirPub = ros.Publisher(planningNode, '/directions', 'std_msgs/Float64MultiArray'); planning.SpeedPub = ros.Publisher(planningNode,'/speed','std_msgs/Float64MultiArray'); planning.NxtPub = ros.Publisher(planningNode, '/nextgoal', 'geometry_msgs/Pose2D');
Создайте подписчиков для плановика, planningNode.
planning.CurVelSub = ros.Subscriber(planningNode, '/currentvel', 'std_msgs/Float64'); planning.CurPoseSub = ros.Subscriber(planningNode, '/currentpose', 'geometry_msgs/Pose2D'); planning.DesrVelSub = ros.Subscriber(planningNode, '/desiredvel', 'std_msgs/Float64');
Создать абонента, GoalReachSub, чтобы послушать /reachgoal раздел узла планирования и укажите операцию обратного вызова.
GoalReachSub = ros.Subscriber(planningNode, '/reachgoal', 'std_msgs/Bool'); GoalReachSub.NewMessageFcn = @(~,msg)exampleHelperROSValetPlannerCallback(msg, planning, valet);
Узел управления отвечает за продольные и боковые контроллеры. Этот узел публикует следующие разделы:
/steeringangle
/accelcmd
/decelcmd
/vehdir
/reachgoal
Узел подписывается на следующие разделы:
/smoothpath
/directions
/speed
/currentpose
/currentvel
/nextgoal
/velprofile
При получении /velprofile сообщение, узел запускает exampleHelperROS2ValetControlCallback обратный вызов, который посылает управляющие сообщения в транспортное средство

Создайте контроллер, controlNodeи настройте издателей и подписчиков в узле.
controlNode = ros.Node('control', masterHost); % Publishers for controlNode control.SteeringPub = ros.Publisher(controlNode, '/steeringangle', 'std_msgs/Float64'); control.AccelPub = ros.Publisher(controlNode, '/accelcmd', 'std_msgs/Float64'); control.DecelPub = ros.Publisher(controlNode, '/decelcmd', 'std_msgs/Float64'); control.VehDirPub = ros.Publisher(controlNode, '/vehdir', 'std_msgs/Float64'); control.VehGoalReachPub = ros.Publisher(controlNode, '/reachgoal'); % Subscribers for controlNode control.PathSub = ros.Subscriber(controlNode, '/smoothpath'); control.DirSub = ros.Subscriber(controlNode, '/directions'); control.SpeedSub = ros.Subscriber(controlNode, '/speed'); control.CurPoseSub = ros.Subscriber(controlNode, '/currentpose'); control.CurVelSub = ros.Subscriber(controlNode, '/currentvel'); control.NextGoalSub = ros.Subscriber(controlNode, '/nextgoal'); % Create subscriber for /velprofile for control node and provide the callback function. VelProfSub = ros.Subscriber(controlNode, '/velprofile'); VelProfSub.NewMessageFcn = @(~,msg)exampleHelperROSValetControlCallback(msg, control, valet);
Узел транспортного средства отвечает за моделирование модели транспортного средства. Этот узел публикует следующие разделы:
/currentvel
/currentpose
Узел подписывается на следующие разделы:
/accelcmd
/decelcmd
/vehdir
/steeringangle
При получении /steeringangle сообщение, имитатор транспортного средства запускается в функции обратного вызова, exampleHelperROSValetVehicleCallback.

% Create vehicle node. vehicleNode = ros.Node('vehicle', masterHost); % Create publishers for vehicle node. vehicle.CurVelPub = ros.Publisher(vehicleNode, '/currentvel'); vehicle.CurPosePub = ros.Publisher(vehicleNode, '/currentpose'); % Create subscribers for vehicle node. vehicle.AccelSub = ros.Subscriber(vehicleNode, '/accelcmd'); vehicle.DecelSub = ros.Subscriber(vehicleNode, '/decelcmd'); vehicle.DirSub = ros.Subscriber(vehicleNode, '/vehdir'); % Create subscriber for |/steeringangle|, which runs the vehicle simulator % callback. SteeringSub = ros.Subscriber(vehicleNode, '/steeringangle', ... @(~,msg)exampleHelperROSValetVehicleCallback(msg, vehicle, valet));
Чтобы инициализировать моделирование, отправьте первое сообщение о скорости и текущее сообщение о позе. Это сообщение приводит к запуску плановиком цикла планирования.
curVelMsg = getROSMessage(vehicle.CurVelPub.MessageType); curVelMsg.Data = valet.vehicleSim.getVehicleVelocity; send(vehicle.CurVelPub, curVelMsg); curPoseMsg = getROSMessage(vehicle.CurPosePub.MessageType); curPoseMsg.X = valet.currentPose(1); curPoseMsg.Y = valet.currentPose(2); curPoseMsg.Theta = valet.currentPose(3); send(vehicle.CurPosePub, curPoseMsg); reachMsg = getROSMessage(control.VehGoalReachPub.MessageType); reachMsg.Data = true; send(control.VehGoalReachPub, reachMsg);
Основной контур ожидает behavioralPlanner сказать, что транспортное средство достигло позиции подготовки.
while ~reachedDestination(valet.behavioralPlanner) pause(1); end % Show the vehicle simulation figure. showFigure(valet.vehicleSim);

Обратные вызовы парковочного маневра немного отличаются от обычного манёвра вождения. Замените обратные вызовы для /velprofile и /reachgoal абоненты.
VelProfSub.NewMessageFcn = @(~,msg)exampleHelperROSValetParkControlCallback(msg, control, valet); GoalReachSub.NewMessageFcn = @(~,msg)exampleHelperROSValetParkManeuver(msg, planning, valet); pause(1); reachMsg = getROSMessage(control.VehGoalReachPub.MessageType); reachMsg.Data = false; send(control.VehGoalReachPub, reachMsg); % Receive a message from the |/reachgoal| topic using the subcriber. This % waits until a new message is received. Display the figure. The vehicle % has completed the full automated valet manuever. receive(GoalReachSub); exampleHelperROSValetCloseFigures; snapnow;

Удалите симулятор и завершите работу всех узлов, очистив издателей, подписчиков и дескрипторы узлов.
delete(valet.vehicleSim); % Clear variables that were created above. clear('valet'); GoalReachSub.NewMessageFcn = []; VelProfSub.NewMessageFcn = []; clear('planning', 'planningNode', 'GoalReachSub'); clear('control', 'controlNode', 'VelProfSub'); clear('vehicle', 'vehicleNode', 'SteeringSub'); clear('curPoseMsg', 'curVelMsg', 'reachMsg'); clear('masterHost'); % Shutdown the ROS network. rosshutdown;
Shutting down global node /matlab_global_node_49911 with NodeURI http://sbd508773glnxa64:42335/ Shutting down ROS master on http://192.168.0.10:60903. .....