Skip to main content

ROS 2 API

CORA uses ROS 2 as the distributed robotic software backend. Usage is largely the same as any other ROS 2 application. The supported release is ROS 2 Jazzy LTS on Ubuntu 24.04.

Packages

The ROS 2 backend spans three repositories, each a self-contained workspace:

RepositoryContents
cora_commonShared packages used by both real robot and simulation
cora_robotOn-board robot packages: ODrive ros2_control hardware interface, ODrive setup scripts
cora_desktopDesktop simulation packages: Gazebo launch files and world configs

cora_common packages

PackagePurpose
cora_bringupTop-level launch files for real robot and simulation
cora_codiROS 2 bridge node connecting the CoDI SDK to the hardware stack
cora_descriptionURDF, XACRO, and mesh files for the robot
cora_gripper_1_descriptionURDF and meshes for the default gripper
cora_moveitMoveIt 2 Python action server (mover_node) and keyboard servo test
cora_moveit_configMoveIt 2 SRDF, kinematics, and controller config
cora_msgsCustom message and action definitions
cora_visionLifecycle camera node and ArUco marker detection

cora_robot packages

PackagePurpose
odrive_ros2_controlros2_control hardware interface plugin for ODrive v3.6 over CAN
odrive_msgsODrive-specific ROS 2 messages (axis state, errors, joint status)
odrive_baseSocketCAN and CAN message helpers (C++ headers)
odrive_setupPython scripts for ODrive configuration and calibration

ROS 2 Interfaces

Topics

TopicTypePublisherNotes
/joint_statessensor_msgs/JointStateros2_control_nodeJoint positions
/tftf2_msgs/TFMessagerobot_state_publisherFull transform tree
/tfstatictf2_msgs/TFMessagestatic_transform_publisherworldbase_link
/robot_descriptionstd_msgs/Stringrobot_state_publisherURDF string latched on startup
/servo_node/delta_twist_cmdsgeometry_msgs/TwistStampedcodi_nodeReal-time Cartesian velocity commands
/servo_node/delta_joint_cmdscontrol_msgs/JointJogcodi_nodeReal-time joint velocity commands
/servo_node/delta_pose_cmdsgeometry_msgs/PoseStampedcodi_nodeReal-time Cartesian pose commands
/arm_controller/joint_trajectorytrajectory_msgs/JointTrajectoryservo_nodeOutgoing trajectory to arm controller
/gripper_cam/image_rawsensor_msgs/Imagecamera_nodeRaw gripper camera feed
/gripper_cam/arucovision_msgs/MarkerArrayvision_nodeDetected ArUco markers

Actions

posegoalcora_msgs/action/PoseGoal

The main motion planning action. Served by mover_node in cora_moveit, called by codi_node.

Goal

cora_msgs/TargetedPoseStamped pose_goal # Cartesian pose + target frame
float64[] joint_goal # Joint positions [J1..J6] in radians
float64 gripper_goal # Gripper opening in metres
string predefined_pose # Named state from SRDF e.g. 'standby', 'straight'
string space # 'TS' (task space) or 'JS' (joint space)
string interface_type # 'position', 'velocity', 'acceleration', or 'effort'

Result

geometry_msgs/PoseStamped pose_result # Achieved end-effector pose
float64[] joint_result # Achieved joint positions
string status_result # 'Complete' or error description
bool success

Feedback

geometry_msgs/PoseStamped pose_feedback
string status

arm_controller/follow_joint_trajectorycontrol_msgs/action/FollowJointTrajectory

Standard ros2_control trajectory execution action for the arm group (J1–J6). Used internally by MoveIt 2.

gripper_fingers_controller/gripper_cmdcontrol_msgs/action/GripperCommand

Standard parallel gripper action for the gripper_fingers group (Finger1, Finger2).

Services

MoveIt Servo

ServiceTypeNotes
servo_node/switch_command_typemoveit_msgs/srv/ServoCommandTypeSwitch between JOINT_JOG, TWIST, and POSE input modes

Lifecycle nodes

ServiceTypeNotes
/vision_node/change_statelifecycle_msgs/srv/ChangeStateActivate/deactivate ArUco detection
/camera_node/change_statelifecycle_msgs/srv/ChangeStateActivate/deactivate camera node
/controller_node/change_statelifecycle_msgs/srv/ChangeStateActivate/deactivate controller node

MoveIt 2 (via move_group)

