API Reference

This reference describes the topics, message types, node parameters, and coordinate-system conventions provided by the contact_glove and contact_glove_msgs packages.


Types and nodes provided by the packages

contact_glove_msgs

Defines ROS 2 messages corresponding to SDK data structures.

FileSDK typeDescription
msg/GloveData.msgGloveDataDevice information: battery percentage, external power, connection state, device display-color setting, and more
msg/Ergonomics.msgErgonomicsDataFinger opening and bending angles, with ThumbPose and FingerPose submessages
msg/Skeleton.msgSkeletonDataPosition and rotation for 25 joints; geometry_msgs/Transform[25] joints plus NODE_* constants
msg/RawSkeleton.msgRawSkeletonDataPosition and rotation for 25 source-skeleton joints before retargeting, in a coordinate system distinct from Skeleton
msg/RawSensorTransforms.msgRawSensorTransformsDataPosition and rotation of five sensors; each field is geometry_msgs/Transform
msg/Controller.msgControllerDataButton states and analog inputs
msg/HapticsBody.msgVibrateGlove()Vibration command specifying frequency, amplitude, and duration
srv/GetRetargetProfile.srvRequestRetargetProfileInfo() / RetargetProfileInfoSynchronously queries display metadata by profile UUID

contact_glove

The package provides two executables.

ExecutableDescription
contact_glove_nodeConnects to the SDK and publishes glove data as ROS 2 topics
contact_glove_rviz_nodeSubscribes to left/skeleton and right/skeleton in the same namespace, then publishes side-specific RViz2 MarkerArrays and TF

Inspect message definitions

The installed ROS 2 message definitions are the authoritative source for fields, constants, types, and comments. Inspect them with:

ros2 interface show contact_glove_msgs/msg/GloveData
ros2 interface show contact_glove_msgs/msg/Ergonomics
ros2 interface show contact_glove_msgs/msg/Skeleton
ros2 interface show contact_glove_msgs/msg/RawSkeleton
ros2 interface show contact_glove_msgs/msg/RawSensorTransforms
ros2 interface show contact_glove_msgs/msg/Controller
ros2 interface show contact_glove_msgs/msg/HapticsBody
ros2 interface show contact_glove_msgs/srv/GetRetargetProfile

This document explains interface-wide conventions that are not evident from an individual msg file, including topic mappings, QoS, coordinate systems, and multiple-glove selection rules.


Topics

Every topic identifies a glove through the message id field and its hand through is_left. All streaming, control, and visualization topics also have a <hand> subtopic (left or right), allowing users to naturally subscribe to or remap one hand. Topic names are simple node-relative names (left/... and right/...) without the package name.

contact_glove_node starts in the contact_glove namespace by default, so its effective names are /contact_glove/left/... and /contact_glove/right/.... Override this with namespace:=.... To operate multiple DivingStation instances or glove pairs on one computer, give each node a distinct namespace or use <group><push-ros-namespace> in a launch file. Per-topic remapping is unnecessary.

A distinct namespace alone is insufficient. receive_port is the OSC receive port used by that node on the local machine (25788 by default). If two nodes share it, the second and later nodes fail at ConnectToHost. Assign each node a unique receive_port.

Topics published by contact_glove_node

TopicTypeQoSDescription
left/glove_data / right/glove_datacontact_glove_msgs/GloveDatadepth 16, transient_local, reliableDevice information
left/ergonomics / right/ergonomicscontact_glove_msgs/ErgonomicsSensor Data QoS (best effort, volatile)Finger opening and bending angles
left/skeleton / right/skeletoncontact_glove_msgs/SkeletonSensor Data QoS (best effort, volatile)Hand skeleton
left/raw_skeleton / right/raw_skeletoncontact_glove_msgs/RawSkeletonSensor Data QoS (best effort, volatile)Source skeleton before retargeting
left/raw_sensor_transforms / right/raw_sensor_transformscontact_glove_msgs/RawSensorTransformsSensor Data QoS (best effort, volatile)Raw sensor transforms
left/controller / right/controllercontact_glove_msgs/ControllerSensor Data QoS (best effort, volatile)Controller input
left/joint_states / right/joint_statessensor_msgs/JointStatedepth 10, reliable, volatileErgonomics data converted for URDF integration; see JointState + URDF below

