ROS 2 Integration
Hardware and Software Integration
After connecting the device through wuji_hand2_driver, you can read joint and sensor state, view the model in RViz, and control the device through joint commands and services.
Installation
Use Ubuntu 26.04 (resolute) with ROS 2 Lyrical installed, on x86_64 or aarch64. The commands below fetch the source, load ROS, install dependencies and Hand 2 descriptions, and build the workspace. The build downloads and verifies the Wuji SDK C package for the host architecture.
# Fetch the source and load ROS 2
git clone https://github.com/wuji-technology/wuji-ros2.git "$HOME/wuji-ros2"
cd "$HOME/wuji-ros2"
source /opt/ros/lyrical/setup.bash
# Install build tools and dependencies
sudo apt-get update
sudo apt-get install -y ros-dev-tools
rosdep update
# Install the Hand 2 descriptions and workspace dependencies
scripts/install_hand2_descriptions.sh
rosdep install --from-paths . --ignore-src -r -y
# Build the workspace. The SDK C package is downloaded and verified automatically
colcon build --cmake-args -DBUILD_TESTING=OFF
# Load the built workspace
source install/setup.bash -
The description installer requires
/etc/apt/sources.list.d/ros2.sources. If it is missing, configure the Lyrical package source using the official ROS 2 installation steps, then rerun the script. -
If rosdep has never been initialized on the computer, run
sudo rosdep initonce beforerosdep update. Thesudocommands require administrator access. -
If the SDK download fails, download and extract
wuji-sdk-c-2026.10.12-x86_64-linux-gnu.tar.gzorwuji-sdk-c-2026.10.12-aarch64-linux-gnu.tar.gzfrom Wuji SDK v2026.10.12, matching your host architecture. Confirm that the extracted directory containsinclude/wuji_sdk.h,lib/libwuji_sdk_c.so, andLICENSE. Replace/path/to/sdkwith the absolute path to that directory, then run this command fromwuji-ros2instead of the default build command:colcon build --cmake-args -DBUILD_TESTING=OFF -DWUJI_SDK_ROOT=/path/to/sdk -
CMake does not validate the SDK version when
WUJI_SDK_ROOTis set. Use the release package specified above.
In each new terminal, enter the workspace and load ROS and the built packages:
cd "$HOME/wuji-ros2"
source /opt/ros/lyrical/setup.bash
source install/setup.bash Select a Device
Complete device mounting in Mechanical Integration and power and network setup in Electrical and Network Integration. Confirm that the operating requirements in User Notice are met, then run this command to scan for devices:
ros2 run wuji_tools wuji_list Record the target device’s required serial_number and hand_side. If connecting by address, also record its optional endpoint.
| Parameter | Purpose |
|---|---|
serial_number | Selects the device. Validates its identity when endpoint is set |
hand_side | Selects the left- or right-hand model and validates device handedness |
endpoint | Connects directly to the device’s IP:port network address |
View the Model and State
Run the Validation
1. Start the Driver and View the Model
Replace HAND_SERIAL_NUMBER and HAND_SIDE with the serial number and hand side recorded in Select a Device.
Run hand2_view.launch.py from the built wuji_bringup package to start the driver, robot_state_publisher, and RViz:
ros2 launch wuji_bringup hand2_view.launch.py \
serial_number:=HAND_SERIAL_NUMBER hand_side:=HAND_SIDE \
model_revision:=2 namespace:=hand To connect by the endpoint shown in the scan, replace HAND_ENDPOINT with that IP:port address and use this command:
ros2 launch wuji_bringup hand2_view.launch.py \
serial_number:=HAND_SERIAL_NUMBER hand_side:=HAND_SIDE \
endpoint:=HAND_ENDPOINT model_revision:=2 namespace:=hand The driver waits up to 10 seconds for the device to connect by default. Append connect_timeout:=20.0 to the launch command to extend the connection wait to 20 seconds. Launch arguments are read only at startup. Boolean arguments accept only lowercase true / false.
This launch file requires serial_number and fixes auto_enable to false. It doesn’t enable joints at startup or disable a hand that was already enabled. When the launch stops, the driver calls the disable API for all joints. If the call fails, it logs a warning and continues to exit. Confirm that the device has actually stopped.
model_revision:=2 selects wuji_hand2_2_description. The model root is l_mount for the left hand or r_mount for the right hand, and RViz uses that root frame. See Model Assets and Loading for model assets and root links. The parent application must provide the transform from the mount to its robot or world frame.
2. Check State Data
Keep the launch running. Open a new terminal, enter the workspace, and load ROS and the built packages before checking state data:
cd "$HOME/wuji-ros2"
source /opt/ros/lyrical/setup.bash
source install/setup.bash
ros2 run wuji_bringup hand2_status_demo --ros-args -r __ns:=/hand The example observes joint_states, joint_diagnostics, and imu for 10 seconds by default and reports frame counts and average receive rates. It succeeds after receiving at least one joint state with 20 positions, one joint diagnostic message with 20 entries, and one IMU message. This check doesn’t validate motion control or every sensor.
The demo reports PASS/FAIL after the full observation period. To observe for 30 seconds, run:
ros2 run wuji_bringup hand2_status_demo --ros-args -r __ns:=/hand -p duration:=30.0 If the driver connects but RViz shows No tf data or the status example receives no data, first check namespaces, ROS_DOMAIN_ID, and QoS across terminals. If the problem persists, try another ROS 2 middleware. Install Cyclone DDS:
sudo apt-get install ros-lyrical-rmw-cyclonedds-cppStop the current launch. Run this command in each terminal used for steps 1 and 2:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cppThen rerun the commands in steps 1 and 2. Set the same ROS 2 middleware in any new terminals you open later.
State and Sensor Topics
The driver publishes joint states, joint diagnostics, and IMU data on the topics below. Subscribe using the listed message types. Topic names are relative: with namespace:=hand, joint_states resolves to /hand/joint_states.
| Topic | Message Type | Content |
|---|---|---|
joint_states | sensor_msgs/msg/JointState | Position (rad), velocity (rad/s), and current (A) for valid joints. effort is current, not measured torque. Identify joints by name |
joint_diagnostics | wuji_msgs/msg/JointDiagnosticsArray | Raw SDK status words, fault codes, current, voltage, temperature, and communication statistics for online joints, not the standard ROS /diagnostics interface |
imu | sensor_msgs/msg/Imu | Angular velocity and linear acceleration. No orientation estimate, with orientation_covariance[0] = -1 |
For field definitions, see wuji_msgs/msg/JointDiagnosticsArray.msg and wuji_msgs/msg/JointDiagnostics.msg in the wuji-ros2 repository, and the ROS 2 JointState.msg and Imu.msg definitions. After sourcing the workspace, run ros2 interface show wuji_msgs/msg/JointDiagnosticsArray to inspect the custom message and its nested fields.
Control the Device
The driver sends position, velocity, and current-proxy targets without switching control modes. Motion depends on the device’s current control mode and parameters.
Send Joint Commands
1. Start the driver
Stop the view launch started in 1. Start the Driver and View the Model. Secure the unloaded hand and keep people clear of moving parts. Start control with the same serial number and hand side:
ros2 launch wuji_bringup hand2.launch.py \
serial_number:=HAND_SERIAL_NUMBER hand_side:=HAND_SIDE \
model_revision:=2 namespace:=hand auto_enable:=true auto_enable:=true enables all joints after startup. It doesn’t send motion targets yet.
2. Check the current positions
Keep the driver running. Open a new terminal.
If the driver uses Cyclone DDS, configure the same ROS 2 middleware in this terminal:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cppRun these commands to load ROS and read joint positions:
cd "$HOME/wuji-ros2"
source /opt/ros/lyrical/setup.bash
source install/setup.bash
ros2 topic echo /hand/joint_states sensor_msgs/msg/JointState --once --timeout 20 --field position The wave demo sends zero targets immediately. Before running it, confirm that the path from the current position to zero is safe. The joints may move quickly toward zero.
3. Run the wave demo
In the terminal with the ROS environment loaded, run:
ros2 run wuji_bringup hand2_wave_demo --ros-args -r __ns:=/hand The wave demo publishes zero targets to /hand/joint_commands for one second, then publishes wave commands at 100 Hz. Thumb and abduction targets stay at 0 rad. The other four fingers move between 0 and 1.0 rad with a 0.5 Hz cosine wave. Observe the hand and joint position changes.
4. Stop and disable
Press Ctrl+C to stop the wave demo, then disable all joints:
ros2 service call /hand/set_enabled wuji_msgs/srv/SetEnabled \
'{enabled: false, mask: [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]}' Confirm the response contains success: true before stopping the driver launch, and verify that the hand has stopped.
The driver has no command watchdog. Stopping the publisher doesn’t send zeros, trigger an emergency stop, or disable the hand. When the launch stops, the driver calls the disable API for all joints. If the call fails, it logs a warning and continues to exit. Confirm that the device has actually stopped. If the disable request fails, don’t assume that exiting the launch or losing the connection has disabled the hand.
Joint Command Format
Publish sensor_msgs/msg/JointState messages to joint_commands. The publisher fills the name, position, velocity, and effort fields. They aren’t launch arguments.
Named commands: When writing a publisher, put joint names published in joint_states in name, and provide position values in the same order. You can specify a subset of joints in any order. The driver fills targets for unlisted joints with zero, so explicitly include any joints whose targets you want to preserve.
Fixed-order commands: Leave name empty and provide all 20 position values in the order below. The wave demo above uses this format. Prefix the listed joint names with l_ or r_ for the selected hand. Fixed-length services use the same order.
| Index | Joint Name Bodies, in Order |
|---|---|
| 0–3 | thumb_cmc_flex, thumb_cmc_abd, thumb_mcp, thumb_ip |
| 4–7 | index_finger_mcp_flex, index_finger_mcp_abd, index_finger_pip, index_finger_dip |
| 8–11 | middle_finger_mcp_flex, middle_finger_mcp_abd, middle_finger_pip, middle_finger_dip |
| 12–15 | ring_finger_mcp_flex, ring_finger_mcp_abd, ring_finger_pip, ring_finger_dip |
| 16–19 | pinky_mcp_flex, pinky_mcp_abd, pinky_pip, pinky_dip |
In either format, empty velocity and effort arrays are filled with zeros. Nonempty arrays must match the number of commanded joints. Unknown, duplicate, or opposite-side names, invalid lengths, nonfinite values, and values outside the float32 range reject the complete command.
Call Control Services
Services use reliable QoS and the same namespace. Check both the success flag and error information in the response.
| Service | Type | Behavior |
|---|---|---|
set_enabled | wuji_msgs/srv/SetEnabled | Enables or disables joints selected by a 20-entry mask |
clear_faults | wuji_msgs/srv/ClearFaults | Clears faults for joints selected by a 20-entry mask |
set_effort_limit | wuji_msgs/srv/SetEffortLimit | Sets one current limit in A for all joints |
set_effort_limits | wuji_msgs/srv/SetEffortLimits | Sets 20 current limits in A in the fixed order |
set_mit_params | wuji_msgs/srv/SetMitParams | Sets 20 kp and kd values in the fixed order |
emergency_stop | std_srvs/srv/Trigger | Calls the SDK whole-hand emergency stop |
An all-false mask changes no joints. Fixed-length services require all 20 entries and don’t accept joint names or partial arrays. Current limits, kp, and kd must be finite and nonnegative. kp and kd retain their SDK definitions without additional physical units.
Rejected joint commands produce rate-limited ERROR logs in the driver terminal, with no result topic. Failed service calls return success: false and error information. The driver keeps running.
To request an emergency stop, call:
ros2 service call /hand/emergency_stop std_srvs/srv/Trigger '{}' The driver doesn’t latch the emergency-stop state. After the call, the controlling application must stop publishing joint commands, or later commands may still take effect.
Documentation