Applies to v0.1.0.
Network · ROS 2
Unity reaches the ROS graph through rosbridge on WebSocket port 9090. Native on Linux, Docker on macOS. Unity only ever speaks to 127.0.0.1:9090, so DDS never has to cross a VM boundary.
Unity is a rosbridge client, not a ROS node, so start rosbridge_server before pressing Connect.
Linux: native
Ubuntu 22.04 → Humble; Ubuntu 24.04 → Jazzy. Swap the distro name to match:
sudo apt install ros-humble-rosbridge-suite
source /opt/ros/humble/setup.bash
ros2 launch rosbridge_server rosbridge_websocket_launch.xmlmacOS: Docker
ROS 2 does not install natively on macOS. A prebuilt Apple-silicon image is published; examples/ is mounted so you can edit the scripts on the host.
git clone https://github.com/Hyeokk/Nano-sim.git && cd Nano-sim
docker run --rm -it --name nanosim-ros -p 9090:9090 \
-v "$PWD/examples:/ws/examples" hyeokk64/nanosim-ros:humble
# Jazzy: swap the tag -> hyeokk64/nanosim-ros:jazzyTo build it yourself, or to use the compose profile, run compose from docker/, since it only looks in the current directory:
docker build -f docker/Dockerfile.ros2-humble -t nanosim-ros:humble .
cd docker && docker compose --profile humble up --buildConnect Unity
- Transport Type =
ROS2, host127.0.0.1, port9090. - Enable
/cmd_velin the topic config; this is what lets you drive. - Press Connect, then check with
ros2 topic list.
Topics
{device}is the sensor's name from the Sensor Setup panel, so the sensor topics that appear depend on what you have added. Run ros2 topic list to see yours.
| Topic | Type | Dir | Notes |
|---|---|---|---|
/cmd_vel | geometry_msgs/Twist | in | Drive commands. Publish above 2 Hz or the watchdog stops the car. |
/camera/{device}/compressed | sensor_msgs/CompressedImage | out | JPEG frames — raw Image is never published (bandwidth). |
/camera/{device}/camera_info | sensor_msgs/CameraInfo | out | Intrinsics — feeds depth → point-cloud and rectification pipelines. |
/lidar/{device}/scan | sensor_msgs/LaserScan | out | 2D LiDAR. |
/lidar/{device}/points | sensor_msgs/PointCloud2 | out | 3D LiDAR — and the 2D scan as a cloud of its hits. |
/imu/{device}/data | sensor_msgs/Imu | out | Orientation, angular velocity, specific force. |
/gnss/{device}/fix | sensor_msgs/NavSatFix | out | Position fix. |
/ego/odom | nav_msgs/Odometry | out | Ego pose and twist in the odom frame. |
/ego/cmd_applied | geometry_msgs/Twist | out | What the car actually obeyed. |
/ego/vehicle_info | std_msgs/String | out | 1 Hz — the vehicle limits as JSON. |
/tf | tf2_msgs/TFMessage | out | 30 Hz — the full frame tree (see Frames & time). |
/clock | rosgraph_msgs/Clock | out | Ships disabled — enable it only when time_scale ≠ 1. |
/ego/vehicle_info is a String rather than a custom type on purpose: rosbridge drops an advertise for a message type it has no definition for, so a nano_sim/VehicleInfo would never arrive.
Example
The repository ships a keyboard teleop. On Linux:
source /opt/ros/humble/setup.bash
python3 examples/ros2/nanosim_teleop_ros2.pyOn macOS it needs a second terminal. CMDruns rosbridge, so the container's stdin already belongs to it. exec allocates a fresh TTY:
docker exec -it nanosim-ros bash -lc \
"source /opt/ros/humble/setup.bash && python3 /ws/examples/ros2/nanosim_teleop_ros2.py"It reads the car's limits before starting, and says where it got them:
>> max_speed 0.500 m/s (step 0.050), lock 26.0 deg, wheelbase 0.1657 m [/ego/vehicle_info]The bracket is the fallback chain: --max-speed, then /ego/vehicle_info, then describe on 7720, then a built-in default. If it says BUILT-IN DEFAULT, the car will saturate long before the bar fills. Pass --max-speed. See Vehicle control for what the values mean.
Troubleshooting
ros2 topic listis empty — Unity is not connected; check transport, host, port, and the rosbridge log.- Connected but the car won't move —
/cmd_velis disabled in the topic config. - Car stutters and stops — publish rate fell under 2 Hz and the watchdog fired.
- Teleop exits immediately in Docker — no TTY; use
docker exec -it, notup.