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.

¿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.

