В этом примере показано использование структур сообщений ROS, а также их преимущества и отличия от объектов сообщений.
Структуры сообщений имеют более высокую производительность над объектами при выполнении начального создания, считывании их из файлов rosbag, доступе к вложенным свойствам и выполнении операций связи по сети ROS. Кроме того, структуры сообщений являются единственным поддерживаемым форматом сообщений при генерации кода через MATLAB Coder™.
Объекты сообщений ROS представляют собой экземпляры классов, определенных специально для каждого типа сообщений.
msgObj = rosmessage("nav_msgs/Path");
class(msgObj)ans = 'ros.msggen.nav_msgs.Path'
Свойства объекта содержат данные сообщения, и каждый тип объекта имеет определенные функции, специфичные для сообщения ROS.
showdetails(msgObj)
Header
Stamp
Sec : 0
Nsec : 0
Seq : 0
FrameId :
Poses
Введены структуры сообщений АФК для повышения эффективности использования сообщений АФК. Каждое сообщение является типом данных структуры MATLAB ® с теми же полями, что и свойства объектов сообщения ROS.
msgStruct = rosmessage("nav_msgs/Path","DataFormat","struct")
msgStruct = struct with fields:
MessageType: 'nav_msgs/Path'
Header: [1x1 struct]
Poses: [0x1 struct]
class(msgStruct)
ans = 'struct'
Для обновления существующего кода, в котором используются объекты, предусмотрены два общих рабочих процесса с шагами, необходимыми для их обновления.
Этот пример кода показывает, как отправлять и принимать сообщения по сети ROS.
% Setup ROS network
rosinitLaunching ROS Core... Done in 0.77187 seconds. Initializing ROS master on http://192.168.0.10:59565. Initializing global node /matlab_global_node_01839 with NodeURI http://bat6315glnxa64:45457/
stringPub = rospublisher("/chatter","std_msgs/String"); stringSub = rossubscriber("/chatter","std_msgs/String"); % Set message field and send message stringMsg = rosmessage("std_msgs/String"); stringMsg.Data = 'Hello World!'; send(stringPub,stringMsg) % Wait for message to be received and then check the value pause(2) showdetails(stringSub.LatestMessage)
Data : Hello World!
Как обновить
Задайте аргумент имя-значение формата данных издателя и подписчика.
stringPub = rospublisher("/chatter","std_msgs/String","DataFormat","struct"); stringSub = rossubscriber("/chatter","std_msgs/String","DataFormat","struct");
Обновить формат данных для rosmessage также функционируют.
stringMsg = rosmessage("std_msgs/String","DataFormat","struct"); stringMsg.Data = 'Hello World!'; send(stringPub,stringMsg)
В качестве альтернативы, rosmessage можно использовать функцию объекта для издателя. Этот синтаксис создает сообщение, которое следует формату, заданному в издателе, и является наиболее эффективным способом обеспечения совместимости между сообщением и издателем.
stringMsg = rosmessage(stringPub);
stringMsg.Data = 'Hello World!';
send(stringPub,stringMsg)Поскольку структуры не имеют объектных функций, предоставляются новые функции для обработки общих задач сообщений ROS. Для отображения подробных данных структурного сообщения используйте rosShowDetails функция. Для просмотра всех новых функций перейдите к разделу Функции обработки сообщений.
% Wait for message to be received and then check the value
pause(2)
rosShowDetails(stringSub.LatestMessage)ans =
'
MessageType : std_msgs/String
Data : Hello World!'
Пример чтения сообщений из rosbag, указать DataFormat аргумент «имя-значение» для readMessages и любые издатели, которые вы используете для отправки этих сообщений.
% Extract message from rosbag msgType = "nav_msgs/Odometry"; bag = rosbag("ex_multiple_topics.bag"); bagSelect = select(bag,"MessageType",msgType); odomMsgs = readMessages(bagSelect,"DataFormat","struct"); odomMsg = odomMsgs{1}
odomMsg = struct with fields:
MessageType: 'nav_msgs/Odometry'
Header: [1x1 struct]
ChildFrameId: 'base_footprint'
Pose: [1x1 struct]
Twist: [1x1 struct]
% Create publisher and send first message odomPub = rospublisher("/odom",msgType,"DataFormat","struct"); send(odomPub,odomMsg)
Поскольку функции в объектах сообщений ROS не могут использоваться со структурами сообщений, были введены новые функции для обработки сообщений. Этот список включает функции для чтения данных из специализированных сообщений или записи данных в них.