glove_data contains state that changes infrequently, such as battery percentage and connection state, so it uses transient_local (latched) durability. A topic for one side can multiplex multiple gloves through id. Latching with depth 1 would retain only the glove most recently published and a late subscriber would miss other gloves.

To avoid that, contact_glove_node retains the latest state for every known glove. Whenever any glove updates, the node republishes all known current glove states to left/glove_data or right/glove_data according to is_left. The transient-local cache then contains one current sample per glove. Its depth is therefore the maximum number of gloves the node can track simultaneously (16 by default). Increase the depth if more gloves must run at once.

Important: A subscriber must also request transient_local durability to receive the glove_data latched cache. A volatile subscriber can connect to a transient_local publisher but receives no cached samples at connection time—only messages published afterward. Thus a default create_subscription(topic, 10, callback), or ros2 topic echo without a durability option, receives no current glove state until a glove sends new glove_data.

In C++, use the same QoS as the publisher:

auto sub = create_subscription<contact_glove_msgs::msg::GloveData>(
  "left/glove_data", rclcpp::QoS(16).transient_local(), callback);

For CLI inspection, specify the durability:

ros2 topic echo /contact_glove/left/glove_data --qos-durability transient_local

ergonomics, skeleton, raw_skeleton, raw_sensor_transforms, and controller are high-rate streams. They use rclcpp::SensorDataQoS() (best effort, volatile, equivalent to depth 5), which prioritizes low latency and non-blocking publication over preventing dropped samples. Subscribers must use that QoS or compatible best effort. A reliable subscriber cannot receive this data because the QoS is incompatible.

Topics subscribed to by contact_glove_node

TopicTypeQoSDescription
left/haptics_body / right/haptics_bodycontact_glove_msgs/HapticsBodydepth 10, reliable, volatileVibration command; publishing it triggers vibration

haptics_body keeps the default reliable/volatile QoS to avoid losing vibration commands. Both topics use msg.id to choose the target glove. When multiple gloves are assigned to one side, set id explicitly.

Services provided by contact_glove_node

ServiceTypeDescription
get_retarget_profilecontact_glove_msgs/srv/GetRetargetProfileQueries display metadata for a profile UUID from the currently connected DivingStation

The service is node-relative. Its fully qualified default name is /contact_glove/get_retarget_profile. Pass only profile_uuid in the request. The ROS 2 SDK assigns a positive request ID internally, sends it to the C++ SDK, and waits only for its matching asynchronous response, so callers do not manage request IDs. It does not query or cache profile UUIDs automatically while Skeleton messages arrive.

ValueConstantDescription
0STATUS_FOUNDThe profile exists. profile_uuid, name, and description are valid.
1STATUS_NOT_FOUNDThe UUID format is valid but DivingStation has no matching profile. name and description are empty.
2STATUS_INVALID_QUERYDivingStation rejected the query as invalid. The profile UUID and similar values are empty.
3STATUS_TIMEOUTNo DivingStation response was received within retarget_profile_timeout_ms (ROS 2 bridge-specific).
4STATUS_SDK_REQUEST_REJECTEDThe C++ SDK did not send the request, for example because the UUID format was invalid (ROS 2 bridge-specific).

Late responses that arrive after STATUS_TIMEOUT are discarded. Use name and description only for STATUS_FOUND.

Topics published by contact_glove_rviz_node

TopicTypeQoSDescription
left/skeleton_markers / right/skeleton_markersvisualization_msgs/MarkerArraydepth 10, reliable, volatileJoint spheres (SPHERE_LIST) and bones (LINE_LIST), in the message header.frame_id frame
/tfTF-With publish_placeholder_wrist_tf:=true, broadcasts placeholder TF from parent_frame_id to header.frame_id. With publish_joint_tf:=true, broadcasts TF for the 24 joints other than the wrist. It is always the absolute /tf path regardless of namespace.

