Skip to content

Conversation

@mingxueg-nv
Copy link

@mingxueg-nv mingxueg-nv commented Oct 29, 2025

Description

Add Haply haptic device teleoperation support for robotic manipulation with force feedback.

Type of change

  • New feature (non-breaking change which adds functionality)
  1. Add haply devices API for teleoperation with force feedback
  2. Real-time teleoperation with force feedback demo via Haply Inverse3 handle

Haply device documentation and usage

Usage (make sure your Haply device is connected):

./isaaclab.sh -p scripts/demos/haply_teleoperation.py --websocket_uri ws://localhost:10001 --pos_sensitivity 1.65

Screenshots

haply-frankal

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@mingxueg-nv mingxueg-nv requested a review from ooctipus as a code owner October 29, 2025 07:49
@github-actions github-actions bot added isaac-mimic Related to Isaac Mimic team isaac-lab Related to Isaac Lab team labels Oct 29, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR introduces support for Haply haptic devices to Isaac Lab's teleoperation ecosystem, enabling 6-DOF robot control with bidirectional force feedback. The implementation adds a new HaplyDevice class following the existing DeviceBase pattern used by keyboard, gamepad, and spacemouse interfaces. The device communicates with Haply SDK via WebSocket in a separate thread to receive position/orientation from Inverse3 and VerseGrip hardware while sending force feedback commands. A standalone demo script (haply_teleoperation.py) demonstrates real-time teleoperation of a Franka Panda robot using differential IK control, mapping the Haply workspace to robot end-effector poses with contact-based force feedback. The changes integrate cleanly into the existing device factory pattern without modifying core teleoperation infrastructure.

Important Files Changed

Filename Score Overview
source/isaaclab/isaaclab/devices/init.py 5/5 Adds Haply device documentation and imports following existing device pattern
source/isaaclab/isaaclab/devices/haply/init.py 5/5 Creates new Haply module initialization exporting HaplyDevice and HaplyDeviceCfg
source/isaaclab/isaaclab/devices/teleop_device_factory.py 5/5 Registers HaplyDeviceCfg in device factory mapping for instantiation
source/isaaclab/isaaclab/devices/haply/se3_haply.py 3.5/5 Implements core Haply device controller with WebSocket communication and force feedback (hardcoded availability flag, potential connection logic issue)
scripts/demos/haply_teleoperation.py 3/5 Adds teleoperation demo with Franka Panda (hardcoded transforms, potential joint index conflict at line 318-320, force feedback always computed but conditionally applied)

Confidence score: 3/5

  • This PR requires careful review due to implementation issues in core device logic and the demo script
  • Score lowered due to: (1) hardcoded WEBSOCKETS_AVAILABLE flag that doesn't respect import failures in se3_haply.py, (2) connection logic using OR condition may allow operation with incomplete device setup, (3) potential joint index conflict in haply_teleoperation.py where joint 7 (index 6) could be set twice based on arm DOF, (4) hardcoded workspace mappings without validation, (5) no graceful WebSocket thread shutdown mechanism
  • Pay close attention to source/isaaclab/isaaclab/devices/haply/se3_haply.py (lines 18-20, 81-82, 264-266, 292-294) and scripts/demos/haply_teleoperation.py (lines211, 318-320) for potential runtime issues

Sequence Diagram

sequenceDiagram
    participant User
    participant HaplyDevice
    participant WebSocket
    participant SimulationContext
    participant Robot
    participant IKController
    participant ContactSensor

    User->>HaplyDevice: Initialize connection (websocket_uri)
    HaplyDevice->>WebSocket: Connect to Haply SDK
    WebSocket-->>HaplyDevice: Connection established
    HaplyDevice->>HaplyDevice: Start background thread
    
    User->>SimulationContext: Start simulation
    SimulationContext->>Robot: Initialize Franka Panda
    SimulationContext->>ContactSensor: Setup finger sensors
    
    loop Simulation Loop
        User->>HaplyDevice: Move handle / Press buttons
        HaplyDevice->>WebSocket: Request device state
        WebSocket-->>HaplyDevice: Return position, orientation, buttons
        HaplyDevice->>HaplyDevice: Cache data with lock
        
        SimulationContext->>HaplyDevice: advance()
        HaplyDevice-->>SimulationContext: Return [pos, quat, buttons]
        
        SimulationContext->>SimulationContext: apply_haply_to_robot_mapping()
        SimulationContext->>IKController: compute(target_pos, target_quat)
        IKController-->>SimulationContext: Return joint_pos_des
        
        SimulationContext->>Robot: set_joint_position_target()
        Robot->>Robot: Update joint positions
        
        alt Force Feedback Enabled
            ContactSensor->>ContactSensor: Measure net_forces_w
            SimulationContext->>ContactSensor: Get finger contact forces
            ContactSensor-->>SimulationContext: Return force vectors
            SimulationContext->>SimulationContext: Calculate feedback_force
            SimulationContext->>HaplyDevice: set_force_feedback(fx, fy, fz)
            HaplyDevice->>WebSocket: Send force command
            WebSocket-->>User: Apply haptic feedback
        end
        
        SimulationContext->>SimulationContext: step()
    end
    
    User->>SimulationContext: Close application
    SimulationContext->>HaplyDevice: Cleanup
    HaplyDevice->>WebSocket: Disconnect
Loading

5 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers only the changes made since the last review, not the entire PR. The latest changes involve two focused updates: (1) removal of a quaternion conversion comment from the teleoperation demo script, and (2) comprehensive removal of all inline comments and section dividers from the HaplyDevice class implementation. The comment removal in se3_haply.py reduces code verbosity by eliminating explanatory text for operations like "Apply sensitivity scaling" and "Detect rising edge", while preserving all docstrings and functional logic. This makes the code more concise but relies entirely on method names and docstrings for understanding the WebSocket communication, threading, and force feedback implementation. The demo script change is minimal, removing a redundant comment about quaternion conversion. Both changes align with a code style preference for self-documenting code over inline annotations, though this may impact readability for contributors unfamiliar with haptic device protocols or the Haply WebSocket API.

Important Files Changed

Filename Score Overview
scripts/demos/haply_teleoperation.py 4/5 Removed quaternion conversion comment on line 117; purely cosmetic change with no logic modifications
source/isaaclab/isaaclab/devices/haply/se3_haply.py 4/5 Stripped all inline comments and section dividers while preserving docstrings; no functional changes but reduced code documentation

Confidence score: 4/5

  • This PR is safe to merge with minimal risk as the changes are purely cosmetic comment removals with no functional modifications
  • Score reflects that while the changes reduce inline documentation, all core logic, docstrings, and functionality remain intact; no new bugs are introduced by these changes
  • No files require special attention as both changes are straightforward comment removals; previous review concerns about logic issues (lines 317-319 data race) and performance (deep copying at 200Hz) remain unaddressed but are not affected by these latest changes

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers only the changes made since the last review, not the entire PR. The most recent changes refactor the Haply teleoperation demo by removing the --enable_force_feedback CLI flag and making force feedback always-on. The se3_haply.py device API was enhanced with proper cleanup mechanisms, improved connection monitoring with timeout tracking, and optimized data processing by removing unnecessary deep copies. Key structural improvements include extracting workspace mapping constants to module-level scope, adding a close() method for graceful device shutdown with try-finally cleanup, and fixing the WebSocket message format to use proper list structure. The connection logic was changed from OR to AND semantics (requiring both Inverse3 and VerseGrip devices), which may affect partial-operation scenarios. The refactoring indicates maturation from experimental to production-ready feature status.

Important Files Changed

Filename Score Overview
scripts/demos/haply_teleoperation.py 3/5 Removes force feedback CLI flag, extracts workspace constants, adds try-finally cleanup; joint position overwrite issue on line 318-319 remains unresolved from previous review
source/isaaclab/isaaclab/devices/haply/se3_haply.py 4/5 Adds optional websockets import handling, implements close() method, improves connection monitoring with timeout tracking, removes deep copies in high-frequency loop; connection logic changed from OR to AND requiring both devices

