A Rust-based bridge that decodes CAN signals (via SocketCAN) and publishes them as ROS2 topics. Configuration is done through XML files, generated from DBC files using the included Python tool (GUI or CLI).
node/ Rust bridge (SocketCAN -> ROS2)
tools/ Python DBC mapper (GUI + CLI)
config/ XML configurations
dbc/ DBC files
test-pipeline/ Integration tests with virtual CAN
docker/ Dockerfile + test scripts
launch/ ROS2 launch file
systemd/ systemd service unit
One command - builds a Docker container with ROS2 + Rust, then runs all tests using your DBC and XML config:
bash docker/test_ros2.sh \
dbc/SensoricSolutionsOMSRace.dbc \
config/config_SensoricSolutionsOMSRace_040326_1846.xmlThis automatically:
- Creates a vcan0 interface (requires sudo)
- Builds the Docker image (ROS2 Humble + Rust + Python)
- Runs all tests inside the container:
- Rust unit tests (26 tests)
- Python unit tests (14 tests)
- Integration test with filter verification
- ROS2 end-to-end test (verifies real topic values)
sudo dnf install -y rust cargo python3-pip python3-devel \
kernel-modules-extra iproute
pip install -r tools/requirements.txt pytest
cd node && cargo build --releasesudo apt install -y rustc cargo python3-pip iproute2
pip install -r tools/requirements.txt pytest
cd node && cargo build --releaseROS2 Humble only supports Ubuntu natively. On Fedora, use Docker:
sudo dnf install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Log out and back in, then:
docker build -t can-ros2-bridge -f docker/Dockerfile .bash docker/test_ros2.sh <dbc_file> <xml_config>Builds the Docker image, creates vcan, runs all 4 test stages in the container.
cd node && cargo test26 tests: config parsing, signal decoding (signed/unsigned), encode/decode roundtrips, edge cases.
cd tools && python3 -m pytest test_parser.py test_xml_export.py -v14 tests: DBC parser, XML export, roundtrip compatibility.
Verifies the bridge only passes through signals defined in the XML config.
# Create vcan (once)
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
# Filter test: only VelAndAngPOI + AccHor allowed, rest blocked
python3 test-pipeline/run_integration.py \
dbc/SensoricSolutionsOMSRace.dbc \
config/test_partial.xmlChecks 4 criteria:
- Allowed signals must appear in output
- Non-configured messages must not pass through
- Non-configured signals within allowed messages must not pass through
- Unknown CAN IDs are ignored
Verifies signals actually arrive as ROS2 topics with correct values.
docker run --rm --privileged --network host \
-v $(pwd):/ws can-ros2-bridge bash -c "
source /opt/ros/humble/setup.bash
ip link show vcan0 || (ip link add dev vcan0 type vcan && ip link set up vcan0)
python3 /ws/test-pipeline/run_ros2_test.py \
/ws/dbc/SensoricSolutionsOMSRace.dbc \
/ws/config/config_SensoricSolutionsOMSRace_040326_1846.xml
"Checks:
- Expected ROS2 topics exist (
ros2 topic list) - Each topic delivers the correct value (
ros2 topic echo --once) - Blocked messages produce no topics
python3 tools/cli.py guiOpens a Qt window: load a DBC file, select signals via checkboxes, edit ROS2 topic names, export as XML.
python3 tools/cli.py map dbc/SensoricSolutionsOMSRace.dbc -o config/my_config.xmlcan-ros2-bridge --config config/my_config.xml --can-interface can0Options:
--config/-c- path to XML config (default:config.xml)--can-interface/-i- CAN interface (default:can0)
Download the .deb or .rpm from the Releases page.
sudo dpkg -i can-ros2-bridge_*_amd64.debsudo rpm -i can-ros2-bridge-*.x86_64.rpmThis installs:
/usr/bin/can-ros2-bridge- the bridge binary/etc/can-ros2-bridge/config.xml- example config/lib/systemd/system/can_ros_bridge.service- systemd unit
The bridge runs as a standalone process alongside your ROS2 nodes. It publishes std_msgs/msg/Float64 on the topics you defined in your XML config.
# From release package
sudo dpkg -i can-ros2-bridge_*_amd64.deb # Ubuntu
sudo rpm -i can-ros2-bridge-*.x86_64.rpm # Fedora
# Or build from source
cd node && cargo build --release --features ros2
sudo cp target/release/can-ros2-bridge /usr/bin/Use the GUI to select which CAN signals you need and map them to topic names:
python3 tools/cli.py gui
# Save as /etc/can-ros2-bridge/config.xmlExample: if your config maps VelXPoi to /vel/x, the bridge publishes a Float64 on /vel/x every time a CAN frame with that signal arrives.
In your existing ROS2 project, include the bridge in your launch file:
from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
def generate_launch_description():
# Start the CAN-ROS2 bridge
bridge = ExecuteProcess(
cmd=[
"can-ros2-bridge",
"--config", "/etc/can-ros2-bridge/config.xml",
"--can-interface", "can0",
],
output="screen",
)
# Your own node that subscribes to the CAN signals
my_node = Node(
package="my_package",
executable="my_node",
)
return LaunchDescription([bridge, my_node])The bridge publishes std_msgs/msg/Float64 on each configured topic. Subscribe in your node like any other ROS2 topic:
Python:
from std_msgs.msg import Float64
class MyNode(Node):
def __init__(self):
super().__init__("my_node")
# Subscribe to CAN signals published by the bridge
self.sub_vel = self.create_subscription(
Float64, "/vel/x", self.on_velocity, 10)
self.sub_acc = self.create_subscription(
Float64, "/acc/x", self.on_acceleration, 10)
def on_velocity(self, msg: Float64):
self.get_logger().info(f"Velocity X: {msg.data} km/h")
def on_acceleration(self, msg: Float64):
self.get_logger().info(f"Acceleration X: {msg.data} m/s^2")C++:
#include <std_msgs/msg/float64.hpp>
class MyNode : public rclcpp::Node {
public:
MyNode() : Node("my_node") {
sub_vel_ = create_subscription<std_msgs::msg::Float64>(
"/vel/x", 10,
[this](std_msgs::msg::Float64::SharedPtr msg) {
RCLCPP_INFO(get_logger(), "Velocity X: %.2f km/h", msg->data);
});
}
private:
rclcpp::Subscription<std_msgs::msg::Float64>::SharedPtr sub_vel_;
};For production use, run the bridge as a background service:
# Edit config and interface if needed
sudo vim /lib/systemd/system/can_ros_bridge.service
# Enable and start
sudo systemctl enable --now can_ros_bridge
# Check logs
sudo journalctl -u can_ros_bridge -fThe bridge will auto-restart on failure and start on boot.
To see which topics the bridge publishes, check your XML config or run:
ros2 topic list | grep -v /parameter_events
ros2 topic echo --once /vel/x # read a single value
ros2 topic hz /vel/x # check publish rate<bridge>
<message id="1536" name="VelAndAngPOI">
<signal name="VelXPoi" start_bit="0" length="16"
byte_order="little_endian" signed="true"
scale="0.02" offset="0" topic="/vel/x" />
</message>
</bridge>Signal attributes:
start_bit/length- position in CAN framebyte_order-little_endianorbig_endiansigned-truefor signed valuesscale/offset- physical value = raw * scale + offsettopic- ROS2 topic name (publishesstd_msgs/msg/Float64)
MIT License - see LICENSE.