@ -118,6 +118,8 @@ if __name__ == '__main__':
parser . add_argument ( ' --ki ' , type = float , default = 0.3 , help = ' Integral gain for drift correction (0.0 = disabled) ' )
parser . add_argument ( ' --ki ' , type = float , default = 0.3 , help = ' Integral gain for drift correction (0.0 = disabled) ' )
parser . add_argument ( ' --i-clamp ' , type = float , default = 0.10 , help = ' Anti-windup clamp for I term in meters (default: 0.10) ' )
parser . add_argument ( ' --i-clamp ' , type = float , default = 0.10 , help = ' Anti-windup clamp for I term in meters (default: 0.10) ' )
parser . add_argument ( ' --i-decay ' , type = float , default = 0.995 , help = ' Per-frame decay factor for I term (default: 0.995) ' )
parser . add_argument ( ' --i-decay ' , type = float , default = 0.995 , help = ' Per-frame decay factor for I term (default: 0.995) ' )
parser . add_argument ( ' --no-calibration ' , action = ' store_true ' ,
help = ' Disable start-time calibration (use absolute VP poses) ' )
# record mode and task info
# record mode and task info
parser . add_argument ( ' --record ' , action = ' store_true ' , help = ' Enable data recording mode ' )
parser . add_argument ( ' --record ' , action = ' store_true ' , help = ' Enable data recording mode ' )
parser . add_argument ( ' --task-dir ' , type = str , default = ' ./utils/data/ ' , help = ' path to save data ' )
parser . add_argument ( ' --task-dir ' , type = str , default = ' ./utils/data/ ' , help = ' path to save data ' )
@ -307,6 +309,26 @@ if __name__ == '__main__':
last_loop_time = time . time ( )
last_loop_time = time . time ( )
i_frame_count = 0
i_frame_count = 0
# Relative-mode calibration state
calibrated = False
vp_ref_left = None # 4x4: VP wrist pose at calibration moment
vp_ref_right = None
robot_ref_left = None # 4x4: Robot FK pose at calibration moment
robot_ref_right = None
prev_start = False
# Initial calibration: capture reference poses on first START
if not args . no_calibration :
_cal_tele = tv_wrapper . get_tele_data ( )
_cal_arm_q = arm_ctrl . get_current_dual_arm_q ( )
robot_ref_left , robot_ref_right = arm_ik . compute_fk_pose ( _cal_arm_q )
vp_ref_left = _cal_tele . left_wrist_pose . copy ( )
vp_ref_right = _cal_tele . right_wrist_pose . copy ( )
calibrated = True
prev_start = True
logger_mp . info ( f " [calibration] Initial reference captured. "
f " Robot L={robot_ref_left[:3,3]}, R={robot_ref_right[:3,3]} " )
# main loop. robot start to follow VR user's motion
# main loop. robot start to follow VR user's motion
while not STOP :
while not STOP :
start_time = time . time ( )
start_time = time . time ( )
@ -323,7 +345,10 @@ if __name__ == '__main__':
if np . all ( np . abs ( arm_ctrl . get_current_dual_arm_q ( ) ) < 0.1 ) :
if np . all ( np . abs ( arm_ctrl . get_current_dual_arm_q ( ) ) < 0.1 ) :
break
break
time . sleep ( 0.05 )
time . sleep ( 0.05 )
logger_mp . info ( " [main] Arms reset complete, resuming tracking " )
logger_mp . info ( " [main] Arms reset complete. Tracking PAUSED — press A/r to recalibrate and resume. " )
START = False
prev_start = False
calibrated = False
arm_ctrl . speed_gradual_max ( )
arm_ctrl . speed_gradual_max ( )
last_loop_time = time . time ( )
last_loop_time = time . time ( )
continue
continue
@ -349,6 +374,20 @@ if __name__ == '__main__':
logger_mp . info ( " [R3] Start pressed → Toggle recording " )
logger_mp . info ( " [R3] Start pressed → Toggle recording " )
_r3_prev_buttons = r3_btns
_r3_prev_buttons = r3_btns
# Detect START resume transition → re-calibrate
if START and not prev_start and not args . no_calibration :
_cal_tele = tv_wrapper . get_tele_data ( )
_cal_arm_q = arm_ctrl . get_current_dual_arm_q ( )
robot_ref_left , robot_ref_right = arm_ik . compute_fk_pose ( _cal_arm_q )
vp_ref_left = _cal_tele . left_wrist_pose . copy ( )
vp_ref_right = _cal_tele . right_wrist_pose . copy ( )
calibrated = True
i_error_left = np . zeros ( 3 )
i_error_right = np . zeros ( 3 )
logger_mp . info ( f " [calibration] Re-calibrated on resume. "
f " Robot L={robot_ref_left[:3,3]}, R={robot_ref_right[:3,3]} " )
prev_start = START
# Skip arm tracking while paused (buttons + reset still work)
# Skip arm tracking while paused (buttons + reset still work)
if not START :
if not START :
i_error_left = np . zeros ( 3 )
i_error_left = np . zeros ( 3 )
@ -429,6 +468,23 @@ if __name__ == '__main__':
dt = min ( now - last_loop_time , 0.1 ) # clamp to prevent huge jumps after stalls
dt = min ( now - last_loop_time , 0.1 ) # clamp to prevent huge jumps after stalls
last_loop_time = now
last_loop_time = now
# --- Apply relative-mode calibration ---
if calibrated :
# Position: robot_ref + (vp_current - vp_ref)
left_wrist_adjusted = np . eye ( 4 )
right_wrist_adjusted = np . eye ( 4 )
left_wrist_adjusted [ : 3 , 3 ] = robot_ref_left [ : 3 , 3 ] + (
tele_data . left_wrist_pose [ : 3 , 3 ] - vp_ref_left [ : 3 , 3 ] )
right_wrist_adjusted [ : 3 , 3 ] = robot_ref_right [ : 3 , 3 ] + (
tele_data . right_wrist_pose [ : 3 , 3 ] - vp_ref_right [ : 3 , 3 ] )
# Rotation: delta_R @ robot_ref_R where delta_R = vp_R @ vp_ref_R^T
left_wrist_adjusted [ : 3 , : 3 ] = (
tele_data . left_wrist_pose [ : 3 , : 3 ] @ vp_ref_left [ : 3 , : 3 ] . T
) @ robot_ref_left [ : 3 , : 3 ]
right_wrist_adjusted [ : 3 , : 3 ] = (
tele_data . right_wrist_pose [ : 3 , : 3 ] @ vp_ref_right [ : 3 , : 3 ] . T
) @ robot_ref_right [ : 3 , : 3 ]
else :
left_wrist_adjusted = tele_data . left_wrist_pose . copy ( )
left_wrist_adjusted = tele_data . left_wrist_pose . copy ( )
right_wrist_adjusted = tele_data . right_wrist_pose . copy ( )
right_wrist_adjusted = tele_data . right_wrist_pose . copy ( )
@ -436,9 +492,9 @@ if __name__ == '__main__':
# FK: where are the robot hands actually?
# FK: where are the robot hands actually?
left_ee_pos , right_ee_pos = arm_ik . compute_fk ( current_lr_arm_q )
left_ee_pos , right_ee_pos = arm_ik . compute_fk ( current_lr_arm_q )
# Error: target - actual (task-space position)
left_error = tele_data . left_wrist_pose [ : 3 , 3 ] - left_ee_pos
right_error = tele_data . right_wrist_pose [ : 3 , 3 ] - right_ee_pos
# Error: calibrated target - actual (task-space position)
left_error = left_wrist_adjusted [ : 3 , 3 ] - left_ee_pos
right_error = right_wrist_adjusted [ : 3 , 3 ] - right_ee_pos
# Leaky integrator with anti-windup
# Leaky integrator with anti-windup
i_error_left = i_error_left * args . i_decay + left_error * dt
i_error_left = i_error_left * args . i_decay + left_error * dt