Overview
This tutorial describes how Gazebo messages are generated and you can create custom messages that can be used with the simulator and command line tools.
Gazebo messages use Protobuf to define the structures that can be easily serialized for use in communication through the Gazebo software stack. Protobuf is a language-neutral framework for serializing structured data, with the advantage of generating native language bindings so that messages are easily used from your language of choice.
Message Definitions
File structure
Gazebo message definitions are stored in the proto folder.
Messages may additionally belong to a package
, which allows for convenient namespacing or grouping of common messages. Currently, all gz-msgs
definitions reside in the gz.msgs
package, which generates the corresponding gz::msgs
namespace in C++ and gz.msgs
in Python.
Proto files
Messages are defined in files with the extension .proto
.
The definitions use the proto
language, which has two major version, of which Gazebo uses the proto3
version.
The full language guide for the proto
language is available here
A typical message definition looks something like this:
Note that each field has a corresponding field number. Each field must be given an ID between 1
and 536,870,911
, with the following restrctions (from the language guide):
- The given number must be unique among all fields for that message.
- Field numbers
19,000
to19,999
are reserved for the Protocol Buffers implementation. The protocol buffer compiler will complain if you use one of these reserved field numbers in your message. - You cannot use any previously reserved field numbers or any field numbers that have been allocated to extensions.
The Message Generation Pipeline
Once the proto messages have been defined, there are several steps that take place to make the messages usable across the Gazebo stack.
To begin with, each message definition is passed through the gz_msgs_protoc
function:
For each file:
gz_msgs_protoc
accepts the arguments necessary to find all the supporting scripts and executables:gz_msgs_generate.py
- Top-level python script entrypoint for generating messages.protoc
- The protobuf compilergz-msgs-protoc-compiler
- The Gazebo messages extension compiler.
- The protobuf compiler generates
.pb.c
,.pb.h
, and_pb2.py
file for each message found in the.proto
file. - The generation python script moves the generated
.pb.h
file into adetails/
directory to hide some of the protobuf implementation from downstream users. - The
gz-msgs-protoc-compiler
generates a new.pb.h
file with Gazebo-specific definitions gz_msgs_generate.py
additionally generates a.pbindex
file to be used by later steps in the process.
After individual message files have been generated, all of the definitions are grouped and processed via gz_msgs_factory
For the collection, gz_msgs_factory
generates:
- A
register.cc
file that can be used to statically register all of the messages passed as arguments. - A
MessageTypes.hh
file that enumerates all messages available in the collection. - A
.gz_desc
file that allows the messages to be dynamically loaded by the various Gazebo tools.
Custom Message Generation
Now that we understand the components of the message generation pipeline, we can use them in our own custom package.
The code for this example can be found in the gz-msgs
repository, in the examples/generating_custom_msgs
folder.
The cmake
functionality is exported from the gz-msgs
library, via the gz-cmake
extras
functionality. To make the functions available, simply find_package(gz-msgs10)
in your CMakeLists.txt
:
Next, create a directory for your custom message definitions:
Give the message files some content:
proto/gz/custom_msgs/foo.proto
:
proto/gz/custom_msgs/bar.proto
:
proto/gz/custom_msgs/baz.proto
:
Then, back in the CMakeLists.txt
file, add following lines to generate the message library:
In order to reduce the amount of edits needed upon a version change of gz-msgs
, it is common to:
- Define a variable
GZ_MSGS_VER
, holding the version number:find_package(gz-msgs10 REQUIRED)set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) - And change the dependency line in above code block to: DEPENDENCIES gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER}
Using Custom messages
There are two primary ways that Gazebo messages are used as part of an application. Messages are either known at compile time and linked into the application, or they are loaded dynamically at runtime.
Compile time message use
When messages are known at compile-time, they can be directly used. This allow for the messages to be used without any sort of reflection/introspection.
Include the headers and interact with the messages using the generated bindings according to the language guide
The corresponding CMake for this:
Run time message use
Alternatively, there may be cases where all message definitions are not known at the time that the executable/library of interest is being built. A common case for this is with command line tools, where only a limited set of messages are available at the time the tool is built, and more user-defined messages are generated later.
For example, when custom messages are generated, they aren't initially visible to the gz msgs
or gz topic
command line tools:
To use the new messages, point the GZ_DESCRIPTOR_PATH
environment variable to the location of the build
folder or where you have choosen to install the .gz_desc
file: