Улучшайте производительность ROS Используя структуры сообщения

Этот пример демонстрирует использование структур сообщения 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 Рабочий процесс

Для примера, который читает сообщения из 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 не применимы со структурами сообщения, новые функции были введены для обработки сообщений. Этот список включает функции для чтения данных из или записывания данные к специализированным сообщениям.

Изменения поведения

Класс Handle по сравнению с поведением структуры

Важное соображение при преобразовании кода - то, что объекты сообщения 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))

Figure contains an axes. The axes contains an object of type image.

% Message structure
msg = rosmessage("sensor_msgs/Image","DataFormat","struct");
msg.Encoding = 'rgb8';
msg = rosWriteImage(msg,image);
imshow(rosReadImage(msg))
close

Время и длительность Arithmatic

Время 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.