XLeRobot icon indicating copy to clipboard operation
XLeRobot copied to clipboard

How to obtain the accurate output of the trigger?

Open meijie-jesse opened this issue 5 months ago • 3 comments

Hello, thank you for your wonderful work!

I want to know how to obtain accurate linear values for triggers in metadata, where the output is only 0 and 1? Just like the output of Thumbstick.

thank you!

meijie-jesse avatar Jul 30 '25 14:07 meijie-jesse

For XLeVR, the trigger button is detected by:

`async def process_single_controller(self, hand: str, data: Dict): """Process data for a single controller.""" position = data.get('position', {}) rotation = data.get('rotation', {}) quaternion = data.get('quaternion', {}) # Get quaternion data directly grip_active = data.get('gripActive', False) trigger = data.get('trigger', 0) thumbstick = data.get('thumbstick', {})

    controller = self.left_controller if hand == 'left' else self.right_controller
    
    # Handle trigger for gripper control
    trigger_active = trigger > 0.5
    if trigger_active != controller.trigger_active:
        controller.trigger_active = trigger_active
        
        # Send gripper control goal - do not specify mode to avoid interfering with position control
        # Reverse behavior: gripper open by default, closes when trigger pressed
        gripper_goal = ControlGoal(
            arm=hand,
            gripper_closed=not trigger_active,  # Inverted: closed when trigger NOT active
            metadata={
                "source": "vr_trigger",
                "trigger": trigger,
                "trigger_active": trigger_active,
                "thumbstick": thumbstick
            }
        )
        await self.send_goal(gripper_goal)
        
        logger.info(f"🤏 {hand.upper()} gripper {'OPENED' if trigger_active else 'CLOSED'}")`

You can modify the code to fit your own task.

For Xbox controller, the trigger button values are fetched directly:

`def get_xbox_key_state(joystick, keymap): """ Map XBOX controller state to semantic action booleans using the provided keymap. """ # Read axes, buttons, hats axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())] buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())] hats = joystick.get_hat(0) if joystick.get_numhats() > 0 else (0, 0)

# Get stick pressed states
left_stick_pressed = bool(buttons[9]) if len(buttons) > 9 else False
right_stick_pressed = bool(buttons[10]) if len(buttons) > 10 else False
lb_pressed = bool(buttons[4]) if len(buttons) > 4 else False
rb_pressed = bool(buttons[5]) if len(buttons) > 5 else False

# Map controller state to semantic actions
state = {}
for action, control in keymap.items():
    if control == 'left_trigger':
        state[action] = axes[2] > 0.5 if len(axes) > 2 else False
    elif control == 'right_trigger':
        state[action] = axes[5] > 0.5 if len(axes) > 5 else False`

Vector-Wangel avatar Jul 30 '25 20:07 Vector-Wangel

Thx for your reply! In fact I have noticed the code, where the gripper states get value from 'data' dic, but the data is obtained from the 'message' of the websocket, and the original value returned in the ’message‘ is either 0 or 1, so maybe i cant modify it easily?

For XLeVR, the trigger button is detected by:

`async def process_single_controller(self, hand: str, data: Dict): """Process data for a single controller.""" position = data.get('position', {}) rotation = data.get('rotation', {}) quaternion = data.get('quaternion', {}) # Get quaternion data directly grip_active = data.get('gripActive', False) trigger = data.get('trigger', 0) thumbstick = data.get('thumbstick', {})

    controller = self.left_controller if hand == 'left' else self.right_controller
    
    # Handle trigger for gripper control
    trigger_active = trigger > 0.5
    if trigger_active != controller.trigger_active:
        controller.trigger_active = trigger_active
        
        # Send gripper control goal - do not specify mode to avoid interfering with position control
        # Reverse behavior: gripper open by default, closes when trigger pressed
        gripper_goal = ControlGoal(
            arm=hand,
            gripper_closed=not trigger_active,  # Inverted: closed when trigger NOT active
            metadata={
                "source": "vr_trigger",
                "trigger": trigger,
                "trigger_active": trigger_active,
                "thumbstick": thumbstick
            }
        )
        await self.send_goal(gripper_goal)
        
        logger.info(f"🤏 {hand.upper()} gripper {'OPENED' if trigger_active else 'CLOSED'}")`

You can modify the code to fit your own task.

For Xbox controller, the trigger button values are fetched directly:

`def get_xbox_key_state(joystick, keymap): """ Map XBOX controller state to semantic action booleans using the provided keymap. """ # Read axes, buttons, hats axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())] buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())] hats = joystick.get_hat(0) if joystick.get_numhats() > 0 else (0, 0)

# Get stick pressed states
left_stick_pressed = bool(buttons[9]) if len(buttons) > 9 else False
right_stick_pressed = bool(buttons[10]) if len(buttons) > 10 else False
lb_pressed = bool(buttons[4]) if len(buttons) > 4 else False
rb_pressed = bool(buttons[5]) if len(buttons) > 5 else False

# Map controller state to semantic actions
state = {}
for action, control in keymap.items():
    if control == 'left_trigger':
        state[action] = axes[2] > 0.5 if len(axes) > 2 else False
    elif control == 'right_trigger':
        state[action] = axes[5] > 0.5 if len(axes) > 5 else False`

Thx for your reply!My device is XLeVR, quest 3. In fact I have noticed the code, where the gripper states get value from 'data' dic, but the data is obtained from the 'message' of the websocket, and I found the original value returned in the ’message‘ is either 0 or 1, so maybe i cant modify it easily?

meijie-jesse avatar Jul 31 '25 03:07 meijie-jesse