Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.

norospy

👉 ROS without ROS 👈

Disclaimer: This project is still a work in progress. Do not use this in production.

A Docker setup and ROS-free Python client for streaming data from ROS 1 without requiring an actual ROS distribution to be installed. This way, you can encapsulate all ROS-related inside a Docker container, while not being forced to run your actual application code on an ancient (pre-22.04) OS. Includes functionality to bridge from CARLA via ROS / Foxglove to Python, but can also be used stand-alone and without CARLA dependencies.

The purpose of the Python library is conceptually similar to libraries like goroslib, but different in that it doesn't connect to ROS directly, but via websockets. This way, we didn't have to implement communication (incl. TCPROS, UDPROS, the XML-RPC API, etc.) all by ourselves.

Use case: Say you got code (e.g. an object detection or planning algorithm), that communicates with sensors and / or actors via (outdated and deprecated) ROS 1.x. For testing, you want to hook it up with CARLA in addition. First, you'll need CARLA ROS bridge streaming CARLA data from and to ROS. Second, you'll need a way to subscribe to those ROS topics without having to install ROS 1 on your system. Using ROS Foxglove bridge, you'll get a simple websocket interface, that you can read from and write to using any generic websocket client in any programming language. This repo provides an according Docker image and a small Python client that implements the basics of Foxglove's WS subprotocol. No ROS whatsoever.

Why Foxglove bridge? Because much more efficient for large, high-frequency data than rosbridge_suite.

Usage

Step 1: Build Docker image

# For standalone use
docker build -t localhost/foxglove-ros-bridge .

# For use with CARLA
docker build -t localhost/carla-foxglove-ros-bridge -f carla.Dockerfile .

Optionally, you can pass --build-arg CARLA_VERSION=0.9.15 to use a different CARLA distro and --build-arg ROS_DISTRO=noetic to use a different ROS distro.

PLease note: the image will not include a full CARLA distribution. Instead, it is assumed that you have CARLA running on your host machine (or inside another, external Docker container).

Step 2: Run a container

docker run -t -d \
  --net=host \
  --name carla-foxglove-ros-bridge \
  -e ROS_IP=127.0.0.1 \
  -e ROS_HOSTNAME=localhost \
  localhost/carla-foxglove-ros-bridge

This will start a ROS master, an instance of the CARLA bridge (when using CARLA Docker setup) and an instance of the websocket bridge (on port 8765).

Optionally, you can pass -e CARLA_HOST=192.168.178.10 and -e CARLA_PORT=2000 to connect to a CARLA instance running on a different host.

If you want to start only the Foxglove bridge without a ROS core and instead connect an external, already running ROS system, pass -e ROS_MASTER_URI=... and -e ROS_IP=....

Step 3: Send and receive data from Python

First, you'll have to install this package like so:

pip install .

Now, you can start sending and receiving data.

from norospy import ROSFoxgloveClient


# Callback for receiving images (used later)
def on_image(msg, ts):
    with open(f'/tmp/{ts}.jpg') as f:
        f.write(msg.data)


# Create a new client and start it
client = ROSFoxgloveClient('ws://localhost:8765')
client.run_background()

# Use case 1: Subscribe to data (e.g. images from CARLA, in this example)
client.subscribe('/carla/ego_vehicle/rgb_front/image/compressed', 'sensor_msgs/CompressedImage', on_image)

# Use case 2: Publish data
client.advertise('/debug_01', 'geometry_msgs/Point')  # afterwards, wait a bit until advertisement is in place (to do: handle this automatically) 
client.publish_json('/debug_01', 'geometry_msgs/Point', {
    'x': 10,
    'y': 20,
    'z': 30,
})

# Tear down
client.close()

Tip 1: Loading additional message defs

By default, the following message definitions are available:

actionlib_msgs
diagnostic_msgs
geometry_msgs
nav_msgs
sensor_msgs
shape_msgs
std_msgs
trajectory_msgs
visualization_msgs

