This repository contains the ROS 2 workspace packages and setup for the Coordinated Robotics cororos2 robots. It currently includes ROS 2 description, bringup, simulation, sensor and drivers integration work for Allie / Ames, Cornelius / Julius, and Joe / Jeanine.
Warning
This repository is under active development. Some robots still contain only partial ROS 2 support, and some hardware integrations are present in launch files but are not yet validated on the real robot.
The repository is the ROS 2 port of several robots and currently contains the most complete ROS 2 implementations for Allie / Ames, Cornelius / Julius, and Joe / Jeanine.
-
Robot-specific base backends:
- Allie: PWM hardware interface
- Cornelius: Roboclaw hardware interface
- Joe: ODrive hardware interface
-
Allie hardware context:
- Ouster OS0-128 lidar
- Intel RealSense D455 camera
- u-blox ZED-F9P GPS
- Memsense MS-IMU3025 IMU
- Pololu Micro Maestro PWM controller
- REV SPARK MAX motor controller using RC PWM input
-
Cornelius hardware context:
- Ouster OS0-128 lidar
- Intel RealSense D455 camera
- u-blox ZED-F9P GPS
- Memsense MS-IMU3025 IMU
- Roboclaw 2x60A motor controller
-
Joe hardware context:
- Velodyne VLP-16 lidar
- Intel RealSense D455 camera
- u-blox ZED-F9P GPS
- Memsense MS-IMU3025 IMU
- custom hoverboard-motor platform driven by ODrive v3.6 motor controllers
-
Current package focus:
cororos2_descriptioncororos2_bringupmemsense_msimu3025_driverpwm_hardware_interfaceroboclaw_hardware_interfaceodrive_hardware_interface
There are two ways to set up the workspace.
If you have installed RTW from the RTW installation guide, you can create and build the workspace in following way:
rtw workspace create --ws-folder cororos_ws --ros-distro jazzy
rtw ws cororos_ws
rosds
git clone -b ros2 git@github.com:b-robotized-forks/cororos2.git
rosdep_prep
rosdepi
cbIf GitHub SSH is not configured on your machine yet, you can clone the public repository over HTTPS instead:
git clone -b ros2 https://github.com/b-robotized-forks/cororos2.gitIf you are not using RTW, follow these steps.
From the root of your workspace (for example ~/cororos2_ws):
mkdir -p ~/cororos2_ws/src
cd ~/cororos2_ws/src
git clone -b ros2 git@github.com:b-robotized-forks/cororos2.git cororos2sudo apt update
sudo apt install -y python3-pip
rosdep updateInstall package dependencies from the workspace root:
cd ~/cororos2_ws
rosdep install --from-paths src --ignore-src -r -yThe package manifests declare the ROS 2 control, Gazebo, RViz, and hardware-driver package dependencies, so rosdep install is the supported way to install them. Separate sudo apt install ros-jazzy-... commands are not needed.
Joe's ODrive backend helper uses the Python odrive module. Install it on robot PCs that run Joe hardware bringup:
python3 -m pip install --break-system-packages --ignore-installed --upgrade odrive
python3 -m pip install --break-system-packages --upgrade "setuptools<80"cd ~/cororos2_ws
source /opt/ros/jazzy/setup.bash
colcon build --symlink-install
source install/setup.bashNow the workspace is ready for use.
The bringup files use the shared robot_model argument. Replace <robot_model> with one of:
alliecorneliusjoe
Open a new terminal and source the workspace before launching:
cd ~/cororos2_ws
source install/setup.bashUse this first if you only want to confirm that the URDF, meshes, and TF tree load correctly:
ros2 launch cororos2_description view_robot.launch.xml robot_model:=<robot_model>This starts the URDF, robot_state_publisher, joint_state_publisher_gui, and the common RViz config.
Example RViz views:
| Allie | Cornelius | Joe |
|---|---|---|
![]() |
![]() |
![]() |
-
Start the robot in Gazebo:
ros2 launch cororos2_bringup robot_gz.launch.xml robot_model:=<robot_model>
Gazebo simulation uses fused odometry by default so RViz heading follows the simulated IMU more closely during turns.
-
To run Gazebo without RViz:
ros2 launch cororos2_bringup robot_gz.launch.xml robot_model:=<robot_model> rviz:=false
-
Check simulated sensor topics:
ros2 topic list | grep '^/<robot_model>/' ros2 topic echo /<robot_model>/imu/data --once ros2 topic echo /<robot_model>/lidar/scan --once
Use offline/mock bringup when you want ROS 2 control, robot state, and RViz without Gazebo or real hardware:
ros2 launch cororos2_bringup cororos2_offline.launch.xml robot_model:=<robot_model>This wrapper uses mock hardware and is useful for checking controllers, descriptions, topic names, and launch arguments.
-
Check the available launch arguments:
ros2 launch cororos2_bringup cororos2_hw.launch.xml --show-args
-
Check serial devices before launching hardware:
ls -l /dev/serial/by-id/
-
You must pass the correct hardware paths, serial numbers, and IP addresses for the robot.
Do not copy placeholders like
<your-device>,<front-serial>, or<sensor-ip>into a real launch command. Replace them with the actual hardware values for the connected robot.Common hardware selectors:
- Allie:
pwm_device_path,ouster_sensor_hostname,ouster_udp_dest - Cornelius:
roboclaw_device,ouster_sensor_hostname,ouster_udp_dest - Joe:
odrive_front_serial_number,odrive_rear_serial_number,velodyne_device_ip - Optional sensors:
realsense_serial_no,gps_device,memsense_device
- Allie:
-
Start the full hardware stack:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=<robot_model>
This launches robot/control bringup and the sensor bringup together, and forces real hardware mode.
-
You must pass the robot-specific hardware selectors with the correct paths, serial numbers, and IP addresses.
Examples:
Allie with Maestro PWM base and Ouster:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=allie \ pwm_device_path:=/dev/serial/by-id/<your-maestro-id> \ ouster_sensor_hostname:=<sensor-ip> \ ouster_udp_dest:=<host-ip>
Cornelius with Roboclaw:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=cornelius \ roboclaw_device:=/dev/serial/by-id/<your-device>
This default path uses
roboclaw_use_encoder:=false, so Cornelius starts with the open-loop controller config unless you explicitly enable encoder-based Roboclaw odometry.Cornelius with encoder-based Roboclaw odometry:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=cornelius \ roboclaw_use_encoder:=true \ roboclaw_device:=/dev/serial/by-id/<your-device>
Joe with ODrive base and Velodyne:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=joe \ odrive_front_serial_number:=<front-serial> \ odrive_rear_serial_number:=<rear-serial> \ velodyne_device_ip:=<sensor-ip>
RealSense on any robot:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=<robot_model> \ realsense_serial_no:="'<serial>'"
GPS on any robot:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=<robot_model> \ gps_device:=/dev/ttyACM0
Memsense IMU on any robot:
ros2 launch cororos2_bringup cororos2_hw.launch.xml robot_model:=<robot_model> \ memsense_device:=/dev/serial/by-id/<your-device>
-
Check hardware sensor topics:
ros2 topic list | grep '^/<robot_model>/' ros2 topic echo /<robot_model>/imu/data --once ros2 topic echo /<robot_model>/gps/fix --once ros2 topic echo /<robot_model>/lidar/scan --once
Drive the robot directly through the diff-drive controller:
ros2 topic pub -r 10 /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped "{header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''}, twist: {linear: {x: 0.5, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.2}}}"This is useful for base controller testing in both simulation (mock/Gazebo) and for real hardware, without the teleop mux or Nav2. The diff_drive_controller has a cmd_vel_timeout of 0.5 s, so velocity commands must publish faster than that.
When testing only the diff-drive controller in RViz, set Global Options -> Fixed Frame to odom. Navigation/localization views usually use map, but direct base tests only require the odometry frame.
For the navigation instructions, see cororos2_navigation/README.md.
The following hardware drivers are integrated into cororos2_hw.launch.xml:
- Ouster lidar via
ouster_ros - Velodyne VLP-16 via
velodyne_driver,velodyne_pointcloud, andvelodyne_laserscan - Intel RealSense D455 via
realsense2_camera - u-blox GPS via
ublox_gps - Memsense IMU via
memsense_msimu3025_driver - Allie Maestro PWM backend via
pwm_hardware_interface - Cornelius Roboclaw base backend via
roboclaw_hardware_interface - Joe ODrive base backend via
odrive_hardware_interface
Warning
The drivers still need hardware validation.
Some hardware backends use serial devices under /dev/ttyUSB*, /dev/ttyACM*, or /dev/serial/by-id/.... On Ubuntu these devices are commonly owned by the dialout group. If a hardware launch fails with a permission error while opening a serial device, add your user to dialout:
sudo usermod -a -G dialout $USERJoystick and gamepad devices are commonly exposed through /dev/input/.... If joystick input fails with a permission error, add your user to input:
sudo usermod -a -G input $USERLog out and back in, or reboot, before trying again.
Joe's ODrive controllers are connected over USB and need the ODrive udev rules on
the robot PC. If the rules are missing, odrivetool or the ROS 2 bringup may
report:
Device permissions are not set up
[UsbDiscoverer] Failed to open USB device: -3
Install the ODrive udev rules:
sudo bash -c "curl https://cdn.odriverobotics.com/files/odrive-udev-rules.rules > /etc/udev/rules.d/91-odrive.rules && udevadm control --reload-rules && udevadm trigger"Verify USB access with:
odrivetoolrobot_model:=allie: PWM base, Ouster lidarrobot_model:=cornelius: Roboclaw base, Ouster lidarrobot_model:=joe: ODrive base, Velodyne VLP-16 lidar
This workspace can also be used with Zenoh to enable communication across networks, for example multiple machines or cloud setups.
- Configure ROS 2 to use Zenoh
Zenoh is enabled via environment variables. You can set them permanently either in ~/.ros_team_ws_rc or temporarily in each terminal:
export RMW_IMPLEMENTATION=rmw_zenoh_cpp- Launch the Zenoh router
Start the router in a dedicated terminal:
rtw-zenoh-routerThis is enough for local setups.
For multi-machine setups, start the router with the remote endpoint:
rtw-zenoh-router <router-ip>Or set the env variable:
export ZENOH_CONNECT_IP=192.168.28.28Note
The router is started once, not per terminal. Environment variables must be set in every terminal running ROS 2. You need to have RTW installed and a RTW workspace.
-
Controllers are already loaded or active. This usually means stale nodes from a previous launch are still running.
pkill -f ros2_control_node pkill -f controller_manager/spawner pkill -f robot_state_publisher
Then relaunch once from a clean terminal.
-
The base drive returns to neutral while testing.
diff_drive_controllerusescmd_vel_timeout: 0.5, so publishcmd_velfaster than that. Otherwise, the controller may time out between messages and cause intermittent wheel turning. For example:ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 1.0}, angular: {z: 0.5}}" -
Serial devices or joystick input fail with permission errors. Show input devices and their permissions / groups. This is useful to confirm joystick or gamepad devices are owned by
inputor another group:ll /dev/input
Show whether the
dialoutgroup exists and which users are in it:cat /etc/group | grep dialoutShow whether the
inputgroup exists and which users are in it:cat /etc/group | grep inputIf your user is not listed in the needed group, add it:
sudo usermod -a -G dialout $USER sudo usermod -a -G input $USER
Log out and back in, or reboot, before trying again.
Cornelius's roboclaw_hardware_interface publishes /diagnostics with one Roboclaw diagnostic entry. The diagnostic message reports conditions such as overcurrent, emergency stop, battery voltage faults, driver faults, and temperature faults.
The Roboclaw diagnostic includes:
deviceaddressmain_battery_voltage_vlogic_battery_voltage_vm1_current_am2_current_atemp1_ctemp2_cerror_word
Published telemetry topics:
roboclaw/battery_stateroboclaw/logic_battery_voltageroboclaw/m1_currentroboclaw/m2_currentroboclaw/temp1roboclaw/temp2


