BookinglyTech News
Infraestructura

Control remoto de robots con controladores VR: paso a paso

Los controladores de realidad virtual, como los de Meta Quest o HTC Vive, se están convirtiendo en la entrada dominante para la teleoperación de robots, gracias a su rastreo 6-DOF y su interfaz intuitiva.

3 min de lecturaDev.to0 vistas

¿Por qué usar controladores VR?

Los dispositivos de Meta Quest, HTC Vive y Valve Index ofrecen rastreo 6‑DOF con precisión sub‑milímetro a centímetro, botones de gatillo y agarre que se mapean de forma natural al pinza del robot y retroalimentación visual estéreo cuando se combinan con una cabeza de realidad virtual.

Integración básica

Lectura del pose y entrada

La mayoría de los SDK (OpenXR, SteamVR, Oculus/Meta) exponen la pose del controlador como posición + cuaternión y el estado de botones. Un wrapper genérico en Python:

class VRController:
    def __init__(self, sdk_client, hand="right"):
        self.sdk_client = sdk_client
        self.hand = hand
    def read(self) -> dict:
        pose = self.sdk_client.get_controller_pose(self.hand)
        return {
            "position": pose.position,
            "orientation": pose.quaternion,
            "trigger": self.sdk_client.get_trigger_value(self.hand),
            "grip": self.sdk_client.get_grip_value(self.hand),
            "buttons": self.sdk_client.get_buttons(self.hand),
        }

Alineación de marcos

El espacio de la cabeza, el marco local del controlador y el marco base del robot son independientes. Se necesita una transformación de calibración. Una técnica sencilla es alinear la posición de referencia al iniciar la sesión:

import numpy as np
from scipy.spatial.transform import Rotation as R

def compute_calibration(vr_pose_at_ref, robot_pose_at_ref):
    vr_pos, vr_quat = vr_pose_at_ref
    robot_pos, robot_quat = robot_pose_at_ref
    R_vr = R.from_quat(vr_quat)
    R_robot = R.from_quat(robot_quat)
    R_offset = R_robot * R_vr.inv()
    t_offset = np.array(robot_pos) - R_offset.apply(vr_pos)
    return R_offset, t_offset

Teleoperación con delta‑pose

Para evitar que la distancia entre la mano humana y el alcance del robot genere movimientos bruscos, se emplea un mecanismo de clutch: se activa al pulsar la mano de agarre y solo se aplica el cambio de pose desde ese momento.

class VRDeltaTeleop:
    def __init__(self, controller, robot, position_scale=1.0):
        self.controller = controller
        self.robot = robot
        self.position_scale = position_scale
        self.engaged = False
        self.ref_vr_pos = None
        self.ref_robot_pose = None
    def step(self):
        state = self.controller.read()
        engage = state["grip"] > 0.5
        if engage and not self.engaged:
            self.ref_vr_pos = np.array(state["position"])
            self.ref_robot_pose = self.robot.get_state()["ee_pose"]
            self.engaged = True
        elif not engage:
            self.engaged = False
            return None
        delta = (np.array(state["position"]) - self.ref_vr_pos) * self.position_scale
        target_pose = self.ref_robot_pose.copy()
        target_pose[:3] += delta
        gripper_cmd = state["trigger"]
        return target_pose, gripper_cmd

Feedback haptico y visual

Si el SDK expone haptics, se pueden disparar pulsos cuando el robot toca un objeto o alcanza un límite de fuerza:

def send_contact_feedback(sdk_client, hand, force_magnitude):
    intensity = min(force_magnitude / 20.0, 1.0)
    sdk_client.trigger_haptic_pulse(hand, duration_ms=50, amplitude=intensity)

El vídeo de la cámara del brazo del robot se puede pasar al visor para mejorar la percepción de profundidad.

Integración con pipeline de recolección de datos

Una vez que se tiene un objeto VRDeltaTeleop, se lo inserta en el bucle de teleoperación que ya estaba en uso:

def vr_teleop_loop(vr_teleop, robot, camera, logger, hz=20):
    dt = 1.0 / hz
    while True:
        start = time.time()
        result = vr_teleop.step()
        if result is not None:
            target_pose, gripper_cmd = result
            robot.send_command(target_pose, gripper_cmd)
            obs = {
                "image": camera.get_frame(),
                "ee_pose": robot.get_state()["ee_pose"],
            }
            logger.log(obs)

Qué sigue

El uso de controladores VR simplifica la teleoperación, pero exige una calibración cuidadosa y una gestión de la latencia del SDK. Los desarrolladores que ya utilizan ROS o frameworks de robotics pueden integrar estos snippets con poco esfuerzo. La comunidad está empezando a publicar repositorios de SDK específicos para Flutter y Android: Repositorio en GitHub y Repositorio en GitHub.