From d845cfe2cc2f5183b4dfaadfebf53c5aa735b97a Mon Sep 17 00:00:00 2001 From: melancholytron Date: Sun, 1 Mar 2026 13:41:50 -0600 Subject: [PATCH] Add recalibrate via gaze ball (yellow, second from left) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking at the yellow gaze ball for 5 seconds sends a recalibrate command to the bridge, which resets calibration state — arms return to zero and after 10s delay the user's neutral pose is recaptured. Co-Authored-By: Claude Opus 4.6 --- Main.gd | 4 ++++ scripts/teleop_client.gd | 6 ++++++ server/retarget_bridge.py | 12 ++++++++++++ server/teleop_server.py | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/Main.gd b/Main.gd index bbf8591..f3e0cc7 100644 --- a/Main.gd +++ b/Main.gd @@ -464,6 +464,10 @@ func _on_gaze_activated(index: int) -> void: 0: # Exit AR print("[Main] Gaze exit: returning to CONFIG") _exit_ar_mode() + 1: # Recalibrate + print("[Main] Gaze: recalibrating") + teleop_client.send_command("recalibrate") + _gaze_timers[index] = 0.0 3: # Quit app print("[Main] Gaze exit: quitting app") get_tree().quit() diff --git a/scripts/teleop_client.gd b/scripts/teleop_client.gd index 8b0fc47..bb5660d 100644 --- a/scripts/teleop_client.gd +++ b/scripts/teleop_client.gd @@ -152,6 +152,12 @@ func _on_tracking_data(data: Dictionary) -> void: # Don't buffer if not connected — tracking data is perishable +## Send a command message to the server (e.g. "recalibrate"). +func send_command(command: String) -> void: + if is_connected: + _send_json({"type": command}) + + func _send_json(data: Dictionary) -> void: var json_str := JSON.stringify(data) var err := ws.send_text(json_str) diff --git a/server/retarget_bridge.py b/server/retarget_bridge.py index c010fd3..6c2b288 100644 --- a/server/retarget_bridge.py +++ b/server/retarget_bridge.py @@ -813,6 +813,18 @@ def main(): while running: t_start = time.perf_counter() + # Check for recalibrate request from client + with tv_wrapper.server.recalibrate_requested.get_lock(): + if tv_wrapper.server.recalibrate_requested.value: + tv_wrapper.server.recalibrate_requested.value = 0 + cal.calibrated = False + first_data_time = time.time() # restart cal_delay countdown + startup_ramp = 0.0 + startup_time = None + smoothed_upper[:] = 0.0 + current_hands[:] = 1.0 + logger.info("Recalibrating — arms to zero, waiting %.1fs", args.cal_delay) + # Read tracking data (for hand positions) tele_data = tv_wrapper.get_tele_data() data_time = tv_wrapper.last_data_time diff --git a/server/teleop_server.py b/server/teleop_server.py index 4e2b315..54c8e89 100644 --- a/server/teleop_server.py +++ b/server/teleop_server.py @@ -118,6 +118,9 @@ class TeleopServer: self._message_count = 0 self._last_log_time = 0 + # Command flags (set by client, read by bridge) + self.recalibrate_requested = Value('i', 0) + def set_webcam_frame(self, jpeg_bytes: bytes, camera_id: int = 0): """Called by external code to provide a new JPEG frame for a camera. @@ -172,6 +175,13 @@ class TeleopServer: return msg_type = data.get("type") + + if msg_type == "recalibrate": + with self.recalibrate_requested.get_lock(): + self.recalibrate_requested.value = 1 + logger.info("Recalibrate requested by client") + return + if msg_type != "tracking": return