contact_glove_rviz_node subscribes to left/skeleton and right/skeleton with rclcpp::SensorDataQoS(), then publishes the corresponding MarkerArrays. Its subscriber QoS must match contact_glove_node's publisher QoS.


Node parameters

contact_glove_node

ParameterDefaultDescription
ds_ip"127.0.0.1"DivingStation IP address
ds_port25791DivingStation OSC receive port
receive_port25788OSC receive port for this node
send_port0Local UDP source port for OSC sent from the SDK to DivingStation. 0 lets the OS choose an available port.
timeout_duration1000No-traffic timeout in milliseconds; expiry reconnects automatically
retarget_profile_timeout_ms1000Time in milliseconds that get_retarget_profile waits for its asynchronous response; zero or less is a startup error
try_reconnecttrueWhether to resend Connect on the existing socket at the timeout
left_joint_state_glove_id""Fixes the glove ID represented by left/joint_states. Empty selects the first glove received on that side.
right_joint_state_glove_id""Same rule for the right side

The default launch argument namespace is contact_glove. It is used for both the namespace embedded in header.frame_id and the ROS 2 node namespace.

DivingStation connection ports

ds_port (25791 by default) is the OSC receive port on the DivingStation side to which the SDK sends data. Check or change it from DivingStation settings:

  1. On the DivingStation home screen, click the gear icon in the upper right and open Settings.
  2. Select DivingStationProtocol from the left menu.
  3. The number for DivingStationProtocol v2 port is the SDK connection destination port.

DivingStationProtocol settings: v2 port number (English UI)

If DivingStation uses a v2 port other than 25791, set contact_glove_node ds_port to the same value. Otherwise OSC packets do not reach DivingStation and data cannot be exchanged.

receive_port is the local UDP port for callbacks from DivingStation. Give every SDK node on one PC a distinct value. When connecting to DivingStation on another PC, permit the actual receive_port through the local PC firewall.

send_port is the source port of UDP packets the SDK sends to DivingStation. With send_port:=0, the OS selects an available port. Specify it only where a fixed source port is required.

DivingStation identifies an active session by the source UDP endpoint and receive_port. Explicit Disconnect, or 90 seconds after the final Connect, expires a session. Fixed ports therefore do not preserve 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:=25789

send_port and receive_port may differ. Multiple SDK nodes on one PC need distinct receive_port values.

contact_glove_rviz_node

ParameterDefaultDescription
parent_frame_id"world"Parent frame of the wrist TF frame
publish_placeholder_wrist_tftruePublishes placeholder wrist TF so the hands appear side by side. Set false when another source publishes actual wrist 6DoF.
left_wrist_offset[0.0, -0.15, 0.0]Placeholder left-wrist position in meters in parent_frame_id coordinates
right_wrist_offset[0.0, 0.15, 0.0]Placeholder right-wrist position in meters in parent_frame_id coordinates
marker_lifetime0.4Marker lifetime in seconds. It expires after this interval if a hand disconnects or stops updating.
publish_joint_tffalsePublishes the 24 non-wrist joints to /tf for debugging. The wrist uses the existing placement mechanism and is not published again.

Skeleton message structure (25 joints)

Skeleton.msg is a ROS 2 representation of SDK SkeletonData::transforms: 25 elements in SkeletonDataType order.

  • geometry_msgs/Transform[25] joints contains position and rotation (translation and rotation) for all 25 joints. Units, coordinate system, and rotation rules are described below.
  • NODE_WRIST through NODE_LITTLE_TIP (uint8, 0 through 24) are index constants for joints. Their values and order exactly match SDK SkeletonDataType.

Every finger's CMC joint has NODE_WRIST as its parent. Every other joint is parented to the preceding joint in that finger. The kBonePairs table in contact_glove_rviz_node.cpp uses the same constants to render bones.

