User Manual
This manual explains how to use the prebuilt ContactGlove ROS2 SDK V2 distribution. It covers installation of the bridge that publishes glove data and the standalone interfaces for subscribers on a separate PC or container. It also covers DivingStation connections, data acquisition and visualization, vibration commands, and robot integration.
For the complete parameter list, topic list, message fields, QoS, coordinate axes, and TF naming rules, see the API reference.
What you can do
The bridge connects to DivingStation and provides these features:
- Receive left and right glove information, finger joint angles, a 25-joint skeleton, raw sensor transforms, and controller input as ROS 2 topics.
- Send a vibration command to a glove by ID.
- Display the post-retarget Skeleton in RViz2.
- Display a RobotModel using JointState and the bundled URDF.
- Compare the post-retarget Skeleton and URDF in one RViz2 window.
- Operate multiple nodes with separate namespaces and receive ports.
The node connects automatically when it starts and disconnects when it exits. Set try_reconnect to control reconnection after communication stops.
Installation
Supported environments
- ContactGlove3 Pro (with ContactGlove3, no data reaches the bridge)
- Ubuntu 22.04 LTS (Jammy) with ROS 2 Humble, amd64 (x86-64)
- Ubuntu 24.04 LTS (Noble) with ROS 2 Jazzy, amd64 (x86-64)
Use the distribution ZIP matching your OS and ROS 2 distribution. ARM64 and other OS/ROS 2 versions are outside this distribution's scope. Configure ROS 2 and its APT repository first, then run installation commands from the extracted ZIP directory. Use the same ROS 2 distribution and compatible message definitions on the publishing and subscriber systems.
Choose the package for this system
The ZIP contains three DEBs for the same ROS 2 distribution: bridge and msgs at the root, and the C++ runtime in lib/.
- To publish glove data, install all three:
contactglove-ros2-bridge,contactglove-ros2-msgs, andcontactglove-sdk-v2. The bridge provides the driver and visualization features; msgs providescontact_glove_msgs. DivingStation is required on the publishing system. - To subscribe on a separate PC or container, install only
contactglove-ros2-msgs. DivingStation and the C++ runtime are unnecessary there. Do not source the ZIP'sinstall/directory on this system.
Bridge and msgs now own separate files and are installed together on the publishing system.
Use the explicit commands in “Bridge: install the DEB bundled in the ZIP (recommended)”: apt does not automatically find adjacent local DEBs, including the runtime in lib/.
Subscriber only: install the standalone messages DEB
contactglove-ros2-msgs includes all nine messages and the GetRetargetProfile service definition in contact_glove_msgs, generated C/C++ and Python interfaces, type support, and ament/CMake metadata.
This package does not connect to gloves or publish device data by itself. DivingStation and the bridge must be running on the publishing system.
sudo apt update
sudo apt install ./contactglove-ros2-msgs_*.deb
# Ubuntu 24.04 / Jazzy
source /opt/ros/jazzy/setup.bash
# On Ubuntu 22.04 / Humble, use this instead:
# source /opt/ros/humble/setup.bash
ros2 pkg prefix contact_glove_msgs
ros2 interface show contact_glove_msgs/msg/GloveData
ros2 interface show contact_glove_msgs/srv/GetRetargetProfile
python3 -c 'from contact_glove_msgs.msg import GloveData; print(GloveData())'Source the setup file for your ROS 2 distribution in every new terminal.
On the subscriber system, do not run the C++ runtime installation helper or source the ZIP's install/setup.bash.
Also avoid sourcing another workspace containing the same interfaces on top of this installation.
Install ros-<distro>-rclpy for a Python subscriber, or ros-<distro>-rclcpp and development tools such as CMake to build a C++ subscriber.
Replace <distro> with humble or jazzy. Use find_package(contact_glove_msgs REQUIRED) in CMake.
See the API reference for topic names and types.
For subscribers on another PC or in a container, use the same ROS_DOMAIN_ID as the publishing system and a network that permits DDS discovery.
Check the container network configuration and firewall as well. Use the commands in “Verify data delivery” to check reception, with QoS compatible with the publisher.
The bridge installation and launch procedures below apply to the publishing PC or container.
Bridge: install the DEB bundled in the ZIP (recommended)
The bridge requires ContactGlove C++/C/Rust SDK V2 runtime 0.4.4 and DivingStation v2 or later. Communication with DivingStation uses Protocol v2.
Run these commands from the extracted distribution ZIP. The contactglove-ros2-bridge_*.deb at the ZIP root installs directly into the current ROS 2 environment.
sudo apt update
sudo apt install ./lib/contactglove-sdk-v2_*.deb \
./contactglove-ros2-msgs_*.deb ./contactglove-ros2-bridge_*.deb
# Source after installation. Use /opt/ros/humble/setup.bash for Humble.
source /opt/ros/jazzy/setup.bashInstalling the ContactGlove ROS2 SDK V2 DEB through apt also installs runtime dependencies such as RViz2, xacro, and robot_state_publisher. Do not mix DEBs built for different ROS 2 distributions. Source the setup file for your ROS 2 distribution in every new terminal.
Upgrade from the combined bridge (0.2.2 or earlier)
Stop running ContactGlove nodes and close terminals that sourced the previous ZIP. Extract the new ZIP for your ROS 2 distribution and OS into a new directory, then run the following commands from the extracted ZIP root. This upgrades the bridge and transfers interface files to msgs through versioned Debian Breaks/Replaces. Do not use force-overwrite or manually delete files under /opt/ros/.
sudo apt update
sudo apt install ./lib/contactglove-sdk-v2_*.deb \
./contactglove-ros2-msgs_*.deb ./contactglove-ros2-bridge_*.debTo change an old publishing system to subscriber-only use, first remove contactglove-ros2-bridge, then install only the msgs DEB.
sudo apt remove contactglove-ros2-bridge
sudo apt install ./contactglove-ros2-msgs_*.debBridge: use the ZIP install directory instead of the DEB
This method sources the install/ directory inside the distribution ZIP without installing the ContactGlove ROS2 SDK V2 system-wide.
# Use /opt/ros/humble/setup.bash for Humble.
source /opt/ros/jazzy/setup.bash
# Required only for visualization features.
sudo apt install ros-${ROS_DISTRO}-rviz2 ros-${ROS_DISTRO}-xacro ros-${ROS_DISTRO}-robot-state-publisher
# Install the bundled ContactGlove C++/C/Rust SDK V2 runtime.
bash scripts/install_contactglove_sdk_deb.sh
# Enable the ROS 2 packages in the distribution ZIP.
source install/setup.bashEvery new terminal must source the ROS 2 setup file first and then install/setup.bash. RViz2, xacro, and robot_state_publisher are unnecessary when using topic delivery only.
Verify the bridge installation
ros2 pkg prefix contact_glove
ros2 pkg prefix contact_glove_msgs
ros2 pkg executables contact_gloveSetup is complete when both packages and contact_glove_node / contact_glove_rviz_node are listed.
Installed documentation
The standalone messages DEB installs the minimal bilingual READMEs and license documents under /opt/ros/<distro>/share/contact_glove_msgs/.
The bridge DEB also places them under /opt/ros/<distro>/share/contact_glove/.
The full guide and this user manual are maintained on this site.
Basic use
DivingStation running on the same PC
Connect a glove in DivingStation, then run:
ros2 launch contact_glove contact_glove.launch.xmlBy default, the node connects to DivingStation at 127.0.0.1:25791 and receives data on UDP port 25788.
DivingStation running on another PC
Use a trusted LAN. In DivingStation's SDK / Protocol, set Listening scope to Expose on LAN.
If you change the listening scope, restart DivingStation. With the default This computer only, other PCs cannot connect. When exposed on a LAN, the default connection policy requires approval for another PC; connections from the same PC are allowed automatically.
Set ds_ip to the numeric IP address of the PC running DivingStation.
ros2 launch contact_glove contact_glove.launch.xml ds_ip:=192.168.1.100If the connection policy requires approval, allow the connection request shown in DivingStation.
Set ds_port to DivingStation's listening port (default 25791).
ros2 launch contact_glove contact_glove.launch.xml \
ds_ip:=192.168.1.100 \
ds_port:=25791ds_port is the receive port on the DivingStation side. receive_port is the callback receive port on the ROS 2 node side. Allow the required UDP traffic on the actual receive ports of both PCs. Multiple SDK nodes on one PC must use non-overlapping receive_port values.
send_port specifies the source port of OSC packets sent from the SDK to DivingStation. With the default send_port=0, the OS chooses an available port. Set it only when a fixed source port is required. Automatic timeout retries resend Connect on the same socket without Disconnect, retaining the OS-selected source port and pending LAN approval.
DivingStation identifies an active session by the source UDP endpoint and receive_port. An explicit Disconnect or 90 seconds after the final Connect expires the session, so fixing ports does not retain approval for a later connection.
# To fix both the source port and callback receive port.
ros2 launch contact_glove contact_glove.launch.xml \
receive_port:=25788 \
send_port:=25789When running multiple SDK nodes on one PC, assign a different receive_port to each. See the API reference node parameters for exact meanings and defaults.
Verify data delivery
First inspect the topic list.
ros2 topic listInspect representative data with:
ros2 topic echo --qos-durability transient_local /contact_glove/left/glove_data
ros2 topic echo --qos-profile sensor_data /contact_glove/left/ergonomics
ros2 topic echo --qos-profile sensor_data /contact_glove/left/skeleton
ros2 topic echo --qos-profile sensor_data /contact_glove/left/raw_skeleton
ros2 topic echo --qos-profile sensor_data /contact_glove/left/controller
ros2 topic echo /contact_glove/left/joint_statesFor the right hand, replace left with right. If you override the namespace, replace contact_glove with the namespace in use.
Use transient local durability for glove_data to receive the current state cached before the subscriber started. Ergonomics, skeleton, raw skeleton, raw sensor transforms, and controller use Sensor Data QoS. See the API reference topic list for all topics and QoS.
Query a retarget profile
When display name and description for Skeleton.profile_uuid are needed, call the service with that UUID. The node manages asynchronous request IDs and response correlation internally.
ros2 service call /contact_glove/get_retarget_profile \
contact_glove_msgs/srv/GetRetargetProfile \
"{profile_uuid: '00000000-0000-4000-8000-000000000001'}"An existing profile returns status: 0 (STATUS_FOUND) with name and description. A well-formed UUID that does not exist returns status: 1. A malformed UUID that the C++ SDK rejects before sending returns status: 4. If DivingStation replies with protocol status 2 (invalid query), the bridge forwards status: 2 with empty profile metadata. The current C++ SDK validates UUIDs before sending, so a malformed UUID passed to this service normally returns status: 4, not 2. If DivingStation does not reply, the service returns status: 3 after the default one-second timeout. Change that timeout at launch:
ros2 launch contact_glove contact_glove.launch.xml \
retarget_profile_timeout_ms:=3000Visualization
Display the post-retarget Skeleton in RViz2
ros2 launch contact_glove contact_glove_with_rviz.launch.xmlThe post-retarget Skeleton is shown as a blue MarkerArray for the left hand and red for the right hand. To run only the visualization-conversion node without RViz2:
ros2 launch contact_glove contact_glove_with_rviz.launch.xml enable_rviz2:=falseDefault left and right wrist positions are placeholders for checking operation; they are not actual wrist 6DoF.
Display the URDF hand model
This starts robot_state_publisher with JointState and the bundled URDF, then displays the model in RViz2 RobotModel.
ros2 launch contact_glove contact_glove_with_urdf.launch.xml hand:=bothUse hand:=left or hand:=right to display only one hand.
Display the post-retarget Skeleton and URDF together
ros2 launch contact_glove contact_glove_with_urdf_and_rviz.launch.xml hand:=bothUse this combined launch for simultaneous display. Starting contact_glove_with_rviz.launch.xml and contact_glove_with_urdf.launch.xml independently with default settings starts two drivers and causes a bind conflict on the same receive_port.
If another launch already owns the driver, pass start_driver:=false to the added visualization launch.
Follow an actual wrist position
For post-retarget Skeleton visualization, disable the placeholder wrist TF:
ros2 launch contact_glove contact_glove_with_rviz.launch.xml \
publish_placeholder_wrist_tf:=falseAn external tracker must publish TF from the frame selected by parent_frame_id to the target glove wrist frame.
For URDF visualization, pass publish_wrist_placement:=false and publish TF from an external node to the left and right URDF wrist frames.
See the API reference sections on message coordinate systems, TF frame naming, and JointState + URDF for frame names, namespace prefixes, and rotation conventions.
Multiple-node configurations
Multiple DivingStation instances or SDK nodes
Separate ROS 2 topics by namespace and assign distinct receive_port values to nodes on the same PC.
ros2 launch contact_glove contact_glove.launch.xml \
namespace:=station_a \
ds_ip:=192.168.1.100 \
receive_port:=25788
ros2 launch contact_glove contact_glove.launch.xml \
namespace:=station_b \
ds_ip:=192.168.1.101 \
receive_port:=25789This creates topics under /station_a/... and /station_b/.... A namespace does not isolate UDP sockets, so receive_port cannot be duplicated on the same PC. Nodes on separate PCs may use the same port number.
Multiple gloves on the same side
Normal topics such as glove_data, ergonomics, and skeleton carry multiple same-side gloves on the same topic. Identify each message by id.
left/joint_states and right/joint_states, in contrast, each drive a single URDF model. When no target ID is configured, the first glove received on that side is selected. To fix it:
ros2 launch contact_glove contact_glove_with_urdf.launch.xml \
hand:=left \
left_joint_state_glove_id:=YOUR_LEFT_GLOVE_IDTo display multiple same-side gloves through separate URDF models, start one node per glove with a distinct namespace, receive_port, and target ID.
ros2 launch contact_glove contact_glove_with_urdf.launch.xml \
namespace:=glove_a \
receive_port:=25788 \
hand:=left \
left_joint_state_glove_id:=GLOVE_ID_A
ros2 launch contact_glove contact_glove_with_urdf.launch.xml \
namespace:=glove_b \
receive_port:=25789 \
hand:=left \
left_joint_state_glove_id:=GLOVE_ID_BFind each target ID in glove_data id. See the API reference multiple-gloves section for the complete selection rules and parameter definitions.
Send vibration
First inspect glove_data to confirm the target ID, connection state, and haptics capability.
ros2 topic echo --qos-durability transient_local /contact_glove/left/glove_dataUse the actual ID you found to send a vibration command:
ros2 topic pub --once \
/contact_glove/left/haptics_body \
contact_glove_msgs/msg/HapticsBody \
"{id: 'YOUR_GLOVE_ID', frequency: 200.0, amplitude: 0.8, duration: 0.5}"For the right hand, replace left in the topic with right. Check valid frequency, amplitude, and duration ranges in the API reference or with:
ros2 interface show contact_glove_msgs/msg/HapticsBodyIntegrate with a robot system
Use these interfaces with a standard ROS 2 robot stack:
left/joint_statesandright/joint_states- Bundled left and right URDF files
- robot_state_publisher
JointState converts Ergonomics values from degrees to radians. See the API reference JointState + URDF section for joint names, ordering, left/right behavior, QoS, and URDF joint axes.
The 25-joint TF feature is for debugging post-retarget Skeleton positions and rotations. Use JointState and URDF for integration with a URDF joint hierarchy.
Troubleshooting
The standalone messages DEB will not install
Check that the OS, ROS 2 distribution, and CPU architecture match a supported environment. For a combined bridge 0.2.2 or earlier, follow the upgrade procedure above. The new bridge requires the same-version msgs DEB. Pass all three local DEBs to apt for a bridge installation; apt does not search neighboring files.
Topics from another PC or container are missing
Check that the bridge is running on the publishing system and its topics are visible locally.
On the subscriber, source the ROS 2 setup file and match the publishing system's ROS_DOMAIN_ID.
Check that container networking and firewalls permit DDS discovery and communication.
The standalone messages package does not contain a driver that connects to DivingStation.
The bridge publishes nothing
Check that:
- DivingStation v2 or later is running.
- A glove is connected in DivingStation.
- On the publishing system, ROS 2 has been sourced; install/setup.bash has also been sourced if using the ZIP bridge instead of the DEB.
ds_ipandds_portmatch DivingStation settings.- The ROS 2 node
receive_portis not already used by another process. - A firewall is not blocking UDP traffic.
For a cross-PC connection, permit the default 25791/udp on the DivingStation side and default 25788/udp on the ROS 2 node side:
sudo ufw allow 25791/udp
sudo ufw allow 25788/udpIf you changed either port, permit the actual values instead. If DivingStation runs on Windows, allow DivingStation to receive through Windows Firewall.
The second node cannot connect
Two nodes on the same PC cannot use the same receive_port because the second UDP bind fails. Use distinct values such as 25788 and 25789. Changing namespace alone does not solve this.
Topics exist, but data is not visible
Check subscriber QoS:
- glove_data: request transient local.
- ergonomics, skeleton, raw sensor transforms, and controller: request Sensor Data QoS.
- joint_states, haptics, and skeleton markers: use reliable-compatible QoS.
See the API reference topic list for exact QoS.
RViz2 does not display a skeleton
Check that:
- skeleton and skeleton_markers are published in the namespace in use.
- RViz2 Fixed Frame is world or the selected parent_frame_id.
- publish_placeholder_wrist_tf is true when using placeholder placement.
- the Marker topic is skeleton_markers in the namespace in use.
Hand positions do not match real positions
The default display is a placeholder that lays the hands out side by side. To use actual wrist positions, follow the procedure in Follow an actual wrist position: disable the placeholder and publish TF from an external tracker.
No vibration
Check that:
- HapticsBody.id matches glove_data.id.
- is_haptics is true.
- connected_state is connected.
- frequency, amplitude, and duration are within their valid ranges.
- The target device is connected in DivingStation.