exponenta event banner

Повышение производительности АФК с использованием структур сообщений

В этом примере показано использование структур сообщений 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
rosinit
Launching 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

Пример чтения сообщений из 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))

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

Арифматический интервал времени и длительности

Структуры сообщений времени и длительности 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.