Child nodeParent node
NODE_THUMB_CMCNODE_WRIST
NODE_THUMB_MCPNODE_THUMB_CMC
NODE_THUMB_IPNODE_THUMB_MCP
NODE_THUMB_TIPNODE_THUMB_IP
NODE_{INDEX,MIDDLE,RING,LITTLE}_CMCNODE_WRIST
NODE_{INDEX,MIDDLE,RING,LITTLE}_MCPCorresponding NODE_*_CMC
NODE_{INDEX,MIDDLE,RING,LITTLE}_PIPCorresponding NODE_*_MCP
NODE_{INDEX,MIDDLE,RING,LITTLE}_DIPCorresponding NODE_*_PIP
NODE_{INDEX,MIDDLE,RING,LITTLE}_TIPCorresponding NODE_*_DIP

RawSkeleton message structure (25 joints)

RawSkeleton.msg exposes SDK RawSkeletonData::transforms with the same 25-joint order and NODE_* constants as Skeleton.msg. It has no profile_uuid.

  • Positions are in the pre-retarget source skeleton model-rooted space. header.frame_id is ds2/<namespace>/<glove_id>/source_wrist.
  • rotation is local relative to the parent joint, as in Skeleton, but only 19 joint rotations arrive over the wire. The C++ SDK initializes the six wrist and fingertip rotations to identity quaternions; do not interpret them as measured or estimated values.
  • RawSkeleton.tracking_state and Ergonomics.tracking_state report the IK that obtains the source skeleton. They can differ from post-retarget Skeleton.tracking_state at the same time. For example, when only retargeting fails, RawSkeleton and Ergonomics can be TRACKED while Skeleton is IK_FAILED.

contact_glove_rviz_node visualizes and publishes TF only for post-retarget Skeleton. It does not publish a MarkerArray or TF for RawSkeleton; consumers that visualize source coordinates must handle it explicitly.


Message coordinate systems

Skeleton, RawSkeleton, and RawSensorTransforms coordinates use the SDK's model-rooted space: right-handed and measured in meters. Skeleton uses the retarget profile hand-model coordinate system; RawSkeleton uses the source-skeleton hand-model coordinate system. Their header.frame_id values therefore differ.

translation is an absolute position in model-rooted space relative to each message's wrist. It is not relative to a preceding joint or sensor. rotation conventions differ by message.

AxisDirection
+XFingertip toward wrist
+YPalm toward back of hand
+ZLittle-finger side toward thumb side on the right hand; thumb side toward little-finger side on the left hand
        +Y (back of hand)
         ^
         |
 wrist   |
   o-----+-----> +X (toward wrist)
        /
       /
      v
    +Z (right: little to thumb / left: thumb to little)

Reversing +Z between hands keeps each local hand coordinate system right-handed (+X x +Y = +Z). Coordinate-transform libraries such as tf2 can therefore assume a right-handed system.

translation and rotation

  • Units: translation (x, y, and z of geometry_msgs/Vector3) is in meters.
  • rotation, common: A normalized quaternion in standard geometry_msgs/Quaternion field order: x, y, z, w.
  • rotation in Skeleton: A local quaternion relative to the parent joint, not an absolute orientation in model-rooted space. See Skeleton.msg NODE_* comments for the parent-child topology. The wire format sends 19 local rotations, including every CMC joint. The SDK fills NODE_WRIST and the five *_TIP rotations with identity quaternions; those six values are not measured rotations.
  • rotation in RawSkeleton: A local quaternion relative to the parent joint, like Skeleton. Only 19 joint rotations are transported; the identity quaternions for the wrist and every tip are not valid rotation measurements.
  • rotation in RawSensorTransforms: An absolute orientation in model-rooted space, like translation. It expresses the orientation from each sensor's local coordinate system to model-rooted space. This differs from Skeleton rotation, which is local relative to the parent joint.
  • Raw and calibrated data: Skeleton is post-retarget data from DivingStation Core IK processing that uses the profile identified by profile_uuid. The SDK decodes those values; it does not run IK. RawSkeleton is pre-retarget data produced by applying source IK and forward kinematics to calibrated EMF tracking. RawSensorTransforms contains raw transforms for the individual sensors without retargeting. tracking_state has TRACKED, IK_FAILED, and NOT_ATTEMPTED values. IK_FAILED and NOT_ATTEMPTED samples are useful for display and diagnostics, but must not drive control, velocity estimation, or interpolation. RawSkeleton and Skeleton tracking states can differ. NOT_ATTEMPTED means the required IK was not performed. It includes earlier processing failures or missing input and cases where pose calculation was deemed unnecessary, such as update-interval control reusing an existing pose. This state does not itself indicate an IK failure. Only TRACKED values are valid for control, velocity estimation, or interpolation.

