Этот пример демонстрирует использование структур сообщения 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
Структуры сообщения ROS были введены, чтобы улучшать производительность использования сообщений ROS. Каждое сообщение является типом данных структуры 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
rosinit
Launching ROS Core... Done in 0.75371 seconds. Initializing ROS master on http://192.168.0.10:54844. Initializing global node /matlab_global_node_38722 with NodeURI http://bat1071901glnxa64:34693/
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
, specifiy 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 не применимы со структурами сообщения, новые функции были введены для обработки сообщений. Этот список включает функции для чтения данных из или записывания данные к специализированным сообщениям.
Важное соображение при преобразовании кода - то, что объекты сообщения 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
Используя сообщение структуры хороший первый шаг, чтобы ускорить отправку и получение сообщений ROS. Структуры также улучшают производительность для установки, и доступ к данным во вложенном mesages.The в соответствии с кодом демонстрирует отправку нескольких сообщений с вложенными полями.
% 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_38722 with NodeURI http://bat1071901glnxa64:34693/ Shutting down ROS master on http://192.168.0.10:54844.