If you want to load additional, custom definitions, do it like so:

client = ROSFoxgloveClient('ws://localhost:8765', msg_search_paths=[ 'path/to/your/msgs' ])

Tip 2: Keeping script alive

If you're running this in one-off Python script (instead of a Jupyter notebook or something) and want your script to stay alive infinitely, here's a way to do so:

try:
    # client initialization
    # your custom code
    signal.pause()  # import signal
finally:
    client.close()

By the way, you can view ROS data in Foxglove Studio (rviz, but cool) by subscribing to ws://localhost:8765.

Usage with JavaScript / web browser

You can also subscribe to ROS topics from a browser- (or NodeJS)-based web application. In this case, you'll only use the Docker setup provided in this project and won't need the Python library part.

Prerequisites

Install JavaScript dependencies

npm install @foxglove/rosmsg
npm install @foxglove/rosmsg-serialization
npm install @foxglove/ws-protocol

If you're not on NodeJS and are not using a module bundler (Vite, Webpack, Rollup, ...), you can also include these libraries manually via <script> tags.

Gather ROS messages

# download message definitions
git clone git@github.com:ros/common_msgs.git

# compile all into one javascript file
python scripts/collect_ros_msgs.py ./common_msgs/ --exclude nav_msgs --template scripts/ros_msgs.tpl.js --target ros_msgs.gen.js

Example Code

See examples/subscribe_web.js.

Running your own nodes

There are two ways of communicating via ROS. One is via the websocket bridge (using the Python library provided here). The second one is, of course, still rolling your own ROS nodes and connecting them to the master.

To run a node, you can either do so in an ad-hoc way by opening a shell inside the container (docker exec -it <container-name> bash), cloning your custom code in there (or mounting it as a volume beforehand), sourcing the ROS environment and then starting it via rosrun. However, this is ephemeral. As soon as you recreate the container, your code will be gone.

Thus, the second (and probably preferable) option is to run your node in a stand-alone fashion inside a separate container.

Custom packages

If you want to include custom ROS packages (e.g. custom message definitions), please them into pkgs/. They will be compiled as part of a catkin workspace and sourced before launching ROS.

Advanced topics

Spawning objects and sensors via CARLA bridge

CARLA bridge supports to spawn obstacles and sensors. Also, to receive global information like TFs between sensors, a global actos list, obstacle markers, etc., you'll need to spawn pseudo-sensors to have these information published.

Here's documentation on spawning actos and sensors and here (or here) is a list of all available sensors.

Example

objects.json:

Click to expand
{   
    "objects": 
    [
        {
            "type": "sensor.pseudo.traffic_lights",
            "id": "traffic_lights"
        },
        {
            "type": "sensor.pseudo.objects",
            "id": "objects"
        },
        {
            "type": "sensor.pseudo.actor_list",
            "id": "actor_list"
        },
        {
            "type": "vehicle.dodge.charger_police_2020",
            "id": "hero",
            "sensors": 
            [
                {
                    "type": "sensor.pseudo.tf",
                    "id": "tf"
                },
                {
                    "type": "sensor.pseudo.markers",
                    "id": "markers"
                },
                {
                    "type": "sensor.pseudo.odom",
                    "id": "odometry"
                }
            ]
        }
    ]
}
# paste your objects.json here
nano /root/objects.json

# get session in container and source environment
docker exec -it carla-foxglove-ros-bridge bash
source /opt/ros/noetic/setup.bash
source /opt/carla-ros-bridge/install/setup.bash

# spawn the sesnors
roslaunch carla_spawn_objects carla_spawn_objects.launch spawn_sensors_only:=True objects_definition_file:=/root/objects.json

By specifying spawn_sensors_only:=True, this will search for an already present actor of role hero and type vehicle.dodge.charger_police_2020 and attach the sensors to it.

Acknowledgement

This project was developed in context of the SofDCar research project.

License

MIT