ServiceType
/compute_ikmoveit_msgs/srv/GetPositionIK
/compute_fkmoveit_msgs/srv/GetPositionFK
/plan_kinematic_pathmoveit_msgs/srv/GetMotionPlan

Custom message definitions

cora_msgs/msg/TargetedPoseStamped

string target_frame # 'Gripper', 'Camera', or 'endeffector'
geometry_msgs/PoseStamped pose

SRDF — planning groups and named states

GroupJoints
armJ1, J2, J3, J4, J5, J6 (chain: baselinkendeffector)
gripper_fingersFinger1, Finger2
Named stateGroupDescription
straightarmAll joints at 0 rad
standbyarmFolded upright position
standby_2armAlternate standby
opengripper_fingersFinger1 = 0.035 m
closedgripper_fingersFinger1 = 0 m

Launch files

bringup.launch.py

Full robot launch. Starts the CoDI node after a 30 s delay to allow MoveIt to initialise.

ros2 launch cora_bringup bringup.launch.py \
hardware_type:=Real \
gripper_package:=cora_gripper_1_description \
use_codi:=true
ParameterOptionsDefault
hardware_typeFake, Real, GazeboFake
gripper_packageany gripper description packageNone
use_coditrue, falsetrue
launch_rsptrue, falsetrue

move.launch.py

Full MoveIt 2 stack: move_group, servo_node, ros2_control_node, robot_state_publisher, RViz, and controller spawners.

ros2 launch cora_moveit move.launch.py \
hardware_type:=Real \
use_servo:=true \
use_moveitpy:=true \
gripper_package:=cora_gripper_1_description

vision.launch.py

Camera node and ArUco detection.

ros2 launch cora_bringup vision.launch.py

gazebo.launch.py

Full Gazebo simulation with bridge, spawner, and bringup.

ros2 launch cora_gazebo gazebo.launch.py \
gripper_package:=cora_gripper_1_description

Simulation

Installation

git clone --recurse-submodules https://github.com/C-O-R-A/cora_desktop.git
cd cora_desktop
bash install.sh
source install/setup.bash

Usage

ros2 launch cora_gazebo gazebo.launch.py

MoveIt 2 and RViz launch alongside Gazebo. Use the MoveIt motion planning panel in RViz to plan and execute trajectories, or send PoseGoal action goals via CoDI.

Real Robot

ODrive Setup

Each joint uses an ODrive v3.6 board over CAN at 250 kbps. Node IDs are assigned per-joint in cora.ros2_control.xacro (J1=0, J2=1, ... J6=5, Finger1=6).

1. Bring up CAN interface

sudo ip link set can0 up type can bitrate 250000

This is automated on boot via the can0-up.service installed by install.sh.

2. Load motor config

# 6354 270KV motors
odrivetool restore-config odrive_setup/configs/closed_loop_6354.json

# 5065 140KV motors
odrivetool restore-config odrive_setup/configs/closed_loop_5065.json

3. Calibrate

python3 odrive_setup/quicksetup.py odrive_setup/configs/closed_loop_6354.json --axis 0

This automates the full calibration sequence: config restore → full calibration → set pre_calibrated flags → save and reboot.

4. Test

python3 odrive_setup/quicktest.py --axis 0 --mode velocity

Installation

git clone --recurse-submodules https://github.com/C-O-R-A/cora_robot.git
cd cora_robot
bash install.sh
source install/setup.bash

Usage

ros2 launch cora_bringup bringup.launch.py \
hardware_type:=Real \
gripper_package:=cora_gripper_1_description

:::warning CAN bus Make sure can0 is up before launching. The install.sh installs a can0-up.service systemd service that brings it up automatically on boot. :::

Adding a Custom Gripper

  1. Create a package with this structure:
gripper_description/
meshes/
urdf/
cora_gripper.urdf
cora_gripper.urdf.xacro # macro name: cora_gripper_xacro
cora_gripper.ros2_control.xacro
  1. The xacro macro must be named cora_gripper_xacro with params hardware_type and initial_positions_file.

  2. Required joint names: Finger1 (prismatic, main) and Finger2 (prismatic, mimic).

  3. Required dummy links and fixed joints: Gripper, Camera, Camera_optical with joints Gripper_frame, Camera_frame, Camera_frame_optical.

  4. Launch with your package:

ros2 launch cora_bringup bringup.launch.py gripper_package:=your_gripper_description