Важным соображением при преобразовании кода является то, что объекты сообщения АФК являются обработчиками, что означает, что объекты сообщения передаются по ссылке, когда предоставляются в качестве входных данных для функций. Если сообщение изменяется в рамках функции, то оно также применяется к сообщению в рабочей области MATLAB ®.
msgObj = rosmessage("geometry_msgs/Pose2D");
pose = [1 2 3];
exampleHelperWritePoseToMsgObj(msgObj,pose)
disp(msgObj) ROS Pose2D message with properties:
MessageType: 'geometry_msgs/Pose2D'
X: 1
Y: 2
Theta: 3
Use showdetails to show the contents of the message
function exampleHelperWritePoseToMsgObj(pointMsg,pose) pointMsg.X = pose(1); pointMsg.Y = pose(2); pointMsg.Theta = pose(3); end
Структуры сообщений передают свое значение только при вводе в функции. Если структура сообщения изменяется в рамках функции, то эта модификация будет применяться только к структуре в рамках этой функции. Чтобы сделать изменение доступным вне функции, должна быть возвращена структура сообщений.
msgStruct = rosmessage("geometry_msgs/Pose2D","DataFormat","struct"); pose = [1 2 3]; % With no return, the message structure will not change exampleHelperWritePoseToMsgObj(msgStruct, pose) disp(msgStruct)
MessageType: 'geometry_msgs/Pose2D'
X: 0
Y: 0
Theta: 0
% When returned from the function, the message can be overwritten.
msgStruct = exampleHelperWritePoseToMsgStruct(msgStruct, pose);
disp(msgStruct) MessageType: 'geometry_msgs/Pose2D'
X: 1
Y: 2
Theta: 3
function pointMsg = exampleHelperWritePoseToMsgStruct(pointMsg,pose) pointMsg.X = pose(1); pointMsg.Y = pose(2); pointMsg.Theta = pose(3); end
Это также относится к специальным функциям обработки сообщений. Функции записи, которые обновляют значения сообщений, теперь имеют выходные аргументы для предоставления обновленной структуры сообщений.
image = imread('imageMap.png'); % Message object msg = rosmessage("sensor_msgs/Image"); msg.Encoding = 'rgb8'; writeImage(msg,image) imshow(readImage(msg))

% Message structure msg = rosmessage("sensor_msgs/Image","DataFormat","struct"); msg.Encoding = 'rgb8'; msg = rosWriteImage(msg,image); imshow(rosReadImage(msg)) close
Структуры сообщений времени и длительности ROS не могут поддерживать перегрузку оператора таким же образом, как объекты времени и длительности. Арифметические операции и операции сравнения должны выполняться путем преобразования структур времени или длительности в числовое значение секунд, выполнения операции и последующего повторного создания структуры времени или длительности при необходимости.
% Periodically update message timestamp with objects msg = rosmessage("std_msgs/Header"); runFor = rosduration(2); tNow = rostime("now"); tEnd = tNow + runFor; while tNow < tEnd msg.Stamp = tNow; % Message may be sent here pause(1) tNow = rostime("now"); end % Periodically update message timestamp with structures msg = rosmessage("std_msgs/Header","DataFormat","struct"); runFor = 2; tNow = rostime("now","DataFormat","struct"); tNowSec = tNow.Sec + tNow.Nsec*1e-9; tEndSec = tNowSec + runFor; while tNowSec < tEndSec msg.Stamp = tNow; % Message may be sent here pause(1) tNow = rostime("now","DataFormat","struct"); tNowSec = tNow.Sec + tNow.Nsec*1e-9; end
Для объектов сообщений ROS поля данных имеют определенные типы. Если установлено значение поля данных, по возможности, ввод преобразуется в правильный тип. В противном случае, если преобразование невозможно, возвращается ошибка.
msg = rosmessage("std_msgs/Int8");
msg.Data = 20;
class(msg.Data)ans = 'int8'
Структуры сообщений ROS принимают без ошибок любой тип данных или имя поля.
msg = rosmessage("std_msgs/Int8","DataFormat","struct"); msg.Data = 'Test'
msg = struct with fields:
MessageType: 'std_msgs/Int8'
Data: 'Test'
msg.Data = 20; class(msg.Data)
ans = 'double'
Вместо этого ошибка недопустимых типов данных при попытке передачи сообщения по сети ROS.
pub = rospublisher("/int_topic","std_msgs/Int8","DataFormat","struct"); send(pub,msg)
Error using ros.Publisher/send (line 290)
Error publishing a message with type std_msgs/Int8 on topic name /int_topic.
Caused by:
Error using ros.internal.Node/publish
Field 'Data' is wrong type; expected a int8.
Для предотвращения ошибок убедитесь, что в сообщениях используется правильный тип данных из определения сообщения.
rosmsg show std_msgs/Int8
int8 Data
Использование структур сообщений является хорошим первым шагом для ускорения отправки и извлечения сообщений АФК. Структуры также повышают производительность настройки и доступа к данным во вложенных сообщениях. Следующий код демонстрирует отправку нескольких сообщений с вложенными полями.
% Set up network (reuse publisher for all examples) pub = rospublisher("/goal_path","nav_msgs/Path","DataFormat","struct"); % Send robot new paths to follow nPtsOnPath = 100; for iPaths = 1:15 pathMsg = rosmessage(pub); for iPts = 1:nPtsOnPath pathMsg.Poses(iPts) = rosmessage("geometry_msgs/PoseStamped","DataFormat","struct"); pathMsg.Poses(iPts).Pose.Position.X = iPaths+iPts; pathMsg.Poses(iPts).Pose.Position.Y = iPaths-iPts; pathMsg.Poses(iPts).Pose.Position.Z = (iPaths+iPts)/10; end send(pub,pathMsg) end
Если сообщения создаются и изменяются в цикле, и для каждой итерации задаются одни и те же поля данных, создать сообщение быстрее только один раз. Переместите создание сообщений за пределы цикла и повторно используйте те же самые сообщения внутри цикла для каждой итерации.
% Set up messages for use pathMsg = rosmessage(pub); poseMsg = rosmessage("geometry_msgs/PoseStamped","DataFormat","struct"); % Send robot new paths to follow nPtsOnPath = 100; for iPaths = 1:15 for iPts = 1:nPtsOnPath pathMsg.Poses(iPts) = poseMsg; pathMsg.Poses(iPts).Pose.Position.X = iPaths+iPts; pathMsg.Poses(iPts).Pose.Position.Y = iPaths-iPts; pathMsg.Poses(iPts).Pose.Position.Z = (iPaths+iPts)/10; end send(pub,pathMsg) end
При чтении или установке нескольких полей во вложенном сообщении извлеките вложенное сообщение перед чтением или установкой полей.
% Set up messages for use pathMsg = rosmessage(pub); poseMsg = rosmessage("geometry_msgs/PoseStamped","DataFormat","struct"); ptMsg = poseMsg.Pose.Position; % Extract nested message % Send robot new paths to follow nPtsOnPath = 100; for iPaths = 1:15 for iPts = 1:nPtsOnPath % Set fields before setting nested message ptMsg.X = iPaths+iPts; ptMsg.Y = iPaths-iPts; ptMsg.Z = (iPaths+iPts)/10; poseMsg.Pose.Position = ptMsg; pathMsg.Poses(iPts) = poseMsg; end send(pub,pathMsg) end
Для относительно больших массивов сообщений предварительное выделение структурного массива может повысить производительность при установке значений в цикле. Используйте этот метод, если массив имеет фиксированную длину каждой итерации.
% Set up messages for use pathMsg = rosmessage(pub); poseMsg = rosmessage("geometry_msgs/PoseStamped","DataFormat","struct"); ptMsg = poseMsg.Pose.Position; % Extract nested message % Preallocate path array nPtsOnPath = 100; pathMsg.Poses(nPtsOnPath) = poseMsg; % Send robot new paths to follow for iPaths = 1:15 for iPts = 1:nPtsOnPath % Set fields before setting nested message ptMsg.X = iPaths+iPts; ptMsg.Y = iPaths-iPts; ptMsg.Z = (iPaths+iPts)/10; poseMsg.Pose.Position = ptMsg; pathMsg.Poses(iPts) = poseMsg; end send(pub,pathMsg) end
Сеть ROS теперь можно отключить.
rosshutdown
Shutting down global node /matlab_global_node_01839 with NodeURI http://bat6315glnxa64:45457/ Shutting down ROS master on http://192.168.0.10:59565.