Confidence score: 3/5

  • This PR has robustness improvements but contains an unresolved logic issue and a potentially breaking connection requirement change that needs verification
  • Score reflects: (1) the unresolved joint position overwrite issue in haply_teleoperation.py line 318-319 that was flagged in previous reviews but not fixed, which may discard IK-computed values for joint 7; (2) the connection logic change from OR to AND semantics in se3_haply.py line 275-277 that now requires BOTH Inverse3 and VerseGrip devices to be connected, which is a breaking change that may not be intentional and needs verification; (3) the PR description still references the removed --enable_force_feedback flag in the usage example, creating documentation inconsistency
  • Pay close attention to haply_teleoperation.py lines 318-320for the joint position assignment logic and se3_haply.py lines 275-277 for the device connection requirements

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers only changes made since the last review, focusing on thread management and cleanup improvements in the Haply device WebSocket connection handling. The changes address previous feedback by adding explicit thread joining with timeout in the close() method and ensuring _websocket_thread is initialized before the thread starts to prevent race conditions. The WebSocket thread is now non-daemon to enable proper cleanup through join(), though this requires that close() is called explicitly to avoid hanging the process at exit.

Important Files Changed

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 3/5 Improved WebSocket thread cleanup with explicit join and timeout handling; changed thread to non-daemon which requires explicit cleanup to prevent hangs

Confidence score: 3/5

  • This PR addresses previous feedback on thread cleanup but introduces a potential process hang if close() is not called before exit
  • Score reflects improved cleanup logic offset by the non-daemon thread change requiring explicit cleanup, plus unaddressed thread safety concerns between advance() and reset() methods accessing shared _prev_buttons state without synchronization
  • Pay close attention to the thread joining logic in close() (lines 165-168) and verify that close() is reliably called in all exit paths; also verify thread safety of _prev_buttons access between advance() and reset() methods

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers only the changes made since the last review, not the entire PR. The latest update adds thread-safe button state tracking to the Haply haptic device teleoperation API. A new _button_lock threading lock was introduced to protect concurrent access to the _prev_buttons dictionary, which tracks button states for rising-edge detection. The lock is used in both the reset() method and the advance() method where button press events are detected. This addresses a race condition where the WebSocket thread (running _websocket_loop()) updates button states while the main thread reads them in advance() for edge detection. The implementation follows the existing synchronization pattern used for force_lock and data_lock in the same class. Additionally, the cleanup logic in __del__ was enhanced to warn about daemon thread conversion if graceful shutdown fails.

Important Files Changed

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 3/5 Added _button_lock for thread-safe button state tracking to prevent race conditions during rising-edge detection

Confidence score: 3/5

  • This PR requires careful review due to potential issues with the thread safety implementation and callback execution within locked sections
  • Score reflects concerns about holding locks during callback invocation (lines 236-245) which could cause deadlocks if callbacks take significant time or attempt to acquire the same lock, and the lack of explicit timeout or error handling for lock acquisition
  • Pay close attention to the button event detection logic in advance() method where callbacks are invoked while holding _button_lock, as this could block the WebSocket thread if callbacks are slow

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers changes made since the last review, focusing on recent modifications to the Haply device controller. The refactor removes the connection_timeout parameter and time-based "data freshness" checks (get_device_state(), _is_data_fresh(), last_data_time) in favor of simpler binary connection state tracking. A critical logic change switches the connection check from OR to AND, now requiring both Inverse3 and VerseGrip devices to be connected simultaneously. Thread safety has been improved with a dedicated _connected_lock separating connection state from data synchronization, and the _button_lock was removed to allow callbacks to execute outside locks, addressing previous deadlock concerns. However, the refactor introduces race conditions where cached_data is accessed without holding data_lock, and array indexing operations lack bounds checking which could cause IndexErrors.

Important Files Changed

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 Refactored device connection management removing timeout logic, changed connection check from OR to AND, improved lock separation but introduced race conditions in cached_data access and missing bounds checks on array indexing

Confidence score: 2/5

  • This PR has significant thread-safety issues that could cause runtime failures or race conditions in production
  • Score reflects critical bugs: (1) cached_data accessed without data_lock at lines 331-334 during connection status updates, (2) Array indexing at lines 288-289 without bounds checking could raise IndexError if WebSocket returns empty arrays, (3) Connection status check at lines 206-217 is separated from data access creating a window where device could disconnect between checks
  • Pay close attention to lines 288-289 (array indexing), lines 331-334 (unlocked cached_data access), and lines 206-217 (connection check race condition). The AND logic change at line 332-333 should also be verified as intentional behavior change requiring both devices

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This review covers only the changes made since the last review, not the entire PR. The developer addressed critical race conditions and thread safety issues in the Haply device teleoperation implementation. The primary fix involves consolidating connection status checks under proper lock synchronization: the advance() method now reads connection state inside data_lock to prevent stale reads, while the WebSocket loop computes connection status locally before atomically updating both cached_data and the self.connected flag. Additional improvements include safer handling of empty device data arrays to prevent IndexError exceptions, and consistent connection state resets in exception handlers under appropriate locks. These changes maintain the existing two-lock design pattern while ensuring thread-safe state transitions between the WebSocket thread and the main simulation thread.

Important Files Changed

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 4/5 Fixed race condition by moving connection checks inside data_lock; improved empty list handling; made connection status updates atomic

Confidence score: 4/5

  • This PR is safer to merge than previous versions but still has some residual threading concerns that warrant attention.
  • Score reflects successful resolution of the critical race condition in connection status checking, proper synchronization of state updates, and improved exception safety. However, one point deducted because the callback invocation inside _button_lock (raised in previous reviews) remains unaddressed, which could potentially block the simulation loop or cause deadlocks if callbacks acquire other locks or perform time-consuming operations.
  • Pay close attention to the button callback logic within the lock in _update_button_callbacks() - consider testing scenarios where button callbacks interact with other locked resources or take significant execution time.

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds Haply haptic device (Inverse3 + VerseGrip) API and teleoperation demo for robotic manipulation with bidirectional force feedback.

Key Changes:

  • New HaplyDevice class in isaaclab/devices/haply/ with WebSocket-based communication (200Hz)
  • Teleoperation demo showing Franka Panda control with IK, gripper control, and force feedback
  • Integration into device factory pattern for seamless device switching
  • Position/orientation tracking from hardware devices with button callbacks