The corresponding units, ranges, and coordinate rules are also comments in every msg file. Inspect them with ros2 interface show, for example:

ros2 interface show contact_glove_msgs/msg/Skeleton

Every streaming message (GloveData, Ergonomics, Skeleton, RawSkeleton, RawSensorTransforms, and Controller) begins with std_msgs/Header header. header.stamp is ROS time when the SDK callback was received and converted to a ROS 2 message. header.frame_id is the target glove wrist frame, except RawSkeleton, which uses the source-skeleton wrist.

generated_at_ms is the Unix timestamp in milliseconds when the SDK / DivingStation Protocol v2 generated a frame. It is included by Ergonomics, Skeleton, RawSkeleton, and RawSensorTransforms. GloveData and Controller do not have it because their protocol data has no generation time.

TF frame naming

The ROS 2 topic namespace (the namespace:=... launch argument) and TF frame names are different concepts.

  • Normal topics such as left/skeleton_markers are nested in the node namespace, for example /contact_glove/left/skeleton_markers, and are independent per namespace.
  • tf2_ros::TransformBroadcaster always publishes to the absolute /tf topic regardless of namespace. Thus /tf is one global topic: TF from every simultaneously running namespace, such as several DivingStation or demo environments, is sent to it.
  • To avoid TF collisions across multiple namespaces, make frame_id and child_frame_id names unique. Separating topic namespaces is not enough.

Wrist frames normally use:

ds2/<namespace>/<glove_id>/wrist

RawSkeleton uses a distinct name to avoid confusing source-skeleton coordinates with post-retarget Skeleton:

ds2/<namespace>/<glove_id>/source_wrist
  • The default namespace is contact_glove. The same launch argument is used for both the namespace in header.frame_id and the ROS 2 node namespace.
  • Physical glove IDs are UUIDs, so collision on glove_id alone is unlikely. Including namespace also makes separate DivingStation and demo environments easier for people to distinguish.
  • ds2 is a fixed prefix identifying frames from DivingStation Protocol v2 and reducing collision with frames from other ROS 2 packages on /tf.
  • contact_glove_with_rviz.launch.xml passes the same namespace to the driver and RViz node, so they always refer to the same wrist-frame names.
  • RViz joint markers and bones are published in the relevant Skeleton.header.frame_id wrist frame.

contact_glove_rviz_node publishes each hand skeleton in that hand's header.frame_id, with the wrist at its origin:

  • publish_placeholder_wrist_tf:=true (default) publishes a placeholder wrist TF at left_wrist_offset or right_wrist_offset so the hands appear side by side. It is for visualization only, not the actual wrist position.
  • publish_placeholder_wrist_tf:=false publishes no wrist TF. Another program must publish parent_frame_id to header.frame_id, for example from an external tracker with wrist 6DoF. The skeleton then follows that actual wrist position.

25-joint debug TF (publish_joint_tf)

Setting contact_glove_rviz_node publish_joint_tf (default false) to true publishes the 24 Skeleton joints other than the wrist to /tf. It is specifically for direct debugging of skeleton positions and rotations as TF. It is independent of JointState + URDF, which serves robot integration and joint-hierarchy visualization; either feature may be enabled alone.

  • The wrist remains ds2/<namespace>/<glove_id>/wrist. Its placement follows the publish_placeholder_wrist_tf placeholder or actual wrist TF from an external node. publish_joint_tf does not alter wrist behavior.

  • Non-wrist joints are direct wrist children named:

    ds2/<namespace>/<glove_id>/<finger>/<joint>

    Examples are ds2/contact_glove/0123456789ABCDEF/thumb/cmc, ds2/contact_glove/0123456789ABCDEF/index/pip, and ds2/contact_glove/0123456789ABCDEF/little/tip. <finger> is thumb, index, middle, ring, or little. <joint> is the relevant Skeleton.msg NODE_* suffix: cmc, mcp, pip, dip, ip, or tip.

  • translation directly relays Skeleton.joints[NODE_*].translation, which is already wrist-relative absolute position. Skeleton.joints[NODE_*].rotation is local relative to the parent joint, so the node composes rotations from parent to child before publishing the wrist-relative absolute rotation. This only changes rotation values; it does not reconstruct a serial TF hierarchy. The debug layout stays flat, with every joint a direct wrist child.

  • TF header.stamp is the corresponding Skeleton.header.stamp.

  • When publish_joint_tf:=false, none of this behavior occurs and existing MarkerArray or placeholder-wrist TF behavior is unchanged.


