This project provides Python bindings for gz-msgs
and gz-transport
.
Follow the installation instructions for Gazebo Garden.
This project depends directly on gz-msgs
and gz-transport
. These may be either available as system installs or in a source install in a local workspace folder which we assume is ~/gz_ws
.
Clone this repo into the workspace source directory:
cd ~/gz_ws/src
git clone https://github.com/srmainwaring/gz-python.git
Currently our use of pybind11_protobuf
requires the protobuf compiler to generate the Python implementation of the Python protobuf bindings. This is configured with the environment variable:
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
Set the environment variable determing the Gazebo version.
The default is garden
:
export GAZEBO_VERSION=garden
Then create a build directory, configure and make:
mkdir -p ~/gz_ws/src/gz-python/build
cd ~/gz_ws/src/gz-python/build
cmake ..
make
Update the PYTHONPATH to include the location of the extension modules and the generated Python protobuf bindings.
cd ~/gz_ws/src/gz-python
export PYTHONPATH=${PYTHONPATH}:$(pwd)/build/python
The Python bindings for gz-msgs
are the standard generated protobuf code
for Python. For example gz-msgs/proto/gz/msgs/time.proto
may be used as follows:
# example.py
from gz.msgs.time_pb2 import Time
msg = Time()
msg.sec = 15
msg.nsec = 21
print(msg)
The Python bindings for gz-transport
are contained in a module called transport
.
The object naming and usage for the most part follows the C++ interface,
so the C++ Gazebo Tutorials are a good guide on how to use the library.
Publish:
from gz.msgs.stringmsg_pb2 import StringMsg
from gz.transport import AdvertiseMessageOptions
from gz.transport import Node
# Create a transport node
node = Node()
# Advertise a topic
topic = "/foo"
msg_type_name = StringMsg.DESCRIPTOR.full_name
pub_options = AdvertiseMessageOptions()
pub = node.advertise(topic, msg_type_name, pub_options)
# Publish a message
msg = StringMsg()
msg.data = "hello"
pub.publish(msg)
Subscribe:
import time
import typing
from gz.msgs.stringmsg_pb2 import StringMsg
from gz.transport import SubscribeOptions
from gz.transport import Node
def cb(msg: StringMsg) -> None:
print("Msg: [{}] from Python".format(msg.data))
# Create a transport node
node = Node()
# Subscribe to a topic by registering a callback
topic = "/foo"
msg_type_name = StringMsg.DESCRIPTOR.full_name
sub_options = SubscribeOptions()
node.subscribe(topic, cb, msg_type_name, sub_options)
A number of examples in C++ and Python are provided. In the following we suppose that
they are being run from the project directory ~/gz_ws/src/gz-python
.
src/msg_example.cc
is a copy of the gz-msgs
tutorial example:
$ ./build/msg_example
Point1:
x: 1
y: 3
z: 5
Point2:
x: 2
y: 4
z: 6
src/publisher.cc
and src/subscriber.cc
is copied from the gz-transport
messages tutorial example.
From terminal 1:
$ ./build/publisher
Publishing hello on topic [/foo]
Publishing hello on topic [/foo]
...
From terminal 2:
$ ./build/subscriber
Msg: hello
Msg: hello
...
src/rover_publisher.cc
and src/rover_subscriber.cc
comprise another publish / subscribe
example that publishes the pose and twist of a rover moving in a circle with constant
angular velocity.
From terminal 1:
$ ./build/rover_publisher
Publishing pose on topic [/pose], twist on topic [/twist]
Publishing pose on topic [/pose], twist on topic [/twist]
...
From terminal 2:
$ ./build/rover_subscriber
header {
stamp {
sec: 10
nsec: 45483925
}
}
name: "base_link"
id: 20
position {
x: 2.7015115293406988
y: 4.2073549240394827
}
orientation {
z: 0.479425538604203
w: 0.87758256189037276
}
header {
stamp {
sec: 10
nsec: 45483925
}
}
linear {
x: -0.42073549240394825
y: 0.27015115293406988
}
angular {
z: 0.1
}
python/msgs_example.py
is a Python example that uses the generated Python protobuf libraries for gz-msgs
:
$ ./python/msgs_example.py
Gazebo Protobuf Example
proto api type: python
----------------------------------------
<class 'gz.msgs.time_pb2.Time'>
sec: 15
nsec: 21
...
python/publisher.py
is a Python version of the C++ src/publisher.cc
described above.
You can listen to the messages using the C++ subscriber as before.
From terminal 1:
$ ./python/publisher.py
Advertising gz.msgs.StringMsg on topic [/foo]
Publishing hello on topic [/foo]
Publishing hello on topic [/foo]
...
From terminal 2:
$ ./python/subscriber.py
Subscribing to type gz.msgs.StringMsg on topic [/foo]
Msg: [hello] from Python
Msg: [hello] from Python
...
python/rover_publisher.py
is a Python version of the C++ src/rover_publisher.cc
example.
From terminal 1:
./python/rover_publisher.py
Advertising gz.msgs.Pose on topic [/pose]
Advertising gz.msgs.Twist on topic [/twist]
Publishing pose on topic [/pose], twist on topic [/twist]
Publishing pose on topic [/pose], twist on topic [/twist]
...
From terminal 2:
./python/rover_subscriber.py
Subscribing to type gz.msgs.Pose on topic [/pose]
Subscribing to type gz.msgs.Twist on topic [/twist]
header {
stamp {
sec: 2
nsec: 511006000
}
}
name: "base_link"
id: 5
position {
x: 4.900332889206208
y: 0.9933466539753061
}
orientation {
z: 0.09983341664682815
w: 0.9950041652780257
}
header {
stamp {
sec: 2
nsec: 511006000
}
}
linear {
x: -0.09933466539753061
y: 0.4900332889206208
}
angular {
z: 0.1
}
...
python/gz_topic_list.py
is a Python version of the command gz topic -l
$ ./python/gz_topic_list.py
/foo
python/gz_topic_info.py
is a Python version of the command gz topic -i -t /foo
$ ./python/gz_topic_info.py -t /foo
Publishers [Address, Message Type]:
tcp://127.0.0.1:60328, gz.msgs.StringMsg
python/gz_topic_echo.py
is a Python version of the command gz topic -e -t /foo
$ ./python/gz_topic_echo.py -t /foo
Subscribing to topic [/foo]
data: "hello"
data: "hello"
data: "hello"
python/gz_service_list.py
is a Python version of the command gz service -l
$ ./python/gz_service_list.py
/gazebo/resource_paths/add
/gazebo/resource_paths/get
/gazebo/worlds
/gui/camera/view_control
/gui/follow
...
python/gz_service_info.py
is a Python version of the command gz service -i -s /gazebo/worlds
$ ./python/gz_service_info.py -s /gazebo/worlds
Service providers [Address, Request Message Type, Response Message Type]:
tcp://127.0.0.1:63657, gz.msgs.Empty, gz.msgs.StringMsg_V
On macOS the project may also be built with Bazel. This is experimental and depends upon
a modified version of the Bazel build rules for Gazebo in the gz-bazel
project.
On macOS Bazel can be installed with brew
:
brew install bazel
The Google protocol buffers compiler version 3.19
is also required and can be installed with:
brew install protobuf
Make a directory to contain this package and all the Gazebo packages and dependencies:
mkdir ~/gz/
cd ~/gz/
Clone each of the packages using the bazel.repos
file and vcstool. The forked version of gz-bazel
contains modified build rules and repo references for macOS.
wget https://raw.githubusercontent.com/srmainwaring/gz-bazel/bazel-macos/example/bazel.repos
vcs import . < bazel.repos
Next, symlink the following files into the workspace directory:
cd ~/gz
ln -sf ./gz_bazel/example/WORKSPACE.example ./WORKSPACE
ln -sf ./gz_bazel/example/BUILD.example ./BUILD.bazel
ln -sf ./gz_bazel/example/bazelrc.example ./.bazelrc
ln -sf ./gz_python/gz-msgs9.BUILD ./gz-msgs9.BUILD
Finally, pybind11_protobuf
requires a patch to the protobuf archive which must be available in the subdirectory ~/gz/external
. It must be copied rather than symlinked:
cd ~/gz
mkdir external
cp ./gz_python/external/com_google_protobuf_build.patch external
Currently our use of pybind11_protobuf
requires the Python implementation of the Python protobuf generator. This is configured with the environment variable:
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
Then build the examples with:
bazel build //gz_python:all
bazel build //gz_python/python:all
Note: on macOS not all Gazebo projects will build with Bazel, so the command:
bazel build //...
will result in a number of errors.
The Bazel build file gz-msgs9.BUILD
defines targets for a selection of messages.
For example the targets for proto/gz/msgs/time.proto
are:
# proto_library
@gz-msgs9//:time_proto
# cpp_proto_library
@gz-msgs9//:time_cc_proto
# python_proto_library
@gz-msgs9//:time_py_pb2
To use the message bindings for C++ targets:
# BUILD.bazel
cc_binary(
name = "main",
srcs = ["main.cc"],
deps = [
"@gz-msgs9//:time_cc_proto",
],
)
To use the bindings for Python targets:
# BUILD.bazel
py_binary(
name = "example",
srcs = ["example.py"],
data = [
"@com_google_protobuf//:proto_api",
],
deps = [
"@gz-msgs9//:time_py_pb2",
],
)
The project depends on protoc
version 3.19.1
. You must ensure that the version of protoc
installed by brew
matches (otherwise the examples will segfault).
$ protoc --version
libprotoc 3.19.1
The brew
installation of protobuf
defaults to use the C++ implementation
of the generated Python protobuf library. When using this with pybind11_protobuf
there are some cases when C++ wrapped protobuf objects returned from an extension module are not
recognised as their Python equivalents. The Python implementation will work in these cases
and to enable this set the following environment variable before building the project:
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
The table summarises the dependencies (repo and branch) on the Gazebo libraries.
library | repo | branch | build | test |
---|---|---|---|---|
gz_python | https://github.com/srmainwaring/gz-python | main | pass | - |
gz-bazel | https://github.com/srmainwaring/gz-bazel | bazel-macos | pass | pass |
gz-math | https://github.com/srmainwaring/gz-math | bazel-macos/gz-math6 | pass | pass |
gz-utils | https://github.com/srmainwaring/gz-utils | bazel-macos/gz-utils1 | pass | pass |
gz-common | https://github.com/srmainwaring/gz-common | bazel-macos/gz-common4 | pass | pass |
gz-msgs | https://github.com/srmainwaring/gz-msgs | bazel-macos/gz-msgs9 | pass | pass |
sdformat | https://github.com/gazebosim/sdformat | bazel-sdf10 | pass | pass |
gz-plugin | https://github.com/srmainwaring/gz-plugin | bazel-macos/gz-plugin1 | pass | pass |
gz-transport | https://github.com/srmainwaring/gz-transport | bazel-macos/gz-transport12 | pass | 1 fail |
gz-physics | https://github.com/srmainwaring/gz-physics | bazel-macos/gz-physics5 | pass | pass |
gz-tools | https://github.com/srmainwaring/gz-tools | bazel-macos/gz-tools1 | partial | - |
gz-rendering | https://github.com/gazebosim/gz-rendering | bazel-rendering4 | fail | fail |
gz-gui | https://github.com/gazebosim/gz-gui | bazel-gui4 | fail | fail |
gz-sensors | https://github.com/gazebosim/gz-sensors | bazel-sensors4 | fail | fail |
gz-gazebo | https://github.com/gazebosim/gz-gazebo | bazel-gazebo4 | fail | fail |