Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ build:clang --linkopt="-fuse-ld=lld"
# Work around https://github.com/bazelbuild/rules_rust/issues/2125
build --action_env=CARGO_BAZEL_REPIN=1

common --enable_bzlmod --noenable_workspace

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this.


# Load any settings specific to the current user.
# user.bazelrc should appear in .gitignore so that settings are not shared with
# team members. This needs to be last statement in this config,
Expand Down
2 changes: 2 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bazel_dep(name = "libyaml", version = "0.2.5")
bazel_dep(name = "lz4", version = "1.10.0.bcr.1")
bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "protobuf", version = "33.5", repo_name = "com_google_protobuf")
bazel_dep(name = "pybind11_bazel", version = "3.0.0")
bazel_dep(name = "readerwriterqueue", version = "1.0.6")
bazel_dep(name = "rules_cc", version = "0.2.16")
Expand Down Expand Up @@ -150,6 +151,7 @@ use_repo(
"foxglove_bridge",
"iceoryx",
"osrf_pycommon",
"proto2ros",
"ros2",
"ros2_ament_index",
"ros2_class_loader",
Expand Down
4 changes: 4 additions & 0 deletions examples/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ local_path_override(
path = "..",
)

bazel_dep(name = "protobuf", version = "33.5", repo_name = "com_google_protobuf")
bazel_dep(name = "rules_cc", version = "0.2.9")
bazel_dep(name = "rules_python", version = "1.6.3")
bazel_dep(name = "rules_rust", version = "0.63.0")
Expand All @@ -29,6 +30,8 @@ rules_ros2_non_module_deps = use_extension("@com_github_mvukov_rules_ros2//ros2:
use_repo(
rules_ros2_non_module_deps,
# Check the rules_ros2 root MODULE.bazel for a full list of available non-module repos
# Required for proto2ros:
"proto2ros",
# Required for bazel test:
"ros2_common_interfaces",
"ros2_geometry2",
Expand All @@ -44,5 +47,6 @@ use_repo(
"ros2cli",
# Required for bazel run:
"ros2_rclpy",
# Required for proto2ros:
"ros2_rosidl",
)
59 changes: 59 additions & 0 deletions examples/proto_to_ros/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
load("@com_github_mvukov_rules_ros2//ros2:cc_defs.bzl", "ros2_cpp_binary")
load("@com_github_mvukov_rules_ros2//ros2:interfaces.bzl", "cpp_ros2_interface_library", "py_ros2_interface_library", "ros2_interface_library")
load("@com_github_mvukov_rules_ros2//ros2:launch.bzl", "ros2_launch")
load("@com_github_mvukov_rules_ros2//ros2:proto2ros.bzl", "proto2ros_message")
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_python//python:defs.bzl", "py_binary")

proto_library(
name = "chatter_proto",
srcs = ["message.proto"],
)

proto2ros_message(
name = "chatter_msgs",
msg_names = ["ChatterMessage"],
proto_library = ":chatter_proto",
)

ros2_interface_library(
name = "chatter_interfaces",
srcs = [":chatter_msgs"],
)

cpp_ros2_interface_library(
name = "chatter_interfaces_cpp",
deps = [":chatter_interfaces"],
)

py_ros2_interface_library(
name = "chatter_interfaces_py",
deps = [":chatter_interfaces"],
)

py_binary(
name = "talker",
srcs = ["talker.py"],
deps = [
":chatter_interfaces_py",
"@ros2_rclpy//:rclpy",
],
)

ros2_cpp_binary(
name = "listener",
srcs = ["listener.cc"],
deps = [
":chatter_interfaces_cpp",
"@ros2_rclcpp//:rclcpp",
],
)

ros2_launch(
name = "proto_to_ros",
launch_file = "proto_to_ros.py",
nodes = [
":listener",
":talker",
],
)
63 changes: 63 additions & 0 deletions examples/proto_to_ros/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Proto2ros Example

This example demonstrates how to use `proto2ros` to convert Protocol Buffer
definitions to ROS 2 messages, integrated with working ROS 2 nodes.

## Overview

The example shows:

- Converting a `.proto` file to ROS 2 `.msg` format
- Using the `proto2ros_message` rule
- **Python talker node** publishing messages on the `chatter` topic
- **C++ listener node** subscribing to messages on the `chatter` topic

## Files

- `message.proto` - Protocol Buffer message definition
- `talker.py` - Python publisher node
- `listener.cc` - C++ subscriber node
- `proto2ros.py` - Launch file to run both nodes
- `BUILD.bazel` - Bazel build configuration

## Usage

### In BUILD.bazel

```python
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@com_github_mvukov_rules_ros2//ros2:proto2ros.bzl", "proto2ros_message")

proto_library(
name = "chatter_proto",
srcs = ["message.proto"],
)

proto2ros_message(
name = "chatter_msgs",
msg_names = ["ChatterMessage"],
proto_library = ":chatter_proto",
)

ros2_interface_library(
name = "chatter_interfaces",
srcs = [":chatter_msgs"],
)
```

### Building

```bash
bazel build //examples/proto_to_ros:all
bazel build //examples/proto_to_ros:talker # Python talker node
bazel build //examples/proto_to_ros:listener # C++ listener node
bazel build //examples/proto_to_ros:proto_to_ros # Launch file
```

### Running

Run the demo to see the Python talker and C++ listener communicate:

```bash
bazel run //examples/proto_to_ros:proto_to_ros
```
46 changes: 46 additions & 0 deletions examples/proto_to_ros/listener.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2026 Milan Vukov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <memory>

#include "chatter_interfaces/msg/chatter_message.hpp"
#include "rclcpp/rclcpp.hpp"

/**
* C++ listener node using proto2ros generated messages.
*/
class ProtoChatterListener : public rclcpp::Node {
public:
ProtoChatterListener() : Node("proto_chatter_listener") {
subscription_ =
create_subscription<chatter_interfaces::msg::ChatterMessage>(
"chatter", 10,
[this](chatter_interfaces::msg::ChatterMessage::UniquePtr msg) {
RCLCPP_INFO(get_logger(),
"I heard: data='%s', timestamp=%ld",
msg->data.c_str(), msg->timestamp);
});
}

private:
rclcpp::Subscription<chatter_interfaces::msg::ChatterMessage>::SharedPtr
subscription_;
};

int main(int argc, char* argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ProtoChatterListener>());
rclcpp::shutdown();
return 0;
}
8 changes: 8 additions & 0 deletions examples/proto_to_ros/message.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";

package chatter;

message ChatterMessage {
string data = 1;
int64 timestamp = 2;
}
35 changes: 35 additions & 0 deletions examples/proto_to_ros/proto_to_ros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2026 Milan Vukov
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Launch proto2ros chatter demo: Python talker and C++ listener."""

import launch
import launch_ros.actions


def generate_launch_description():
"""Launch a Python talker and C++ listener using proto2ros messages."""
return launch.LaunchDescription(
[
launch_ros.actions.Node(
executable="proto_to_ros/talker",
output="screen",
name="proto_talker",
),
launch_ros.actions.Node(
executable="proto_to_ros/listener",
output="screen",
name="proto_listener",
),
]
)
61 changes: 61 additions & 0 deletions examples/proto_to_ros/talker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2026 Milan Vukov
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Python talker node using proto2ros generated messages."""

import time

import rclpy
from chatter_interfaces.msg import ChatterMessage
from rclpy import node


class ProtoChatterTalker(node.Node):
"""Publishes ChatterMessage messages on the 'chatter' topic."""

def __init__(self):
super().__init__("proto_chatter_talker")
self.publisher_ = self.create_publisher(ChatterMessage, "chatter", 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0

def timer_callback(self):
"""Publish a ChatterMessage with data and timestamp."""
msg = ChatterMessage()
msg.data = f"Hello from proto2ros: {self.i}"
msg.timestamp = int(time.time() * 1000) # milliseconds since epoch
self.publisher_.publish(msg)
self.get_logger().info(
f'Publishing: data="{msg.data}", timestamp={msg.timestamp}'
)
self.i += 1


def main():
rclpy.init()

talker = ProtoChatterTalker()

try:
rclpy.spin(talker)
except KeyboardInterrupt:
pass
finally:
talker.destroy_node()
rclpy.shutdown()


if __name__ == "__main__":
main()
39 changes: 39 additions & 0 deletions repositories/proto2ros.BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Builds proto2ros from bdaiinstitute/proto2ros repository."""

load("@com_google_protobuf//bazel:py_proto_library.bzl", "py_proto_library")
load("@rules_python//python:defs.bzl", "py_binary", "py_library")
load("@rules_ros2_pip_deps//:requirements.bzl", "requirement")

py_proto_library(
name = "protobuf_runtime",
deps = ["@com_google_protobuf//:any_proto"],
)

# Core proto2ros Python library
py_library(
name = "proto2ros_lib",
srcs = glob(["proto2ros/proto2ros/**/*.py"]),
data = glob([
"proto2ros/proto2ros/configuration/*.yaml",
"proto2ros/proto2ros/output/templates/**/*",
]),
imports = ["proto2ros"],
deps = [
":protobuf_runtime",
"@ros2_rosidl//:rosidl_adapter_lib",
requirement("inflection"),
requirement("jinja2"),
requirement("networkx"),
requirement("numpy"),
requirement("pyyaml"),
],
)

# Main generation CLI binary
py_binary(
name = "generate",
srcs = ["proto2ros/proto2ros/cli/generate.py"],
main = "proto2ros/proto2ros/cli/generate.py",
visibility = ["//visibility:public"],
deps = [":proto2ros_lib"],
)
11 changes: 11 additions & 0 deletions repositories/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,14 @@ def ros2_repositories():
strip_prefix = "rcl_logging_syslog-e63257f2d5ca693f286bbcedf2b23720675b7f73",
urls = ["https://github.com/fujitatomoya/rcl_logging_syslog/archive/e63257f2d5ca693f286bbcedf2b23720675b7f73.zip"],
)

def proto2ros_repository():
"""Fetches proto2ros from GitHub."""
maybe(
http_archive,
name = "proto2ros",
build_file = "@com_github_mvukov_rules_ros2//repositories:proto2ros.BUILD.bazel",
sha256 = "31fbebb35056266f2959fda4291862f063b21ea29a8209c5d87cde49443e71bd",
strip_prefix = "proto2ros-0cc24714f99d1538d20fc25c9312d6199c3ce662",
url = "https://github.com/bdaiinstitute/proto2ros/archive/0cc24714f99d1538d20fc25c9312d6199c3ce662.tar.gz",
)
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
catkin_pkg
coverage
empy==3.3.*
inflection
jinja2
lark-parser
networkx
numpy~=1.23
packaging
psutil
Expand Down
Loading