ROS 2 driver, description and bringup for the KIMA robotic arm.
This repository provides the ros2_control hardware interface, robot description and launch files for the KIMA robotic arm. The driver talks to the arm over EtherCAT through Kinova's Robot Control Library (RCL), vendored by the kinova_rcl_vendor package.
-
Install ROS 2.
This repository targets ROS 2 Jazzy on Ubuntu 24.04. The vendored Robot Control Library is only published for Linux x86_64 and arm64 with the gcc-13 ABI.
Stable LTS release: Install ROS 2 Jazzy
After installing ROS 2, source its
setup.bash, which sets the$ROS_DISTROenvironment variable.
If you want to build this repository from source or contribute back to it, read on.
-
Make sure that
colcon, its extensions, andvcsare installed:sudo apt install python3-colcon-common-extensions python3-vcstool
-
Create a new ROS 2 workspace:
export COLCON_WS=~/workspace/ros2_kima_ws mkdir -p $COLCON_WS/src
-
Pull the relevant packages:
cd $COLCON_WS git clone https://github.com/Kinovarobotics/ros2_kima.git src/ros2_kima vcs import src --skip-existing --input src/ros2_kima/ros2_kima.jazzy.repos
ros2_kima.jazzy.reposonly pulls the FZIcartesian_controllers, which are not released for Jazzy. All other dependencies (ros2_control, the controllers, gz_ros2_control, ros_gz, MoveIt) are released for Jazzy and are installed byrosdepin the next step. -
Install the dependencies and compile the workspace:
rosdep install --ignore-src --from-paths src -y -r colcon build --packages-skip cartesian_controller_simulation cartesian_controller_tests --cmake-args -DCMAKE_BUILD_TYPE=Release
cartesian_controller_simulation(MuJoCo) andcartesian_controller_testsare part of the FZI repository but are not used here.By default, colcon uses as many resources as possible to build the workspace, which can temporarily freeze or even crash your machine. To avoid this, limit the number of parallel workers; 3 is a good trade-off between build time and resource use:
colcon build --packages-skip cartesian_controller_simulation cartesian_controller_tests --cmake-args -DCMAKE_BUILD_TYPE=Release --parallel-workers 3
-
Source the workspace, and have every new terminal source it:
source $COLCON_WS/install/setup.bash echo 'source ~/workspace/ros2_kima_ws/install/setup.bash' >> ~/.bashrc
-
Bring up the Ethernet interface connected to the robotic arm:
sudo ip link set <ip_link_name> up
where
<ip_link_name>can be identified with:ip link
The kima.launch.py launch file brings up the KIMA arm. To bring up and visualize the arm with mock hardware in RViz:
ros2 launch kortex_bringup kima.launch.py \
robot_ip:=yyy.yyy.yyy.yyy \
use_fake_hardware:=trueFor the physical arm:
ros2 launch kortex_bringup kima.launch.py robot_ip:=192.168.1.10Arguments:
-
robot_type: Robot model. Default value (and only one) iskima. -
robot_ip: Required by the launch file. The EtherCAT driver does not use it, so any address works. -
use_fake_hardware: Start the robot with mock hardware that mirrors commands to its states. Default value isfalse. -
fake_sensor_commands: Enable fake command interfaces for sensors, for simple simulations. Only used ifuse_fake_hardwareistrue. Default value isfalse. -
robot_controller: Robot controller to start. Default value isjoint_trajectory_controller. -
controllers_file: ros2_control configuration file to use. Default value isros2_controllers.yaml. -
launch_rviz: Start RViz to visualize the robot. Default value istrue.
To run the arm in Gazebo Sim (Harmonic) instead of on the hardware:
ros2 launch kortex_bringup kortex_sim_control.launch.pyThis starts Gazebo, spawns the arm and activates joint_state_broadcaster and joint_trajectory_controller, so the arm can be commanded exactly as described in Commanding the arm.
Arguments:
-
launch_rviz: Start RViz. Default value istrue. -
gz_args: Arguments passed togz sim. Default value is" -r -v 3 empty.sdf". Add-sto run the server headless, without the Gazebo GUI. -
controllers_file: Controllers configuration, inkortex_description/arms/kima/7dof/config/. Default value isros2_controllers_sim.yaml.
In simulation, gz_ros2_control replaces the EtherCAT driver, so the fault controller is not available. It needs interfaces that only the real driver provides.
The kima_moveit_config package adds MoveIt (move_group) and RViz with the MotionPlanning panel on top of the regular bringup:
# Real arm
ros2 launch kima_moveit_config robot.launch.py
# Mock hardware
ros2 launch kima_moveit_config robot.launch.py use_fake_hardware:=true
# Gazebo simulation
ros2 launch kima_moveit_config robot.launch.py sim:=trueArguments:
-
use_fake_hardware: Run on mock hardware instead of the real arm. Default value isfalse. -
sim: Run in Gazebo instead of on the real arm. It takes precedence overuse_fake_hardware. Default value isfalse. -
launch_rviz: Start RViz with the MotionPlanning panel. Default value istrue. -
robot_ip: Forwarded tokima.launch.py. The EtherCAT driver does not use it. Default value is0.0.0.0.
The planning group is manipulator (base_link to tool_frame), with a home named state (all joints at 0). The OMPL (default) and Pilz (PTP, LIN, CIRC) planners are available. Motions execute through joint_trajectory_controller.
Velocity and acceleration are scaled to 10% by default (config/joint_limits.yaml). They can be raised in the MotionPlanning panel.
Command the arm by publishing JointTrajectory messages to the joint trajectory controller. Joint positions are in radians:
ros2 topic pub /joint_trajectory_controller/joint_trajectory trajectory_msgs/JointTrajectory "{
joint_names: [joint_1, joint_2, joint_3, joint_4, joint_5, joint_6, joint_7],
points: [
{ positions: [0, 0, 0, 0, 0, 0, 0], time_from_start: { sec: 10 } },
]
}" -1cartesian_motion_controller (FZI cartesian_controllers) moves tool_frame to a target pose. It is loaded inactive by kima.launch.py and kortex_sim_control.launch.py, together with motion_control_handle, an interactive marker in RViz whose pose is fed to the controller.
The controller tracks the target pose it receives on /cartesian_motion_controller/target_frame (geometry_msgs/msg/PoseStamped). It keeps tracking the last target until a new one arrives. That target can come from the RViz handle or from a topic, but only one source should be active at a time.
Switch from the trajectory controller to Cartesian control with the handle:
ros2 control switch_controllers \
--activate cartesian_motion_controller motion_control_handle \
--deactivate joint_trajectory_controllerThen drag the CartesianTarget marker in RViz. The handle starts at the current tool pose, so the arm does not jump when it is activated.
The marker is shown by the CartesianTarget display (Interactive Markers, namespace /motion_control_handle), which is already included in the RViz started by kima.launch.py, kortex_sim_control.launch.py and kima_moveit_config robot.launch.py. If RViz was launched with launch_rviz:=false, open it separately with the same configuration:
ros2 run rviz2 rviz2 -d $(ros2 pkg prefix --share kortex_description)/rviz/view_robot.rvizWith any other RViz configuration, add the display manually: Add > By display type > rviz_default_plugins/InteractiveMarkers, then set Interactive Markers Namespace to /motion_control_handle.
The marker appears as soon as the handle is loaded, even while it is inactive. It only moves to the current tool pose, and only commands the arm, once motion_control_handle and cartesian_motion_controller are active.
-
Switch to the Cartesian controller, and leave
motion_control_handleinactive so it does not publish targets too:ros2 control switch_controllers --activate cartesian_motion_controller --deactivate joint_trajectory_controller
-
Read the current tool pose:
ros2 run tf2_ros tf2_echo base_link tool_frame
Note the
Translationand the quaternion (xyzw). -
Publish the target pose. Edit the position, and paste the orientation read in step 2 unless you want the tool to rotate:
ros2 topic pub --once -w 1 /cartesian_motion_controller/target_frame geometry_msgs/msg/PoseStamped \ "{header: {frame_id: base_link}, pose: {position: {x: <x>, y: <y>, z: <z>}, orientation: {x: <x>, y: <y>, z: <z>, w: <w>}}}"frame_idmust be exactlybase_link. A target in any other frame is ignored, and the only sign is a warning in the controller's log.- Positions are in metres, relative to
base_link. -w 1waits until the controller is subscribed before sending, so the single message is not lost.--onceis enough because the controller keeps tracking the last target.
Switch back to the trajectory controller (needed for MoveIt and joint trajectories) with:
ros2 control switch_controllers \
--activate joint_trajectory_controller \
--deactivate cartesian_motion_controller motion_control_handleNotes:
- The controller has no velocity limit of its own: how fast the arm moves depends on how far the target is and on the gains in
kortex_description/arms/kima/7dof/config/ros2_controllers.yaml(solver.error_scale,pd_gains). Start with small target steps on the real arm. - Activate it away from the straight-up zero pose, which is a kinematic singularity. Move the arm into a bent pose with the trajectory controller first.
- The orientation in the target is followed too: a quaternion different from the current one rotates the tool.
Actuator faults stay latched in the actuators across restarts: stopping the launch does not clear them. There are two ways to clear them.
If the launch is up, clear the fault through the fault_controller:
ros2 service call /fault_controller/reset_fault example_interfaces/srv/TriggerOn success, the driver re-enters real-time mode from the arm's current position. When a fault latches, the driver logs its causes and the per-actuator fault banks (Arm fault latched. ... Actuator fault banks:).
If a fault is still latched when the launch starts, the hardware may fail to activate and ros2_control_node exits with SetArmMode: 'SelectMode' is not allowed in state 'Fault'. In that case, stop the launch and clear the fault with the standalone RCL tool, which talks to the arm without ROS:
ros2 run kortex_driver clear_faults \
"$(ros2 pkg prefix --share kortex_description)/arms/kima/7dof/config/network_topology_1_arm.yaml"The tool scans the bus, prints the arm state, the latched causes and the fault banks, clears the faults and prints the state again. A successful run ends with:
[after] arm state: Idle
[after] (no actuator reports a non-zero fault bank)
Notes:
- Only one program can hold the EtherCAT master at a time, so
ros2_control_nodemust not be running. - The tool never enables the arm: it stays in
Idlewith its brakes engaged. - RCL sometimes reports
Fault clear incomplete: N actuator(s) still in faulteven though every fault bank already reads zero. The tool, like~/reset_fault, retries the clear up to 3 times in that case. If it still ends inFault, run it again. - The expected arm identity (
ARM-L3M000-001, revisionA, serial0001, 7 actuators, slaves 0-13) is fixed inkortex_driver/tools/clear_faults.cppand matches the defaults inkortex.ros2_control.xacro. The scan fails if the arm on the bus does not match.