gz-python
gz-python copied to clipboard
Python bindings for gz-msgs and gz-transport
Gazebo Python Bindings
This project provides Python bindings for gz-msgs and gz-transport.
Building with CMake
Install Gazebo
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.
Install gz-python
Clone this repo into the workspace source directory:
cd ~/gz_ws/src
git clone https://github.com/srmainwaring/gz-python.git
Build with CMake
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 Python environment
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
Usage
gz-msg bindings
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)
gz-transport bindings
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)
Examples
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
Building with Bazel
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.
Installing Bazel
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
Setting up the workspace
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
Build with Bazel
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.
Usage in Bazel builds
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",
],
)
Notes
Protobuf compiler version
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
Protobuf generated Python libraries
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
CI Testing
Repos used to build Gazebo on macOS with Bazel
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 |