Junhyeok Lee

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.xml

macOS: 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:jazzy

To 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 --build

Connect Unity

  • Transport Type = ROS2, host 127.0.0.1, port 9090.
  • Enable /cmd_vel in 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.

TopicTypeDirNotes
/cmd_velgeometry_msgs/TwistinDrive commands. Publish above 2 Hz or the watchdog stops the car.
/camera/{device}/compressedsensor_msgs/CompressedImageoutJPEG frames — raw Image is never published (bandwidth).
/camera/{device}/camera_infosensor_msgs/CameraInfooutIntrinsics — feeds depth → point-cloud and rectification pipelines.
/lidar/{device}/scansensor_msgs/LaserScanout2D LiDAR.
/lidar/{device}/pointssensor_msgs/PointCloud2out3D LiDAR — and the 2D scan as a cloud of its hits.
/imu/{device}/datasensor_msgs/ImuoutOrientation, angular velocity, specific force.
/gnss/{device}/fixsensor_msgs/NavSatFixoutPosition fix.
/ego/odomnav_msgs/OdometryoutEgo pose and twist in the odom frame.
/ego/cmd_appliedgeometry_msgs/TwistoutWhat the car actually obeyed.
/ego/vehicle_infostd_msgs/Stringout1 Hz — the vehicle limits as JSON.
/tftf2_msgs/TFMessageout30 Hz — the full frame tree (see Frames & time).
/clockrosgraph_msgs/ClockoutShips 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.py

On 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 list is empty — Unity is not connected; check transport, host, port, and the rosbridge log.
  • Connected but the car won't move/cmd_vel is 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, not up.