Project

General

Profile

Task #215

Updated by Sanghoon Lee about 2 months ago

Create a new xela_server_ros2 named xela_server2_ahv4 for Allegro hand like XR23AHLCPP, XR23AHRCPP. 

 -------------- 

 !{width:800px}clipboard-202602071630-emryr.png! 

 -------------------- 

 h1. xela_server2_ah 

 h2. 1. Overview 

 This document describes the design for the @xela_server2_ah@ ROS 2 node that 
 converts WebSocket JSON messages into @xela_taxel_msgs/msg/XTaxelSensorArray@ 
 and publishes them on @/x_taxel_ah@ for Allegro Hand (AHv4) visualization. 
 The design mirrors @xela_server2_2f@ to maximize code reuse and operational parity. 

 h2. 2. Goals 

 * Provide a stable WebSocket client that reconnects on failure. 
 * Parse and validate JSON with clear drop reasons. 
 * Map data into ROS 2 messages exactly as defined in the FSD. 
 * Use a deterministic joint map (@server_model_joint_map.yaml@) shared with AHv4 visualization. 
 * Support Allegro Hand models @XR23AHLCPP@ (left) and @XR23AHRCPP@ (right). 

 h2. 3. Non-Goals 

 * No buffering or backfill of historical messages. 
 * No on-disk logging of raw WebSocket data. 
 * No transformation into URDF/TF beyond @frame_ids@ assignment. 

 h2. 4. Technology Choices 

 * Language: C++ (rclcpp) 
 * WebSocket: @boost::beast@ (Boost.Asio) 
 * JSON: @nlohmann/json@ (vendored single-header) 
 * YAML: @yaml-cpp@ for mapping file 

 h3. 4.1 Dependencies and Auto-Install 

 To make fresh clones buildable without manual setup, keep dependencies in 
 @package.xml@ and rely on @rosdep@ during setup: 
 * ROS 2: @rclcpp@, @std_msgs@ 
 * Xela msgs/libs: @xela_taxel_msgs@, @xela_server_ros2@ 
 * System libs: @yaml-cpp@, @boost@ (beast/asio), @nlohmann_json@ 

 Recommended setup command (document in README): 
 <pre><code> 
 rosdep install --from-paths src --ignore-src -r -y 
 </code></pre> 
 This installs missing system dependencies automatically in new environments. 

 h2. 5. Package Layout (proposed) 

 * @xela_apps/xela_server2_ah/@ 
 * @package.xml@ 
 * @CMakeLists.txt@ 
 * @config/@ 
 * @server_model_joint_map.yaml@ 
 * @include/xela_server2_ah/@ 
 * @ah_parser.hpp@ 
 * @ah_joint_map.hpp@ 
 * @src/@ 
 * @ah_node.cpp@ (main node) 
 * @ah_parser.cpp@ (JSON to message conversion) 
 * @ah_joint_map.cpp@ (YAML loading helper) 

 Naming note: 
 * Use @ah_@ prefixes in this package to avoid confusion with @xela_server2_2f@. 
 * Renaming @xela_server2_2f@ sources to @2f_@ is possible but not required 
   (would be a breaking refactor with low value). 

 h2. 6. Parameters 

 * @ws_host@ (string, default: @localhost@) 
 * @ws_port@ (int, default: @5000@) 
 * @frame_ids_yaml@ (string, default: path to @server2_ah_config.yaml@) 
 * @hand_side@ (string, default: @left@, accepts @left|right|l|r@) 
 * @header_frame_id@ (string, default: empty) 
 * @use_ros_time_for_sensor_time@ (bool, default: @false@) 
 * @publisher_qos_depth@ (int, default: @10@) 
 * @input_json_path@ (string, default: empty; enables file playback mode) 
 * @playback_interval_ms@ (int, default: @100@) 
 * @playback_loop@ (bool, default: @true@) 

 Config note: 
 * @server2_ah_config.yaml@ is a separate wrapper config (for parity with 
   @xela_server2_2f@) and should reference the mapping file. 
   @server_model_joint_map.yaml@ remains the source of truth for joint mapping. 

 Recommended @server2_ah_config.yaml@ schema: 
 <pre><code> 
 mapping_yaml: server_model_joint_map.yaml 
 </code></pre> 

 h2. 7. Data Flow 

 # WebSocket receives JSON text (or file playback supplies JSON). 
 # JSON is parsed and validated. 
 # Joint map is loaded once and cached. 
 # @XTaxelSensorArray@ is constructed. 
 # Message is published to @/x_taxel_ah@. 

 h2. 8. Parsing and Mapping Details 

 h3. 8.1 Module Ordering 

 * Build modules from the joint map grouped by @sensor_pos@. 
 * Order modules by ascending @sensor_pos@. 
 * @md_frame_ids@ aligns 1:1 with @x_modules@ order. 

 h3. 8.2 Mapping Schema 

 @server_model_joint_map.yaml@ format: 
 <pre><code> 
 taxel_joint_map: 
   0: <joint_name> 
   1: <joint_name> 
   ... 
   367: <joint_name> 
 </code></pre> 
 The loader sorts the numeric keys and builds a contiguous @frame_ids@ list. 
 The mapping file is not duplicated; it must stay consistent with the AHv4 viz map. 

 h3. 8.3 Joint Name Parsing 

 Joint names follow @x_taxel_<group>_<sensor_pos>_<model>_<index>_joint@. 
 The parser extracts: 
 * @sensor_pos@ (used for module ordering) 
 * @model@ (used for @md_frame_ids@ and @x_modules[i].model@) 

 The joint list is grouped by @sensor_pos@, and each group preserves the 
 order from the sorted map (required for integer/calibrated/temp alignment). 
 In the mapping file, @sensor_pos@ is stored as a zero-padded two-digit token 
 (e.g., @03@, @15@). Parse to an integer for the message field. 

 h3. 8.4 Hand Side Prefix Mapping 

 The mapping file uses @x_taxel_0_@ prefixes. For right-hand operation, the 
 node rewrites @x_taxel_0_@ to @x_taxel_1_@ after loading the map. This is 
 controlled by the @hand_side@ parameter. 

 h3. 8.4 integer -> Taxel 

 * Use @integer@ array if present. 
 * Convert sequential triples into @Taxel(x,y,z)@. 
 * If counts mismatch, warn and use the available min count. 

 Note: Some inputs include a @data@ CSV hex string. It is not parsed in this design, 
 but leave a reference comment near the parsing logic for future fallback work. 

 h3. 8.5 calibrated -> Forces 

 * Use @calibrated@ array when present. 
 * Convert sequential triples into @Forces(x,y,z)@. 
 * If missing, leave @forces@ empty. 

 h3. 8.6 temp -> temps 

 * Use @temp@ array (Kelvin) when present. 
 * Convert to Celsius: @C = K - 273.15@. 
 * Preserve @frame_ids@ order. 

 h2. 9. Threading and Concurrency 

 * WebSocket runs in a dedicated thread with @boost::asio::io_context@. 
 * ROS 2 node runs in the main thread with a @SingleThreadedExecutor@. 
 * Use a thread-safe queue (size 1) to hand off raw JSON strings. 

 h2. 10. Error Handling Strategy 

 * JSON parse error: warn and drop. 
 * Missing required fields: warn and drop. 
 * Mapping load failure: error and drop. 
 * Length mismatch: warn and use available count. 
 * @sensor@ not parseable to int: warn and drop. 
 * WebSocket error/close: log and reconnect after a short delay. 

 h2. 11. Logging 

 * Info: connection established, reconnect attempts, playback enabled. 
 * Warn: parse/validation failures or count mismatches. 
 * Error: mapping load failures. 

 h2. 12. QoS 

 * Publisher QoS depth from parameter (default 10). 
 * Reliability: default rclcpp QoSProfile (reliable). 

 h2. 13. Testing Plan (Design-Level) 

 * Unit tests for integer parsing, calibrated parsing, temp conversion, and map loading. 
 * Unit test to verify 368 frame IDs are loaded in order. 
 * Integration test using @XR23AHLCPP_left.json@ and file playback. 
 * Runtime verification with @ros2 topic echo /x_taxel_ah@. 

 h2. 14. Open Items 

 * Confirm whether a separate mapping file is needed for right-hand hardware. 

 ---------------- 

 h1. README 

 ROS 2 WebSocket client that converts Xela AH JSON into @xela_taxel_msgs/XTaxelSensorTArray@ 
 and publishes it on @/x_taxel_ah@ for Allegro Hand (left/right). 

 h2. Build 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 cd /home/invokelee/xela_robotics/02_dev_ws 
 colcon build --packages-select xela_server2_ah 
 </code></pre> 

 h2. Run (WebSocket mode) 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 run xela_server2_ah xela_server2_ah_node --ros-args \ 
   -p ws_host:=localhost \ 
   -p ws_port:=5000 \ 
   -p frame_ids_yaml:=/home/invokelee/xela_robotics/02_dev_ws/src/xela_apps/xela_server2_ah/config/server2_ah_config.yaml \ 
   -p hand_side:=left 
 </code></pre> 

 h2. Run (File playback mode) 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 run xela_server2_ah xela_server2_ah_node --ros-args \ 
   -p input_json_path:=/home/invokelee/xela_robotics/02_dev_ws/src/xela_apps/sim_xela_server/resource/XR23AHLCPP.json \ 
   -p playback_interval_ms:=200 \ 
   -p playback_loop:=true \ 
   -p hand_side:=left 
 </code></pre> 

 h2. Run (ROS time override) 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 run xela_server2_ah xela_server2_ah_node --ros-args \ 
   -p input_json_path:=/home/invokelee/xela_robotics/02_dev_ws/src/xela_apps/sim_xela_server/resource/XR23AHLCPP.json \ 
   -p use_ros_time_for_sensor_time:=true \ 
   -p hand_side:=left 
 </code></pre> 

 h2. Launch 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 launch xela_server2_ah xela_server2_ah.launch.py 
 </code></pre> 

 h2. Launch (Replayer + xela_server2_ah) 

 Uses @sim_xela_server@ with @XR23AHLCPP.json@ (left) or @XR23AHRCPP.json@ (right). 
 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 launch xela_server2_ah xela_server2_ah_with_replayer.launch.py \ 
   model_name:=XR23AHLCPP 
 </code></pre> 

 h2. Launch (Replayer + xela_server2_ah + xela_taxel_viz_ahv4) 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 launch xela_server2_ah xela_server2_ah_with_replayer_and_viz.launch.py \ 
   model_name:=XR23AHLCPP 
 </code></pre> 

 h2. Verify 

 <pre><code> 
 source /opt/ros/humble/setup.bash 
 source /home/invokelee/xela_robotics/02_dev_ws/install/setup.bash 
 ros2 topic echo /x_taxel_ah --once 
 </code></pre> 

 h2. Notes 

 * @frame_ids_yaml@ should point to @server2_ah_config.yaml@, which references 
   @server_model_joint_map.yaml@ via @mapping_yaml@. 
 * @hand_side@ controls the prefix mapping (@x_taxel_0_@ for left, @x_taxel_1_@ for right). 
 * Input arrays (@integer@, @calibrated@, @temp@) are mapped by joint order from 
   @server_model_joint_map.yaml@. 
 * Replayer server lives in @sim_xela_server@ and must be built/available. 

 



Back