JointState + URDF (robot integration)

contact_glove_node also publishes the data in left/ergonomics and right/ergonomics as sensor_msgs/JointState on left/joint_states and right/joint_states. These topics are for robot_state_publisher and standard ROS 2 robot stacks such as MoveIt. The contact_glove package includes left and right hand-model URDF files: urdf/left_hand.urdf.xacro and urdf/right_hand.urdf.xacro.

25-joint debug TF exposes post-retarget Skeleton positions and rotations for inspection. JointState + URDF instead maps joint angles into named URDF joints for conventional robot visualization and integration.

Ergonomics degrees to JointState radians

  • The 20 Ergonomics.msg fields (thumb.{cmc,mcp,ip,tilt} and {index,middle,ring,little}.{mcp,pip,dip,tilt}) map one-to-one to 20 URDF joints.
  • Conversion is degrees to radians only. Values are not sign-inverted between left and right hands. URDF geometry and joint axes create the visual mirror image.
  • JointState.name is <side>_<finger>_<field>_joint, where <side> is left or right. Examples: left_thumb_cmc_joint and right_index_tilt_joint.
  • JointState.name and JointState.position follow Ergonomics.msg field order: thumb, index, middle, ring, little; mcp, pip, dip, tilt for each non-thumb finger; cmc, mcp, ip, tilt for the thumb.
TopicTypeQoS
left/joint_statessensor_msgs/JointStatedepth 10, reliable, volatile
right/joint_statessensor_msgs/JointStatedepth 10, reliable, volatile

Unlike high-rate ergonomics and skeleton topics, these publishers are reliable. robot_state_publisher, MoveIt, and other common ROS 2 robot stacks often use reliable QoS, the ROS 2 default. A best-effort publisher would be incompatible; a reliable publisher remains compatible with a subscriber requesting best effort.

The fully qualified default topics are /contact_glove/left/joint_states and /contact_glove/right/joint_states. Override the namespace under the same rules as other streaming topics.

Multiple gloves on the same side

left/joint_states represents one robot model. With multiple left gloves connected at once, it publishes data from only one.

  • When left_joint_state_glove_id or right_joint_state_glove_id is empty (the default), the first glove detected on that side is selected automatically. It is selected again after reconnecting.
  • To fix the selected glove, set the parameter to that glove ID. Data from other gloves is ignored and logged as a warning. Normal topics such as left/ergonomics continue to publish all gloves.
  • To display multiple gloves separately through URDF, start multiple contact_glove_node instances with one namespace per glove. Also set a unique receive_port per node; a duplicate port causes the second and later UDP binds to fail. ds_ip and ds_port may remain shared when they point to the same DivingStation. Explicitly set left_joint_state_glove_id or right_joint_state_glove_id too, otherwise every node can select the same first glove.

URDF files

  • Every joint and link name uses a left_ or right_ prefix, so both hand models can be loaded without collision.
  • Each hand has 20 joints, matching the 20 Ergonomics.msg fields, plus links that connect them. The root link is <side>_wrist.
  • Joint positions and orientations (origin) use calibrated hand geometry derived from measured reference points. The left hand reverses the right-hand reference-point Z coordinates and derives its own geometry. Its rest pose has exact mirror-image fingertip positions: equal X/Y and reversed Z.
  • Joint axes are adjusted so applying equal signed Ergonomics values to both hands correctly mirrors finger motion for flex joints (cmc, mcp, pip, dip, ip) and tilt joints (abduction/adduction).
  • Joint limits use the hand model's ranges in radians. They are not exact physical-device specifications.
  • Each link visual is a simple cylinder connecting adjacent joints, not a reconstruction of the physical hand.
  • The URDF is for visualization and joint hierarchy. It has no collision or inertial definitions; physical simulation is outside this package scope.

contact_glove_with_urdf.launch.xml

This launch file provides URDF-based real-time visualization through robot_state_publisher and RViz2 RobotModel.

ros2 launch contact_glove contact_glove_with_urdf.launch.xml hand:=both
ArgumentDefaultDescription
namespace"contact_glove"Same meaning as contact_glove.launch.xml
ds_ip / ds_port / receive_port / send_port / timeout_duration / try_reconnectSame defaults as contact_glove.launch.xmlDivingStation connection parameters
left_joint_state_glove_id / right_joint_state_glove_id""Passed directly to identically named contact_glove_node parameters
start_drivertrueManual ownership switch for starting the SDK driver contact_glove_node. It does not automatically prevent duplicate starts.
hand"both"both, left, or right: selects which hand to display
parent_frame_id"world"Parent frame for left_wrist and right_wrist
enable_rviz2trueWhether to start RViz2 with config/contact_glove_urdf.rviz
publish_wrist_placementtruePublishes placeholder static TF from parent_frame_id to left_wrist and right_wrist at left/right_wrist_offset_{x,y,z}. Set false when an external source publishes actual wrist 6DoF.
left_wrist_offset_x/y/z, right_wrist_offset_x/y/zleft (0.0, -0.15, 0.0) / right (0.0, 0.15, 0.0)Placeholder wrist positions in meters in parent_frame_id coordinates
left_wrist_offset_roll, right_wrist_offset_rollleft π (180°) / right 0.0Placeholder wrist rotations in radians about X. The left and right URDFs share a local coordinate convention, so rotating only the left hand by 180° places them facing each other.

With hand:=both, the file starts both left and right robot_state_publisher processes. hand:=left or hand:=right starts only the specified side. robot_description and joint_states resolve to /<namespace>/left/... and equivalent names, automatically matching contact_glove_node left/joint_states. No remapping is needed.

This launch file does not start contact_glove_rviz_node for MarkerArrays or 25-joint debug TF. To use both views, do not start it and contact_glove_with_rviz.launch.xml separately. Use contact_glove_with_urdf_and_rviz.launch.xml instead. Both individual launch files start the SDK driver, so separate starts create two contact_glove_node instances whose UDP bind to the same receive_port conflicts.

URDF TF frame names

URDF TF frame names such as left_wrist and left_thumb_cmc_link include namespace as a prefix, preventing TF collisions for multiple DivingStation instances in separate namespaces.

<namespace>/left_wrist
<namespace>/left_thumb_cmc_link
<namespace>/right_wrist

The publish_wrist_placement static TF uses the same names. The bundled config/contact_glove_urdf.rviz and config/contact_glove_combined.rviz set RobotModel TF Prefix to contact_glove for the default namespace. When overriding namespace:=..., set RViz TF Prefix to the same value without a trailing slash.

contact_glove_with_urdf_and_rviz.launch.xml

This launch file displays the post-retarget Skeleton (MarkerArray and 25-joint debug TF) alongside the derived URDF RobotModel. Its primary use is comparing both motions to verify Ergonomics-to-JointState conversion and URDF joint axes.

As above, separately starting contact_glove_with_rviz.launch.xml and contact_glove_with_urdf.launch.xml creates duplicate SDK drivers. Always use this combined launch file when both views are required.

ros2 launch contact_glove contact_glove_with_urdf_and_rviz.launch.xml hand:=both

It accepts every argument of contact_glove_with_rviz.launch.xml and contact_glove_with_urdf.launch.xml except start_driver. RViz2 starts with the bundled config/contact_glove_combined.rviz preset, which combines MarkerArray, TF, and RobotModel in one window.