You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
5.5 KiB
150 lines
5.5 KiB
import concurrent.futures
|
|
import time
|
|
|
|
import numpy as np
|
|
from scipy.spatial.transform import Rotation as R
|
|
|
|
from decoupled_wbc.control.teleop.device.iphone.iphone import IPhoneDevice
|
|
from decoupled_wbc.control.teleop.streamers.base_streamer import BaseStreamer, StreamerOutput
|
|
|
|
|
|
def get_data_with_timeout(obj, timeout=0.02):
|
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
future = executor.submit(obj.data_collect.request_vive_data)
|
|
try:
|
|
combined_data = future.result(timeout=timeout)
|
|
return combined_data
|
|
except concurrent.futures.TimeoutError:
|
|
print("Data request timed out.")
|
|
return None
|
|
|
|
|
|
def get_transformation(vive_raw_data):
|
|
"""
|
|
Turn the raw data from the Vive tracker into a transformation matrix.
|
|
"""
|
|
position = np.array(
|
|
[
|
|
vive_raw_data["position"]["x"],
|
|
vive_raw_data["position"]["y"],
|
|
vive_raw_data["position"]["z"],
|
|
]
|
|
)
|
|
quat = np.array(
|
|
[
|
|
vive_raw_data["orientation"]["x"],
|
|
vive_raw_data["orientation"]["y"],
|
|
vive_raw_data["orientation"]["z"],
|
|
vive_raw_data["orientation"]["w"],
|
|
]
|
|
)
|
|
T = np.identity(4)
|
|
T[:3, :3] = R.from_quat(quat).as_matrix()
|
|
T[:3, 3] = position
|
|
return T
|
|
|
|
|
|
class IphoneStreamer(BaseStreamer):
|
|
def __init__(self):
|
|
self.left_device = IPhoneDevice(port=5557)
|
|
self.right_device = IPhoneDevice(port=5558)
|
|
self.left_prev_button_states = {"Reset": False, "Close": False}
|
|
self.right_prev_button_states = {"Reset": False, "Close": False}
|
|
|
|
def start_streaming(self):
|
|
self.left_device.start()
|
|
self.right_device.start()
|
|
|
|
def __del__(self):
|
|
self.stop_streaming()
|
|
|
|
def get(self):
|
|
"""Request combined data and return transformations as StreamerOutput."""
|
|
# Initialize data groups
|
|
ik_data = {} # For pose and joint data (ik_keys)
|
|
control_data = {} # For robot control commands (control_keys)
|
|
teleop_data = {} # For internal policy commands (teleop_keys)
|
|
|
|
try:
|
|
# Request combined data from the server and wait until we get data
|
|
left_combined_data = None
|
|
right_combined_data = None
|
|
while not left_combined_data:
|
|
left_combined_data = self.left_device.get_cmd()
|
|
if not left_combined_data:
|
|
print("Waiting for left iPhone data...")
|
|
while not right_combined_data:
|
|
right_combined_data = self.right_device.get_cmd()
|
|
if not right_combined_data:
|
|
print("Waiting for right iPhone data...")
|
|
|
|
# IK data - wrist poses and finger positions (ik_keys)
|
|
ik_data["left_wrist"] = np.array(left_combined_data.get("transformMatrix"))
|
|
ik_data["right_wrist"] = np.array(right_combined_data.get("transformMatrix"))
|
|
|
|
# left button states
|
|
current_left_reset = left_combined_data.get("buttonStates").get("Reset")
|
|
current_left_close = left_combined_data.get("buttonStates").get("Close")
|
|
|
|
# Trigger logic: only True when button transitions from False to True
|
|
left_reset = current_left_reset and not self.left_prev_button_states["Reset"]
|
|
|
|
# Store current button states for next iteration
|
|
self.left_prev_button_states["Reset"] = current_left_reset
|
|
self.left_prev_button_states["Close"] = current_left_close
|
|
|
|
# left fingers - IK data (ik_keys)
|
|
fingertips = np.zeros([25, 4, 4])
|
|
positions = fingertips[:, :3, 3]
|
|
if current_left_close:
|
|
positions[4, 0] = 0 # closed
|
|
else:
|
|
positions[4, 0] = 1 # open
|
|
ik_data["left_fingers"] = {"position": fingertips}
|
|
|
|
# right button states
|
|
current_right_reset = right_combined_data.get("buttonStates").get("Reset")
|
|
current_right_close = right_combined_data.get("buttonStates").get("Close")
|
|
|
|
# Trigger logic: only True when button transitions from False to True
|
|
right_reset = current_right_reset and not self.right_prev_button_states["Reset"]
|
|
|
|
# Store current button states for next iteration
|
|
self.right_prev_button_states["Reset"] = current_right_reset
|
|
self.right_prev_button_states["Close"] = current_right_close
|
|
|
|
# right fingers - IK data (ik_keys)
|
|
fingertips = np.zeros([25, 4, 4])
|
|
positions = fingertips[:, :3, 3]
|
|
if current_right_close:
|
|
positions[4, 0] = 0 # closed
|
|
else:
|
|
positions[4, 0] = 1 # open
|
|
ik_data["right_fingers"] = {"position": fingertips}
|
|
|
|
# Teleop commands (teleop_keys) - used by TeleopPolicy for activation
|
|
teleop_data["toggle_activation"] = left_reset
|
|
|
|
# Control commands (control_keys) - sent to robot
|
|
control_data["toggle_stand_command"] = right_reset
|
|
|
|
except Exception as e:
|
|
print(f"Error while requesting iPhone data: {e}")
|
|
|
|
# Return structured output
|
|
return StreamerOutput(
|
|
ik_data=ik_data, control_data=control_data, teleop_data=teleop_data, source="iphone"
|
|
)
|
|
|
|
def stop_streaming(self):
|
|
self.left_device.stop()
|
|
self.right_device.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
streamer = IphoneStreamer()
|
|
streamer.start_streaming()
|
|
while True:
|
|
data = streamer.get()
|
|
print(data)
|
|
time.sleep(0.1)
|