Issues Found:

  • Critical race condition in se3_haply.py:207-214: advance() reads cached_data without holding data_lock, causing potential data corruption when WebSocket thread updates the same data
  • Callback deadlock risk in se3_haply.py:217-224: Button callbacks invoked while holding _button_lock (though current code doesn't use _button_lock, the pattern in _prev_buttons update could block simulation if callbacks are slow)
  • IndexError risk in se3_haply.py:287-288: Empty device arrays from SDK will raise IndexError on [0] access
  • Breaking compatibility change in se3_haply.py:334: Connection logic changed from OR to AND, now requires both devices instead of allowing partial operation
  • Minor: Joint assignment pattern in demo overwrites IK solution for joint 7

Confidence Score: 2/5

  • Not safe to merge - contains critical race condition that can cause data corruption in multi-threaded environment
  • Score of 2 reflects the race condition in advance() method accessing cached_data without proper locking (lines 207-214 in se3_haply.py). This is a critical thread-safety issue where the main simulation thread reads device data while the WebSocket thread concurrently updates it, potentially causing torn reads or inconsistent state. The IndexError risk on empty arrays and the breaking compatibility change (OR→AND) are also concerning.
  • source/isaaclab/isaaclab/devices/haply/se3_haply.py requires immediate attention for race condition fix. The advance() method must acquire data_lock before reading cached_data.

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 Core Haply device implementation with several critical issues: race condition in advance() accessing cached_data without lock (line 207-214), callback deadlock risk with button state updates happening inside lock (line 217-224), IndexError risk on empty device arrays (line 287-288), and changed device connection logic from OR to AND breaking backwards compatibility (line 334)
scripts/demos/haply_teleoperation.py 3/5 Teleoperation demo with IK-based control and force feedback. Contains joint index mismatch where joint 7 (index 6) is set manually after IK solution includes it (line 318-319), and force feedback computed unconditionally every iteration (line 332-336)

Sequence Diagram

sequenceDiagram
    participant User
    participant Demo as haply_teleoperation.py
    participant Device as HaplyDevice
    participant WS as WebSocket Thread
    participant SDK as Haply SDK
    participant Robot as Franka Robot
    participant Sensors as Contact Sensors

    User->>Demo: Start teleoperation demo
    Demo->>Device: HaplyDevice(cfg)
    Device->>WS: Start WebSocket thread
    WS->>SDK: Connect via WebSocket
    SDK-->>WS: Connection established
    WS->>SDK: Receive device data (200Hz)
    SDK-->>WS: {inverse3: [], wireless_verse_grip: []}
    WS->>Device: Update cached_data (position, orientation, buttons)
    
    loop Simulation Loop (200Hz)
        Demo->>Device: advance()
        Device->>Device: Read cached_data (potential race condition)
        Device->>Device: Check button callbacks
        Device-->>Demo: [x,y,z, qx,qy,qz,qw, btn_a, btn_b, btn_c]
        Demo->>Demo: Apply coordinate mapping
        Demo->>Demo: Compute IK for arm joints
        Demo->>Robot: set_joint_position_target()
        Robot->>Sensors: Measure contact forces
        Sensors-->>Demo: net_forces_w
        Demo->>Device: set_force_feedback(fx, fy, fz)
        Device->>WS: Update feedback_force
        WS->>SDK: Send force command via WebSocket
        SDK-->>WS: Acknowledgment
    end

    User->>Demo: Stop simulation
    Demo->>Device: close()
    Device->>WS: running = False
    WS->>SDK: Close WebSocket connection
    Device->>Device: Wait for thread termination
Loading

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: mingxueg-nv <mingxueg@nvidia.com>
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Added comprehensive Haply device API for teleoperation with force feedback. The implementation provides WebSocket-based communication with Haply Inverse3 and VerseGrip devices, enabling real-time position tracking, orientation sensing, button input, and bidirectional force feedback at 200Hz.

Key changes:

  • New HaplyDevice class with thread-safe data handling using multiple locks
  • WebSocket client running in dedicated thread for async communication
  • Device connection monitoring with timeout detection and auto-reconnection
  • Force feedback support sent to Inverse3 device
  • Button event callbacks with rising edge detection
  • Proper cleanup and thread termination in close() method

Latest commit (243b8ae) addresses critical race condition by adding .copy() calls inside the data lock when reading position and quaternion arrays, preventing potential data corruption from concurrent access.

Confidence Score: 4/5

  • Safe to merge with minor style improvements recommended
  • The latest commit fixed the critical race condition identified in previous review. Thread safety mechanisms are properly implemented with appropriate locks. Connection handling and error recovery are robust. Remaining comments are style/performance suggestions that don't affect correctness.
  • No files require special attention - the critical race condition has been resolved

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 4/5 New Haply device API with WebSocket communication, force feedback, and thread-safe data handling. Latest commit fixes critical race condition.

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant HD as HaplyDevice
    participant WSThread as WebSocket Thread
    participant HaplySDK as Haply SDK Server

    App->>HD: __init__(cfg)
    HD->>HD: Initialize locks & data structures
    HD->>WSThread: Start WebSocket thread
    WSThread->>HaplySDK: Connect (WebSocket)
    
    loop Every 1/data_rate seconds (200Hz default)
        HaplySDK->>WSThread: Send device state (JSON)
        WSThread->>WSThread: Parse position, quaternion, buttons
        WSThread->>HD: Update cached_data (with data_lock)
        WSThread->>HD: Update connection status
        WSThread->>HaplySDK: Send force feedback (with force_lock)
    end
    
    App->>HD: advance()
    HD->>HD: Check connection status
    HD->>HD: Copy position, quaternion, buttons (with data_lock)
    HD->>HD: Trigger button callbacks if pressed
    HD->>App: Return torch.Tensor[10]
    
    App->>HD: set_force_feedback(fx, fy, fz)
    HD->>HD: Update feedback_force (with force_lock)
    
    App->>HD: close()
    HD->>HD: Set running=False, reset force
    HD->>WSThread: Wait for thread termination
    WSThread->>HaplySDK: Disconnect
Loading

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds comprehensive Haply haptic device support for robotic teleoperation with bidirectional force feedback. The implementation includes a WebSocket-based device controller (se3_haply.py) that connects to Haply Inverse3 (position tracking) and VerseGrip (orientation/buttons) devices, plus a complete teleoperation demo (haply_teleoperation.py) showing Franka Panda arm control with contact-based force feedback.

Key additions:

  • HaplyDevice class with async WebSocket communication, thread-safe data handling, and force feedback API
  • Teleoperation demo with IK-based position control, workspace mapping, and contact sensor force feedback
  • Comprehensive test coverage with proper mocking for device constructors

Issues identified:

  • Critical joint indexing bug in demo (line 317): IK computes 6 joints but [:-1] slice incorrectly handles assignment
  • Previous comments highlighted race conditions in se3_haply.py (data_lock usage, button callback timing) and connection logic issues

Confidence Score: 2/5

  • This PR has critical bugs that will cause incorrect robot motion and previous unresolved race conditions
  • Score reflects one new critical logic error in joint assignment (line 317) that will cause incorrect IK joint application, plus multiple unresolved issues from previous review including race conditions in se3_haply.py that could cause thread safety problems and data corruption. The test coverage is excellent but doesn't catch the runtime logic bug.
  • scripts/demos/haply_teleoperation.py requires immediate fix to joint indexing logic before merge

Important Files Changed

File Analysis

Filename Score Overview
scripts/demos/haply_teleoperation.py 3/5 New teleoperation demo with Haply device - has joint indexing issue where IK output for 6 joints is assigned but joint 7 is also referenced causing potential mismatch
source/isaaclab/test/devices/test_device_constructors.py 5/5 Comprehensive test coverage added for Haply device with proper mocking of WebSocket connections and device states

Sequence Diagram

sequenceDiagram
    participant User as User/Haply Device
    participant WS as WebSocket Thread
    participant Device as HaplyDevice
    participant Demo as haply_teleoperation.py
    participant IK as IK Controller
    participant Robot as Franka Robot
    participant Sensor as Contact Sensors
    
    Note over User,WS: Device Initialization
    User->>WS: Connect via WebSocket
    WS->>Device: Receive position (Inverse3)
    WS->>Device: Receive orientation & buttons (VerseGrip)
    Device->>Device: Cache data with lock
    
    Note over Demo,Robot: Teleoperation Loop
    Demo->>Device: advance()
    Device->>Demo: Return [pos, quat, buttons]
    Demo->>Demo: Map Haply workspace to robot frame
    Demo->>IK: set_command(target_pos, ee_quat)
    IK->>Demo: Compute joint positions (6 joints)
    Demo->>Robot: set_joint_position_target()
    Robot->>Robot: Execute motion (5 sim steps)
    
    Note over Sensor,Device: Force Feedback Path
    Robot->>Sensor: Contact forces on fingers
    Sensor->>Demo: Read net_forces_w
    Demo->>Demo: Compute feedback_force (clamped)
    Demo->>Device: set_force_feedback(fx, fy, fz)
    Device->>Device: Store in feedback_force with lock
    WS->>Device: Get feedback_force
    WS->>User: Send cursor_force command to Inverse3
Loading

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds Haply haptic device teleoperation support with bidirectional force feedback for robotic manipulation. The implementation includes a WebSocket-based device API (HaplyDevice) and a complete demo script for controlling a Franka Panda arm.

Key Changes:

  • New HaplyDevice API providing position tracking (Inverse3), orientation/buttons (VerseGrip), and force feedback
  • Teleoperation demo with IK control for 6-DOF arm positioning, button-based gripper/rotation control, and contact-based haptic feedback
  • Thread-safe WebSocket communication with proper connection monitoring and auto-reconnect
  • Workspace safety limits and coordinate mapping from Haply to robot frame

Recent Fix:

  • Latest commit (13c4047) correctly fixed IK joint index issue - now properly assigns all 6 IK-computed joints to panda_joint1-6 via arm_joint_indices

Minor Issues:

  • VerseGrip orientation data computed but unused (intentional design for button-based rotation)
  • Python list vs numpy array inconsistency for haply_initial_pos
  • Previous comments cover threading, race conditions, and error handling in device API

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • The recent commit fixed the critical IK joint index issue. The implementation is functionally correct with proper error handling, thread safety in the device API, and workspace safety limits. Previous review comments identified race conditions and threading issues in the device API that should be addressed, but the demo script itself is solid. The unused orientation data is a design choice rather than a bug.
  • No files require special attention - the IK index bug was already fixed in commit 13c4047

Important Files Changed

File Analysis

Filename Score Overview
scripts/demos/haply_teleoperation.py 4/5 Teleoperation demo correctly fixed IK joint index issue; orientation from VerseGrip unused but this appears intentional for button-based rotation control

Sequence Diagram

sequenceDiagram
    participant User
    participant HaplyDevice
    participant WebSocket
    participant Simulation
    participant Robot
    participant IKController
    participant ContactSensor

    User->>HaplyDevice: Initialize(websocket_uri, pos_sensitivity)
    HaplyDevice->>WebSocket: Connect to Haply SDK
    WebSocket-->>HaplyDevice: Connection established
    HaplyDevice->>HaplyDevice: Start WebSocket thread
    WebSocket-->>HaplyDevice: Device data (Inverse3 + VerseGrip)
    HaplyDevice-->>User: Device ready

    loop Teleoperation Loop
        Simulation->>HaplyDevice: advance()
        HaplyDevice-->>Simulation: [pos, quat, buttons]
        
        Simulation->>Simulation: apply_haply_to_robot_mapping()
        Simulation->>Simulation: Handle button events (gripper, rotation)
        
        Simulation->>IKController: set_command(target_pos, ee_quat)
        IKController->>Robot: Get current_joint_pos, jacobian
        Robot-->>IKController: Joint states
        IKController-->>Simulation: joint_pos_des (6 joints)
        
        Simulation->>Robot: set_joint_position_target()
        
        loop 5 simulation steps
            Simulation->>Simulation: write_data_to_sim()
            Simulation->>Simulation: step()
        end
        
        Simulation->>Simulation: update(sim_dt)
        
        Simulation->>ContactSensor: Get finger forces
        ContactSensor-->>Simulation: net_forces_w
        Simulation->>Simulation: Calculate feedback_force
        Simulation->>HaplyDevice: set_force_feedback(fx, fy, fz)
        
        HaplyDevice->>WebSocket: Send force command to Inverse3
        WebSocket-->>User: Apply haptic feedback
    end
Loading

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile


return torch.tensor(command, dtype=torch.float32, device=self._sim_device)

def set_force_feedback(self, force_x: float, force_y: float, force_z: float):
Copy link
Contributor

@rwiltz rwiltz Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we generalized this and added it to the DeviceBase interface, so that this teleop device could be used in the exisiting teleop/record_demos scripts.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rwiltz thank you for your suggestion, I have added this interface into DeviceBase, the input is force from contact sensor, set to fx, fy, fz, and the output is customed to adapt to different force feedback devices.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds support for Haply haptic devices (Inverse3 + VerseGrip) to Isaac Lab, enabling teleoperation with bidirectional force feedback.

Key changes:

  • New HaplyDevice class with WebSocket-based communication at 200Hz
  • Position tracking from Inverse3 device with configurable sensitivity
  • Orientation and 3-button input from VerseGrip device
  • Force feedback support with safety limits (default 2.0N)
  • Demo script showing Franka Panda manipulation with IK control and contact-based haptic feedback
  • Integration with device factory and comprehensive test coverage
  • Added set_force_feedback() method to DeviceBase interface

Architecture:
The implementation uses a background thread for WebSocket communication, maintaining cached device state that's read by the main simulation loop. Force feedback is computed from contact sensors and sent back to the device with safety clamping.

Concerns:
Several threading synchronization issues were identified in previous comments that could lead to race conditions or data corruption in the se3_haply.py implementation. The teleoperation demo also has a joint indexing inconsistency that needs verification.

Confidence Score: 3/5

  • Approve with caution - core functionality is sound but threading issues need attention before production use
  • Score reflects well-designed API and comprehensive test coverage, but critical race conditions in WebSocket thread synchronization (advance() method accessing cached_data outside lock) and potential IndexError from empty device arrays need resolution. Demo script also has joint indexing questions. These issues won't prevent basic functionality but could cause intermittent failures under real-world conditions.
  • source/isaaclab/isaaclab/devices/haply/se3_haply.py requires careful review of threading logic (lines 209-214, 275, 288, 331). scripts/demos/haply_teleoperation.py needs verification of joint index assignments (lines 317-324).

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 3/5 Implements WebSocket-based Haply device interface with position/orientation tracking and force feedback. Several critical threading issues noted in previous comments need resolution.
scripts/demos/haply_teleoperation.py 3/5 Teleoperation demo integrating Haply with Franka Panda robot via IK control. Some indexing issues with joint assignments flagged in previous comments.
source/isaaclab/isaaclab/devices/device_base.py 5/5 Added set_force_feedback method to base device interface with proper default implementation. Clean abstraction for force feedback support.
source/isaaclab/test/devices/test_device_constructors.py 4/5 Comprehensive test coverage for HaplyDevice including mocked WebSocket connections, force feedback limits, and factory integration tests.

Sequence Diagram

sequenceDiagram
    participant User
    participant Demo as haply_teleoperation.py
    participant Device as HaplyDevice
    participant WebSocket as WebSocket Thread
    participant HaplySDK as Haply SDK
    participant Robot as Franka Panda
    participant Sensor as Contact Sensors

    User->>Demo: Start teleoperation demo
    Demo->>Device: HaplyDevice(cfg)
    Device->>WebSocket: Start WebSocket thread
    WebSocket->>HaplySDK: Connect ws://localhost:10001
    HaplySDK-->>WebSocket: Connection established
    WebSocket->>HaplySDK: Receive device data (Inverse3 + VerseGrip)
    WebSocket->>Device: Update cached_data (position, quaternion, buttons)
    
    loop Simulation Loop (200Hz)
        Demo->>Device: advance()
        Device-->>Demo: [pos, quat, buttons] tensor
        Demo->>Demo: Map Haply coordinates to robot workspace
        Demo->>Demo: Compute IK for joints 1-6
        Demo->>Demo: Set joint 7 rotation from Button C
        Demo->>Demo: Set gripper from Buttons A/B
        Demo->>Robot: set_joint_position_target()
        Robot->>Robot: Execute motion (5 sim steps)
        
        Robot->>Sensor: Update contact forces
        Sensor-->>Demo: net_forces_w (left + right fingers)
        Demo->>Device: set_force_feedback(fx, fy, fz)
        Device->>Device: Clip forces to limit_force
        Device->>WebSocket: Update feedback_force
        WebSocket->>HaplySDK: Send force command to Inverse3
        HaplySDK->>User: Apply haptic feedback
    end
    
    User->>Demo: Stop demo
    Demo->>Device: close()
    Device->>WebSocket: running = False
    WebSocket->>HaplySDK: Disconnect
    WebSocket-->>Device: Thread terminated
Loading

8 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds Haply haptic device support (Inverse3 + VerseGrip) for robot teleoperation with bidirectional force feedback. Implementation includes device API, WebSocket-based communication, and a Franka Panda teleoperation demo.

Major Changes:

  • New HaplyDevice class implementing DeviceBase interface with WebSocket communication for position/orientation tracking and force feedback
  • Teleoperation demo using IK control for 6-DOF arm positioning with manual joint 7 rotation control
  • Added optional set_force_feedback() method to DeviceBase for haptic devices
  • Comprehensive test coverage with WebSocket and async mocking

Issues Found:

  • Critical: Demo calls non-existent haply_device.close() method (line 376) - will crash at cleanup
  • Hardcoded WEBSOCKETS_AVAILABLE = True despite import that can fail (se3_haply.py:19)
  • Button callbacks executed inside lock, risking deadlock if callbacks are slow (se3_haply.py:224-225)
  • IK computes 6 joints but joint assignment logic needs verification against Panda's actual joint configuration

Confidence Score: 2/5

  • Not safe to merge - will crash at cleanup due to missing close() method
  • Critical bug in demo (AttributeError from missing close() method) blocks normal cleanup. Additionally, hardcoded WEBSOCKETS_AVAILABLE flag and lock management issues in button callbacks pose reliability concerns.
  • scripts/demos/haply_teleoperation.py (missing close() method) and source/isaaclab/isaaclab/devices/haply/se3_haply.py (hardcoded flag, callback lock issues)

Important Files Changed

File Analysis

Filename Score Overview
scripts/demos/haply_teleoperation.py 2/5 Teleoperation demo with critical AttributeError (missing close() method), potential joint indexing issues, and unused target_quat
source/isaaclab/isaaclab/devices/haply/se3_haply.py 3/5 Core Haply device implementation with hardcoded WEBSOCKETS_AVAILABLE flag and button callback execution inside lock
source/isaaclab/isaaclab/devices/device_base.py 5/5 Added optional force feedback interface to base class - clean implementation with default no-op behavior
source/isaaclab/test/devices/test_device_constructors.py 4/5 Comprehensive test coverage for Haply device with proper mocking of WebSocket and async operations

Sequence Diagram

sequenceDiagram
    participant User
    participant Demo as haply_teleoperation.py
    participant Device as HaplyDevice
    participant WS as WebSocket Thread
    participant SDK as Haply SDK
    participant Robot as Franka Robot
    participant Sensor as Contact Sensors

    User->>Demo: Start demo
    Demo->>Device: Create HaplyDevice(cfg)
    Device->>WS: Start WebSocket thread
    WS->>SDK: Connect to ws://localhost:10001
    SDK-->>WS: Connection established
    WS->>SDK: Receive device data (Inverse3 + VerseGrip)
    WS->>Device: Update cached_data (position, quaternion, buttons)
    Device-->>Demo: Device ready

    loop Simulation Loop (200Hz)
        Demo->>Device: advance()
        Device->>Device: Lock data_lock
        Device->>Device: Copy position, quaternion, buttons
        Device->>Device: Unlock data_lock
        Device->>Device: Check button callbacks
        Device-->>Demo: Return tensor [pos(3), quat(4), buttons(3)]
        
        Demo->>Demo: Apply coordinate mapping
        Demo->>Demo: Compute IK for joints 1-6
        Demo->>Robot: Set joint targets (IK + joint7 + gripper)
        Demo->>Robot: Step simulation (5 steps)
        
        Robot->>Sensor: Update contact sensors
        Sensor-->>Demo: Get contact forces
        Demo->>Device: set_force_feedback(fx, fy, fz)
        Device->>Device: Lock force_lock
        Device->>Device: Update feedback_force
        Device->>Device: Unlock force_lock
        
        WS->>Device: Lock force_lock
        WS->>Device: Copy feedback_force
        WS->>Device: Unlock force_lock
        WS->>SDK: Send force command to Inverse3
    end

    Demo->>Device: close()
    Note over Demo,Device: AttributeError: no close() method!
    Device->>Device: __del__ called
    Device->>WS: Set running=False
    WS->>SDK: Disconnect WebSocket
Loading

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds Haply haptic device integration with bidirectional force feedback for robotic teleoperation. The implementation includes a WebSocket-based device API (HaplyDevice) and a demonstration script showing real-time teleoperation of a Franka Panda arm with IK control.

Key changes:

  • New HaplyDevice class with WebSocket communication thread for position/orientation tracking and force feedback
  • Teleoperation demo with differential IK control, coordinate mapping, and contact-based force feedback
  • Button-based gripper and end-effector rotation control

Issues identified in previous comments:

  • Critical: Race conditions in se3_haply.py with data_lock and cached_data access (lines 209, 331)
  • Critical: Joint indexing mismatch in demo - IK computes 6 joints but assignment expects 7 (line 317)
  • Critical: Missing error handling for WebSocket import and empty device arrays (IndexError risks)
  • Important: Thread lifecycle issues in __del__ and connection status logic changed from OR to AND
  • Multiple style improvements for performance and code clarity

Confidence Score: 2/5

  • This PR has multiple critical issues that need resolution before merging
  • Score reflects critical race conditions in device implementation, joint indexing bugs in the demo, and missing error handling for WebSocket operations. The core functionality is sound but thread safety and array bounds checking must be addressed to prevent runtime errors and data corruption during high-frequency teleoperation.
  • Primary attention needed on source/isaaclab/isaaclab/devices/haply/se3_haply.py for race conditions (lines 209, 331) and connection logic (line 275), and scripts/demos/haply_teleoperation.py for joint indexing fix (line 327)

Important Files Changed

File Analysis

Filename Score Overview
scripts/demos/haply_teleoperation.py 3/5 New teleoperation demo with IK control logic. Previous comments identified critical joint indexing issue on line 327 and unused target_quat variable. Force feedback logic is sound but could be optimized.

Sequence Diagram

sequenceDiagram
    participant User as Haply Device<br/>(Inverse3 + VerseGrip)
    participant WS as WebSocket Thread
    participant Device as HaplyDevice
    participant Demo as haply_teleoperation.py
    participant IK as IK Controller
    participant Robot as Franka Robot
    participant Sensors as Contact Sensors

    Note over User,Sensors: Initialization Phase
    Demo->>Device: HaplyDevice(cfg)
    Device->>WS: Start WebSocket thread (daemon)
    WS->>User: Connect to Haply SDK
    Demo->>Robot: Initialize to default pose
    Demo->>IK: Create DifferentialIKController

    Note over User,Sensors: Main Control Loop (200Hz)
    loop Every Simulation Step
        Demo->>Device: advance()
        Device->>WS: Read cached_data (with data_lock)
        WS-->>Device: position, quaternion, buttons
        Device-->>Demo: haply_data [pos, quat, buttons]
        
        Demo->>Demo: Process button inputs<br/>(gripper, rotation)
        Demo->>Demo: apply_haply_to_robot_mapping()<br/>(coordinate transform)
        
        Demo->>Robot: Get current EE pose & jacobian
        Robot-->>Demo: ee_pos_w, ee_quat_w, jacobian
        
        Demo->>IK: set_command(target_pos, ee_quat)
        Demo->>IK: compute(ee_pos, ee_quat, jacobian, joint_pos)
        IK-->>Demo: joint_pos_des (6 joints)
        
        Demo->>Robot: set_joint_position_target()<br/>(joints 0-5 from IK, joint 6 manual, gripper)
        Demo->>Robot: step simulation (5x)
        
        Robot->>Sensors: Update contact forces
        Sensors-->>Demo: left_finger_forces, right_finger_forces
        Demo->>Demo: Calculate total_contact_force
        Demo->>Device: set_force_feedback(fx, fy, fz)
        Device->>WS: Update force_feedback (with force_lock)
        WS->>User: Send force feedback to device
    end
Loading

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: mingxueg <mingxueg@nvidia.com>
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds a new teleoperation demo (scripts/demos/haply_teleoperation.py) that integrates Haply haptic devices (Inverse3 + VerseGrip) with Isaac Lab's Franka Panda robot arm. The implementation provides real-time robot control with bidirectional force feedback via WebSocket communication.

Key changes:

  • New teleoperation script with IK-based position control for 6-DOF arm manipulation
  • Button-based gripper control (open/close) and end-effector rotation
  • Contact sensor integration for haptic force feedback to the operator
  • Workspace limits and coordinate mapping between Haply and robot frames

Critical issues identified:

  • IK joint assignment logic error that may cause incorrect robot movement (see previous comments on line 317/327)
  • Race conditions in the underlying HaplyDevice implementation affecting data consistency and connection status checks
  • Missing cleanup handling (close() method doesn't exist)
  • Several unused variables that could cause confusion

Confidence Score: 2/5

  • This PR has critical logic errors in joint control and race conditions that could cause unexpected robot behavior
  • Score reflects multiple critical issues: (1) IK joint assignment error on line 327 where 6 IK joints are assigned but joint 7 is also set separately, potentially causing incorrect robot motion; (2) Race conditions in HaplyDevice.advance() where data_lock is released before accessing copied array references; (3) Missing close() method causing AttributeError at cleanup; (4) Force feedback updates happen every iteration without checking if device supports it. The underlying device implementation also has connection logic changed from OR to AND which breaks backward compatibility. These issues need resolution before merge.
  • Pay close attention to scripts/demos/haply_teleoperation.py - the IK joint assignment logic (lines 315-329) needs careful review and testing. The underlying source/isaaclab/isaaclab/devices/haply/se3_haply.py also has race conditions that impact this demo.

Important Files Changed

File Analysis

Filename Score Overview
scripts/demos/haply_teleoperation.py 2/5 New teleoperation demo with Haply device integration. Contains critical logic errors in IK joint assignment and potential race conditions in force feedback. Multiple style issues with unused variables.

Sequence Diagram

sequenceDiagram
    participant User
    participant TeleopScript as haply_teleoperation.py
    participant HaplyDevice as HaplyDevice
    participant WebSocket as WebSocket Thread
    participant HaplySDK as Haply SDK
    participant IKController as IKController
    participant Robot as Franka Robot
    participant ContactSensors as Contact Sensors

    User->>TeleopScript: Start demo
    TeleopScript->>HaplyDevice: Initialize(cfg)
    HaplyDevice->>WebSocket: Start WebSocket thread
    WebSocket->>HaplySDK: Connect ws://localhost:10001
    HaplySDK-->>WebSocket: Connection established
    WebSocket->>HaplySDK: Request device data
    HaplySDK-->>WebSocket: Device data (Inverse3 + VerseGrip)
    WebSocket->>HaplyDevice: Update cached_data
    HaplyDevice-->>TeleopScript: Device ready
    
    TeleopScript->>Robot: Initialize robot state
    TeleopScript->>IKController: Initialize IK controller
    
    loop Simulation Loop
        TeleopScript->>HaplyDevice: advance()
        HaplyDevice->>HaplyDevice: Read cached_data (position, quaternion, buttons)
        HaplyDevice-->>TeleopScript: Return [pos(3), quat(4), buttons(3)]
        
        TeleopScript->>TeleopScript: Process button inputs (gripper, rotation)
        TeleopScript->>TeleopScript: apply_haply_to_robot_mapping()
        TeleopScript->>IKController: compute(target_pos, jacobian)
        IKController-->>TeleopScript: joint_pos_des (6 joints)
        
        TeleopScript->>Robot: set_joint_position_target(joints 1-6, joint 7, gripper)
        Robot->>Robot: Execute motion (5 sim steps)
        
        Robot->>ContactSensors: Update contact forces
        ContactSensors-->>TeleopScript: net_forces_w (left + right fingers)
        
        TeleopScript->>HaplyDevice: set_force_feedback(force_x, force_y, force_z)
        HaplyDevice->>HaplyDevice: Update feedback_force (with lock)
        
        par WebSocket Loop (async)
            WebSocket->>HaplyDevice: Read feedback_force (with lock)
            WebSocket->>HaplySDK: Send force command to Inverse3
            HaplySDK->>User: Apply haptic feedback
        end
    end
    
    User->>TeleopScript: Stop demo
    TeleopScript->>HaplyDevice: Cleanup (__del__)
    HaplyDevice->>WebSocket: Stop thread
    WebSocket->>HaplySDK: Disconnect
Loading

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

- IK joint assignment is correct (6 from IK + joint7 from button)
- No race conditions (.copy() ensures thread safety)
- close() merged into __del__() (consistent with other devices)
- Force feedback updates are necessary for haptic devices
- AND logic correct (Haply needs both Inverse3 + VerseGrip)
- Add orientation_sensitivity to tests
- Remove unused quaternion return value
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds Haply haptic device API (HaplyDevice class in se3_haply.py) for teleoperation with force feedback, integrated with a Franka Panda demo script that uses IK control and contact sensors for bidirectional haptic feedback.

Key Changes:

  • WebSocket-based communication with Haply SDK for Inverse3 (position + force feedback) and VerseGrip (orientation + buttons)
  • Real-time teleoperation at 200Hz with force clipping for safety
  • Demo script maps Haply workspace to robot end-effector with IK control for 6 joints
  • Contact sensors on gripper fingers provide force feedback to the operator

Critical Issues:

  • Race condition in advance() method where cached_data arrays are accessed after lock release (se3_haply.py:209-214)
  • Invalid quaternion scaling by orientation_sensitivity breaks normalization (se3_haply.py:214)
  • Joint indexing mismatch between IK output (6 joints) and array assignment (se3_haply.py:317-320)

Other Issues:

  • Missing dependency handling for websockets import
  • Callback invocations within locks may cause deadlock
  • IndexError risk when device arrays are empty
  • Thread cleanup incomplete in __del__

Confidence Score: 2/5

  • This PR has multiple critical issues including race conditions, invalid quaternion operations, and joint indexing mismatches that will cause runtime errors
  • Score reflects three critical issues that need resolution: (1) race condition in data access that can corrupt position/quaternion data during teleoperation, (2) quaternion scaling that violates normalization requirements and will cause undefined rotation behavior, (3) joint indexing bug where IK computes 6 joints but assignment logic expects 7. Additional concerns include missing error handling for empty device arrays, potential deadlocks from callbacks inside locks, and incomplete thread cleanup
  • Pay close attention to source/isaaclab/isaaclab/devices/haply/se3_haply.py - critical race condition in advance() method (lines 209-217) and invalid quaternion scaling must be fixed. Also review scripts/demos/haply_teleoperation.py joint assignment logic (lines 319-320)

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 New Haply device API with WebSocket-based teleoperation and force feedback. Critical race condition in data access and invalid quaternion scaling found, plus several threading and error handling issues
scripts/demos/haply_teleoperation.py 3/5 Teleoperation demo integrating Haply device with Franka robot using IK control. Has joint indexing issue and minor inefficiencies in force feedback computation

Sequence Diagram

sequenceDiagram
    participant User
    participant HaplySDK
    participant HaplyDevice
    participant WSThread
    participant TeleopDemo
    participant IKController
    participant Robot
    participant ContactSensor

    User->>TeleopDemo: Start demo
    TeleopDemo->>HaplyDevice: Initialize(cfg)
    HaplyDevice->>WSThread: Start WebSocket thread
    WSThread->>HaplySDK: Connect(websocket_uri)
    HaplySDK-->>WSThread: Connection established
    WSThread->>HaplySDK: Receive device data
    HaplySDK-->>WSThread: {inverse3, verse_grip}
    WSThread->>HaplyDevice: Update cached_data (position, quaternion, buttons)
    
    loop Simulation Loop
        TeleopDemo->>HaplyDevice: advance()
        HaplyDevice-->>TeleopDemo: [x, y, z, qx, qy, qz, qw, btn_a, btn_b, btn_c]
        TeleopDemo->>TeleopDemo: apply_haply_to_robot_mapping()
        TeleopDemo->>IKController: compute(target_pos, ee_quat, jacobian)
        IKController-->>TeleopDemo: joint_pos_des[6 joints]
        TeleopDemo->>Robot: set_joint_position_target()
        Robot->>Robot: Execute motion
        Robot->>ContactSensor: Update contact forces
        ContactSensor-->>TeleopDemo: net_forces_w
        TeleopDemo->>HaplyDevice: set_force_feedback(fx, fy, fz)
        HaplyDevice->>WSThread: Update feedback_force
        WSThread->>HaplySDK: send({set_cursor_force})
        HaplySDK-->>User: Apply force feedback
    end

Loading

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: mingxueg <mingxueg@nvidia.com>
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This update addresses critical issues from the previous review of the Haply device API implementation. Key improvements include:

  • Fixed websockets import handling with proper try-except block (previously hardcoded to True)
  • Improved thread cleanup in __del__ with explicit join and 2s timeout
  • Resolved race condition by copying all data inside data_lock (lines 213-217)
  • Removed invalid quaternion multiplication by orientation_sensitivity
  • Maintains AND logic for dual-device connection requirement (both Inverse3 and VerseGrip)
  • Button callbacks now invoked outside data lock to prevent blocking

The implementation provides a robust WebSocket-based interface for teleoperation with force feedback, handling real-time data streaming at 200Hz between the Haply SDK and Isaac Lab.

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk - addresses all critical threading and concurrency issues from previous review
  • The code fixes all critical bugs identified in the previous review (race conditions, import handling, thread cleanup, quaternion math). Thread safety is now properly implemented with correct lock usage. Minor style suggestions remain but don't affect correctness.
  • No files require special attention - all critical issues have been resolved

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 4/5 Addresses most critical issues from previous review - proper websocket import handling, thread cleanup, data locking, and quaternion handling now correct

Sequence Diagram

sequenceDiagram
    participant App as Teleoperation Demo
    participant HD as HaplyDevice
    participant WSThread as WebSocket Thread
    participant SDK as Haply SDK
    participant I3 as Inverse3 Device
    participant VG as VerseGrip Device

    App->>HD: __init__(cfg)
    HD->>HD: Start WebSocket thread
    HD->>WSThread: _start_websocket_thread()
    WSThread->>SDK: Connect to ws://localhost:10001
    
    loop Every 1/200s (5ms)
        SDK->>WSThread: Send device state (JSON)
        WSThread->>WSThread: Parse position from Inverse3
        WSThread->>WSThread: Parse orientation & buttons from VerseGrip
        WSThread->>HD: Update cached_data (with data_lock)
        
        Note over HD,WSThread: Force feedback path
        App->>HD: set_force_feedback(fx, fy, fz)
        HD->>HD: Store in feedback_force (with force_lock)
        WSThread->>HD: Read feedback_force (with force_lock)
        WSThread->>SDK: Send cursor force command
        SDK->>I3: Apply force feedback
    end
    
    loop Simulation Loop
        App->>HD: advance()
        HD->>HD: Read cached_data (with data_lock)
        HD->>HD: Apply pos_sensitivity scaling
        HD->>HD: Detect button press edges
        HD->>App: Return [pos(3), quat(4), buttons(3)]
        
        App->>App: Compute IK for target position
        App->>App: Calculate force feedback from sensors
        App->>HD: set_force_feedback(fx, fy, fz)
    end
    
    App->>HD: __del__()
    HD->>HD: Set running = False
    HD->>WSThread: Wait for thread (join timeout=2s)
    WSThread->>SDK: Close WebSocket connection
Loading

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

@mingxueg-nv
Copy link
Author

Hi, @rwiltz , @kellyguo11, I resolved the most comments, please help review this PR again, thanks🙏

"""
raise NotImplementedError

def set_force_feedback(self, force_x: float, force_y: float, force_z: float) -> Any:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, but can we make it a little more general since we expect more devices to be coming soon? Maybe we can add something like:

    # -----------------------------
    # Optional force/torque feedback
    # -----------------------------
    def push_force(self, forces: torch.Tensor, names: list[str] | None = None, frame: str = "world") -> None:
        """Push one or more 3D force vectors to the device (optional; default no-op).

        Args:
            forces: Tensor of shape (N, 3) with forces [fx, fy, fz].
            names: Optional labels for each force channel (e.g., ["ee", "finger_1"]).
            frame: Frame of the vectors: "world" (default) or "device".

        Note:
            Devices that support haptics should override this method to forward
            forces to their hardware API. Devices that do not support haptics
            may ignore this call.
        """
        return  # no-op by default

    def push_wrench(self, wrenches: torch.Tensor, names: list[str] | None = None, frame: str = "world") -> None:
        """Push one or more 6D wrenches to the device (optional; default no-op).

        Args:
            wrenches: Tensor of shape (N, 6) with [fx, fy, fz, tx, ty, tz].
            names: Optional labels for each wrench channel (e.g., ["ee", "finger_1"]).
            frame: Frame of the vectors: "world" (default) or "device".

        Note:
            Devices that support haptics should override this method. Devices that
            do not support haptics may ignore this call.
        """
        return  # no-op by default`
        ```
        
        Does that make sense?

Copy link
Contributor

@rwiltz rwiltz Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also do we need a "clear_force" call to reset all the forces?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just update your function to def push_force(self, forces: torch.Tensor, names: list[str] | None = None, frame: str = "world") -> None: we can merge this and add the rest later incrementally

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @rwiltz, thank you for your good point. I'll use push_force and test with office's haply device tomorrow - will push the commit once verified.

@kellyguo11 kellyguo11 moved this to In progress in Isaac Lab Nov 1, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Adds comprehensive Haply haptic device support (Inverse3 + VerseGrip) for teleoperation with force feedback. The implementation includes a WebSocket-based device driver with multi-threaded communication at 200Hz, a teleoperation demo using IK control with a Franka Panda robot, and integration with the existing device factory pattern.

Key additions:

  • HaplyDevice class with position/orientation tracking, button inputs, and bidirectional force feedback
  • Real-time WebSocket communication thread with connection monitoring and automatic reconnection
  • Demo script showcasing end-effector control with contact force feedback from finger sensors
  • Comprehensive test coverage with mocked WebSocket connections
  • Force limiting (2N default) for safety

Issues requiring attention:

  • Multiple threading synchronization issues exist in se3_haply.py (see previous comments about race conditions on lines 209, 331)
  • Invalid quaternion multiplication by orientation_sensitivity on line 213 (quaternions must remain normalized)
  • Connection logic changed from OR to AND (line 275) - breaks backward compatibility if only one device is available
  • IK joint indexing in demo needs clarification - computes 6 joints but handles 7-joint arm
  • orientation_sensitivity parameter is stored but never actually used

Confidence Score: 2/5

  • This PR has critical threading race conditions and logic errors that could cause runtime failures or undefined behavior in production teleoperation.
  • Score reflects multiple critical issues: race conditions in thread synchronization (lines 209, 331 of se3_haply.py) where data is accessed outside locks, invalid quaternion operations that break rotation math, and connection logic that may fail unexpectedly. While the feature implementation is comprehensive with good test coverage, the threading bugs and mathematical errors need resolution before safe deployment in a real-time teleoperation context.
  • Pay close attention to source/isaaclab/isaaclab/devices/haply/se3_haply.py for race condition fixes and scripts/demos/haply_teleoperation.py for joint indexing clarification

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 New Haply device driver with WebSocket communication. Has multiple critical race conditions in thread synchronization, invalid quaternion multiplication, and connection logic issues that need resolution.
scripts/demos/haply_teleoperation.py 3/5 Teleoperation demo with IK control. Has joint indexing mismatch where IK computes 6 joints but joint 7 handling is unclear, and unused orientation computation.
source/isaaclab/test/devices/test_device_constructors.py 4/5 Comprehensive test coverage for Haply device including mock WebSocket connections, force feedback, and configuration validation.

Sequence Diagram

sequenceDiagram
    participant User
    participant Demo as haply_teleoperation.py
    participant HaplyDev as HaplyDevice
    participant WSThread as WebSocket Thread
    participant HaplySDK as Haply SDK
    participant IKCtrl as IK Controller
    participant Robot as Franka Robot
    
    User->>Demo: Start teleoperation demo
    Demo->>HaplyDev: HaplyDevice(cfg)
    HaplyDev->>WSThread: Start WebSocket thread
    WSThread->>HaplySDK: Connect via WebSocket
    HaplySDK-->>WSThread: Connection established
    
    loop Every 200Hz (data_rate)
        WSThread->>HaplySDK: Request device data
        HaplySDK-->>WSThread: Position, orientation, buttons
        WSThread->>HaplyDev: Update cached_data (with data_lock)
        WSThread->>HaplySDK: Send force feedback
    end
    
    loop Simulation Loop
        Demo->>HaplyDev: advance()
        HaplyDev->>HaplyDev: Read cached_data (with data_lock)
        HaplyDev-->>Demo: [pos, quat, buttons] tensor
        Demo->>Demo: apply_haply_to_robot_mapping()
        Demo->>IKCtrl: compute(target_pos, jacobian)
        IKCtrl-->>Demo: joint_pos_des[6 joints]
        Demo->>Demo: Set joint 7 (rotation angle)
        Demo->>Robot: set_joint_position_target()
        Demo->>Robot: Get contact forces from sensors
        Demo->>HaplyDev: set_force_feedback(fx, fy, fz)
        HaplyDev->>HaplyDev: Update feedback_force (with force_lock)
    end
Loading

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

# Store configuration
self.websocket_uri = cfg.websocket_uri
self.pos_sensitivity = cfg.pos_sensitivity
self.orientation_sensitivity = cfg.orientation_sensitivity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: orientation_sensitivity is stored but never used in the advance() method (line 213-214). Either remove it or apply it to quaternion data if scaling is needed.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds support for Haply haptic devices (Inverse3 + VerseGrip) to IsaacLab for teleoperation with bidirectional force feedback. The implementation includes a WebSocket-based device API (HaplyDevice), a Franka Panda teleoperation demo with IK control, and comprehensive unit tests.

Key Changes:

  • New HaplyDevice class in source/isaaclab/isaaclab/devices/haply/se3_haply.py providing SE(3) control with real-time position tracking, orientation sensing, button inputs, and force feedback at 200Hz
  • Demo script scripts/demos/haply_teleoperation.py showcasing robot manipulation with workspace mapping, IK-based motion control, and contact-based force feedback
  • Unit tests in test_device_constructors.py validating device initialization, configuration, and force limiting
  • Added websockets dependency to extension requirements

Critical Issues Found:

  • Race condition in se3_haply.py:209-214: Data copied outside lock protection after release
  • Invalid quaternion scaling on line 214 breaks normalization constraint
  • Missing try-except for websockets import (hardcoded WEBSOCKETS_AVAILABLE = True)
  • Connection logic changed from OR to AND (line 275), breaking backward compatibility
  • Button callbacks invoked inside lock (line 225), potential for deadlock
  • IK joint indexing mismatch in demo (6 joints from IK applied incorrectly to 7-joint array)

Recommendations:
Address the race condition and quaternion scaling issues before merge as these will cause runtime failures and undefined rotation behavior.

Confidence Score: 2/5

  • This PR has critical threading issues and logic errors that will cause runtime failures and undefined behavior
  • Score of 2 reflects multiple critical issues: (1) race condition accessing cached_data arrays outside lock protection will cause data corruption in high-frequency loops, (2) invalid quaternion multiplication breaks rotation math entirely, (3) missing import error handling will fail on systems without websockets, (4) IK joint indexing mismatch causes incorrect motion, (5) callback invocation inside locks risks deadlock. These are not potential issues but definite bugs that will manifest in production.
  • source/isaaclab/isaaclab/devices/haply/se3_haply.py requires immediate attention for race conditions (lines 209-214), quaternion handling (line 214), import handling (line 20), and connection logic (line 275). scripts/demos/haply_teleoperation.py needs IK indexing fix (line 319).

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 Added Haply device API with WebSocket communication and force feedback. Contains critical race conditions (data access outside locks), invalid quaternion scaling, missing import error handling, and connection logic issues that need resolution.
scripts/demos/haply_teleoperation.py 3/5 Teleoperation demo with IK control and force feedback. Has joint indexing mismatch (6-joint IK applied to 7-joint array), force computation happens unconditionally, and minor code style issues.
source/isaaclab/test/devices/test_device_constructors.py 4/5 Added unit tests for Haply device constructors with proper mocking. Tests cover config validation, force limiting, and reset functionality. Well-structured test implementation.

Sequence Diagram

sequenceDiagram
    participant Demo as haply_teleoperation.py
    participant Haply as HaplyDevice
    participant WS as WebSocket Thread
    participant SDK as Haply SDK
    participant Robot as Franka Robot
    participant Sensor as Contact Sensors

    Note over Demo,SDK: Initialization Phase
    Demo->>Haply: Create HaplyDevice(cfg)
    Haply->>WS: Start WebSocket thread
    WS->>SDK: Connect ws://localhost:10001
    SDK-->>WS: Connection established
    WS->>SDK: Receive device data
    SDK-->>WS: {inverse3, wireless_verse_grip}
    WS->>Haply: Cache position, orientation, buttons
    Haply-->>Demo: Device ready

    Note over Demo,Sensor: Main Teleoperation Loop (200Hz)
    loop Every simulation step
        Demo->>Haply: advance()
        Haply->>Haply: Lock data_lock
        Haply->>Haply: Copy position * pos_sensitivity
        Haply->>Haply: Copy quaternion
        Haply->>Haply: Copy buttons (a, b, c)
        Haply->>Haply: Release data_lock
        Haply-->>Demo: [pos, quat, buttons] tensor
        
        Demo->>Demo: Map Haply to robot workspace
        Demo->>Demo: Compute IK (6 joints)
        Demo->>Demo: Apply joint targets (7 joints + gripper)
        Demo->>Robot: set_joint_position_target()
        Robot->>Robot: Execute motion (5 steps)
        
        Demo->>Sensor: Read left_finger_contact_sensor
        Demo->>Sensor: Read right_finger_contact_sensor
        Sensor-->>Demo: net_forces_w
        Demo->>Demo: Average contact forces
        Demo->>Haply: push_force(total_contact_force)
        Haply->>Haply: Lock force_lock
        Haply->>Haply: Clip forces to [-2.0, 2.0]N
        Haply->>Haply: Update feedback_force
        Haply->>Haply: Release force_lock
    end

    Note over WS,SDK: Background Force Feedback (200Hz)
    loop Async WebSocket loop
        WS->>SDK: Receive state update
        SDK-->>WS: {position, orientation, buttons}
        WS->>Haply: Lock data_lock and update cached_data
        WS->>Haply: Lock force_lock and read feedback_force
        WS->>SDK: Send set_cursor_force command
        SDK->>SDK: Apply force to Inverse3 device
        WS->>WS: Sleep 1/data_rate (5ms)
    end
Loading

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds comprehensive Haply haptic device support to Isaac Lab, enabling real-time teleoperation with bidirectional force feedback. The implementation introduces a new HaplyDevice class that communicates with Haply hardware (Inverse3 + VerseGrip) via WebSocket, along with a complete demo showcasing robotic manipulation with IK control.

Key changes:

  • Added push_force() and push_wrench() interfaces to DeviceBase for force feedback support
  • Implemented HaplyDevice with WebSocket-based communication, position/orientation tracking, and force feedback
  • Created teleoperation demo with Franka Panda robot, IK control, and contact-based haptic feedback
  • Added comprehensive unit tests with proper mocking of async operations

Critical issues identified:

  • Race condition in se3_haply.py:207-214 where data is copied inside lock but accessed outside (previous comment addresses this)
  • Connection status logic changed from OR to AND on line 275, requiring both devices (previous comment questions backward compatibility)
  • Multiple threading and cleanup issues flagged in previous comments

Architecture:
The system uses a producer-consumer pattern with a background WebSocket thread updating cached device data at 200Hz, while the main simulation loop reads this data through thread-safe locks and sends force feedback back to the hardware.

Confidence Score: 3/5

  • This PR introduces valuable haptic teleoperation functionality but contains critical race conditions and threading issues that should be resolved before merging
  • Score of 3 reflects the presence of several critical issues flagged in previous comments: race condition in advance() data access (line 207-214), connection status logic changes potentially breaking backward compatibility (line 275), and multiple threading/cleanup concerns. While the feature is well-tested and architecturally sound, these concurrency issues could cause intermittent failures in production use. The device_base.py interface additions are clean (5/5), but the core implementation needs fixes.
  • source/isaaclab/isaaclab/devices/haply/se3_haply.py requires immediate attention for race conditions and threading issues. scripts/demos/haply_teleoperation.py has minor issues with joint assignment logic that should be verified.

Important Files Changed

File Analysis

Filename Score Overview
source/isaaclab/isaaclab/devices/device_base.py 5/5 Added push_force and push_wrench methods to DeviceBase interface for force feedback support - clean, well-documented API additions with sensible no-op defaults
source/isaaclab/isaaclab/devices/haply/se3_haply.py 2/5 New Haply device implementation with WebSocket communication and force feedback - contains critical race conditions in advance() and connection status checks, plus several style/cleanup issues flagged in previous comments
scripts/demos/haply_teleoperation.py 3/5 New teleoperation demo with IK control and force feedback - contains logic error in joint assignment (line 313) and some unused variables, but overall functional implementation
source/isaaclab/test/devices/test_device_constructors.py 4/5 Added comprehensive unit tests for HaplyDevice constructor with proper mocking of WebSocket and threading - thorough test coverage including force limiting and reset functionality

Sequence Diagram

sequenceDiagram
    participant User as User/Haply Device
    participant HaplySDK as Haply SDK (WebSocket)
    participant HaplyDevice as HaplyDevice
    participant Demo as haply_teleoperation.py
    participant IK as IK Controller
    participant Robot as Franka Robot
    participant Sensor as Contact Sensors

    Note over User,HaplySDK: Initialization Phase
    User->>HaplySDK: Connect Inverse3 + VerseGrip
    Demo->>HaplyDevice: __init__(cfg)
    HaplyDevice->>HaplySDK: WebSocket connect (ws://localhost:10001)
    HaplySDK-->>HaplyDevice: Connection established
    HaplyDevice->>HaplyDevice: Start WebSocket thread
    HaplySDK-->>HaplyDevice: Device IDs (Inverse3, VerseGrip)
    HaplyDevice-->>Demo: Device ready

    Note over User,Robot: Teleoperation Loop (200Hz)
    loop Every simulation step
        User->>HaplySDK: Move device, press buttons
        HaplySDK->>HaplyDevice: WebSocket data: {position, orientation, buttons}
        HaplyDevice->>HaplyDevice: Cache data (with data_lock)
        
        Demo->>HaplyDevice: advance()
        HaplyDevice->>HaplyDevice: Read cached_data (with data_lock)
        HaplyDevice-->>Demo: [x,y,z, qx,qy,qz,qw, btn_a,btn_b,btn_c]
        
        Demo->>Demo: apply_haply_to_robot_mapping()
        Demo->>Demo: Handle button events (gripper, rotation)
        Demo->>IK: compute(target_pos, current_state, jacobian)
        IK-->>Demo: joint_pos_des (6 joints)
        Demo->>Demo: Assemble joint targets (IK + rotation + gripper)
        Demo->>Robot: set_joint_position_target()
        
        Robot->>Robot: Execute motion
        Robot->>Sensor: Contact forces
        Sensor-->>Demo: net_forces_w (left + right finger)
        Demo->>Demo: Calculate total_contact_force
        Demo->>HaplyDevice: push_force(total_contact_force)
        HaplyDevice->>HaplyDevice: Update feedback_force (with force_lock)
        HaplyDevice->>HaplySDK: WebSocket send: set_cursor_force
        HaplySDK->>User: Apply haptic feedback
    end
Loading

4 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team isaac-mimic Related to Isaac Mimic team

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants