Getting Started with ROS on Embedded Systems - User Guide - C++ - Messages
Getting Started with ROS on Embedded Systems RidgeRun documentation is currently under development. |
Getting Started with ROS on Embedded Systems |
---|
ROS on Embedded Systems Basics |
Getting Started |
C++ User Guide |
Examples |
Basic pipelines |
Performance |
Xavier |
Contact Us |
Introduction
This wiki is based on the following ROS page: https://docs.ros.org/en/crystal/Tutorials/Custom-ROS2-Interfaces.html
Messages are the main form of communication in a topic, in which the publisher will push a message of some defined type and the subscriber will use that same kind of message. The communication channel is locked to a topic name and the message type, so knowing and controlling the messages that will flow through a topic is important for a correct client-producer bind.
These messages are normally described in a simplified file that is then used by ROS to generate C++ (and other languages) code with them (which ends up being a simple struct). Message descriptions are located inside the msg/ folder in the package and end with the .msg extension.
Besides messages there is another form of communications that can be very handy, services. These are a way to have different data types in a topic between nodes. That is because in a service we define input types and output types. So on a topic we can have nodes that only check or modify the output or the input, and have an easier way of communication between nodes without the need of new topics for different data types.
The services are defined on a similar way to messages, the file used for that purpose has the .srv extension.
Lastly there are actions, a complex type that can be used to further expand client-consumer architectures, and even add the option for an inner channel to provide feedback between nodes.
Message description
Messages have 2 parts in the .msg file: fields and constants
Message fields
Fields are the data sent inside the message, this is defined in the messages as pairs of key and value, default_value is and optional field that can also be used to set default values:
type1 name1 default_value type2 name2 type3 name3
Types
The types can be primitives:
bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, string, time, duration
They can also be arrays or even another message, but the later is limited to messages defined on the same project. Also as an important note, the arrays and strings can be limited, and even ranges can be used to not hard limit lengths to a single value, like:
int32[] unbounded_integer_array int32[5] five_integers_array int32[<=5] up_to_five_integers_array string string_of_unbounded_size string<=10 up_to_ten_characters_string string[<=5] up_to_five_unbounded_strings string<=10[] unbounded_array_of_strings_up_to_ten_characters_each string<=10[<=5] up_to_five_strings_up_to_ten_characters_each
Names
The names for the fields determine how to get the data on the target language, just as one would do using structs. Field names must be translated to multiple languages, so their names are restricted to the following pattern: [a-zA-Z][a-zA-Z1-9_]*.
Constants
These are convenient fields that can be used to add constants to messages. They have to be defined on UPPERCASE. And they follow the following structure: type NAME=VALUE
Example
uint8 x int16 y -2000 string full_name "John Doe" int32[] samples [-200, -100, 0, 100, 200] int32 CONST_A=123
Service description
Services can be a bit more complex but mainly have 4 parts in the .srv file: constants for inputs, inputs, output and constants for outputs. The definition rules for names and types is the same as messages. The main difference is that services cannot be nested. The inputs are separated from the outputs by '---'.
Example
#request constants int8 FOO=1 int8 BAR=2 #request fields int8 foobar another_pkg/AnotherMessage msg --- #response constants uint32 SECRET=123456 #response fields another_pkg/YetAnotherMessage val CustomMessageDefinedInThisPackage value uint32 an_integer
Action description
Actions are types intended for long and continuous systems, the idea is that they have three sections, input, output and a new feedback channel. The main idea is that the feedback channel can be used for any sort of runtime system correction, task progress, even cancel input processing. These are intended for client-server usage. Underneath they are a mix of different services and topics.
Example
# Define a goal of washing all dishes bool heavy_duty # Spend extra time cleaning --- # Define the result that will be published after the action execution ends. uint32 total_dishes_cleaned --- # Define a feedback message that will be published during action execution. float32 percent_complete uint32 number_dishes_cleaned