Browse Source
Document code deployment workflow, update launch/pull scripts
Document code deployment workflow, update launch/pull scripts
- CLAUDE.md: Add CODE DEPLOYMENT WORKFLOW section — git push to Gitea then pull on robot, never SFTP. Document repo layout and two xr_teleoperate locations on robot. - pull_on_robot.py: Updated verification markers (webcam, head_R_at_cal, hasattr guards) - start_teleop_webcam_full.py: Use standalone ~/xr_teleoperate path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>master
3 changed files with 148 additions and 14 deletions
@ -0,0 +1,111 @@ |
|||||
|
"""Launch full teleop with webcam (no image server, with hand drivers). |
||||
|
|
||||
|
Replicates start_teleop.sh but skips the image server that kills the UVC driver. |
||||
|
""" |
||||
|
import paramiko |
||||
|
import sys |
||||
|
import time |
||||
|
|
||||
|
sys.stdout.reconfigure(encoding='utf-8', errors='replace') |
||||
|
|
||||
|
ssh = paramiko.SSHClient() |
||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
||||
|
ssh.connect('10.0.0.64', username='unitree', password='123', |
||||
|
timeout=15, look_for_keys=False, allow_agent=False) |
||||
|
|
||||
|
# ── Step 0: Kill any existing teleop / hand driver processes ── |
||||
|
print("Cleaning up old processes...") |
||||
|
_, o, _ = ssh.exec_command( |
||||
|
'ps aux | grep -E "teleop_hand_and_arm|ModbusDataHandler|image_server" | grep -v grep', |
||||
|
timeout=5) |
||||
|
procs = o.read().decode().strip() |
||||
|
if procs: |
||||
|
print(f" Found:\n{procs}") |
||||
|
for line in procs.split('\n'): |
||||
|
parts = line.split() |
||||
|
if len(parts) > 1: |
||||
|
ssh.exec_command(f'kill {parts[1]}', timeout=5) |
||||
|
time.sleep(1) |
||||
|
print(" Killed stale processes") |
||||
|
else: |
||||
|
print(" No stale processes") |
||||
|
|
||||
|
# ── Step 1: Start Inspire hand Modbus-to-DDS drivers ── |
||||
|
print("\nStarting Inspire hand drivers...") |
||||
|
|
||||
|
TELEOP_DIR = "/home/unitree/xr_teleoperate/teleop" |
||||
|
CYCLONE_HOME = "/home/unitree/g1-control/repos/cyclonedds/install/cyclonedds" |
||||
|
CYCLONE_URI = "file:///home/unitree/g1-control/repos/cyclonedds/cyclonedds.xml" |
||||
|
PYTHON = "/home/unitree/miniforge3/envs/tv/bin/python" |
||||
|
|
||||
|
env_prefix = ( |
||||
|
f'export CYCLONEDDS_HOME="{CYCLONE_HOME}" && ' |
||||
|
f'export CYCLONEDDS_URI="{CYCLONE_URI}" && ' |
||||
|
) |
||||
|
|
||||
|
# Right hand (also initializes DDS) |
||||
|
right_hand_cmd = ( |
||||
|
f'{env_prefix} ' |
||||
|
f'cd {TELEOP_DIR} && ' |
||||
|
f'nohup {PYTHON} -m inspire_hand.ModbusDataHandler ' |
||||
|
f'--ip 192.168.123.211 --initDDS True > /tmp/right_hand.log 2>&1 &' |
||||
|
) |
||||
|
ssh.exec_command(right_hand_cmd, timeout=10) |
||||
|
print(" Right hand driver started (192.168.123.211)") |
||||
|
time.sleep(2) |
||||
|
|
||||
|
# Left hand (check reachability first) |
||||
|
_, o, _ = ssh.exec_command('ping -c 1 -W 1 192.168.123.210 2>&1 | grep "1 received"', timeout=5) |
||||
|
left_reachable = bool(o.read().decode().strip()) |
||||
|
if left_reachable: |
||||
|
left_hand_cmd = ( |
||||
|
f'{env_prefix} ' |
||||
|
f'cd {TELEOP_DIR} && ' |
||||
|
f'nohup {PYTHON} -m inspire_hand.ModbusDataHandler ' |
||||
|
f'--ip 192.168.123.210 > /tmp/left_hand.log 2>&1 &' |
||||
|
) |
||||
|
ssh.exec_command(left_hand_cmd, timeout=10) |
||||
|
print(" Left hand driver started (192.168.123.210)") |
||||
|
else: |
||||
|
print(" Left hand not reachable (skipped)") |
||||
|
|
||||
|
time.sleep(2) |
||||
|
|
||||
|
# ── Step 2: Launch teleop with webcam ── |
||||
|
print("\nStarting teleop with webcam...") |
||||
|
print("=" * 60) |
||||
|
|
||||
|
teleop_cmd = ( |
||||
|
f'{env_prefix} ' |
||||
|
f'cd {TELEOP_DIR} && ' |
||||
|
f'{PYTHON} teleop_hand_and_arm.py ' |
||||
|
f'--arm=G1_29 --ee=inspire_ftp --webcam 0 --webcam-res 720p' |
||||
|
) |
||||
|
|
||||
|
stdin, stdout, stderr = ssh.exec_command(teleop_cmd, timeout=300, get_pty=True) |
||||
|
|
||||
|
# Stream output in real-time |
||||
|
start_time = time.time() |
||||
|
while time.time() - start_time < 120: |
||||
|
if stdout.channel.recv_ready(): |
||||
|
data = stdout.channel.recv(4096).decode('utf-8', errors='replace') |
||||
|
print(data, end='') |
||||
|
if stderr.channel.recv_stderr_ready(): |
||||
|
data = stderr.channel.recv_stderr(4096).decode('utf-8', errors='replace') |
||||
|
print(f"[err] {data}", end='') |
||||
|
if stdout.channel.exit_status_ready(): |
||||
|
remaining = stdout.channel.recv(65536).decode('utf-8', errors='replace') |
||||
|
if remaining: |
||||
|
print(remaining, end='') |
||||
|
remaining_err = stderr.channel.recv_stderr(65536).decode('utf-8', errors='replace') |
||||
|
if remaining_err: |
||||
|
print(f"[err] {remaining_err}", end='') |
||||
|
exit_code = stdout.channel.recv_exit_status() |
||||
|
print(f"\n\nProcess exited with code {exit_code}") |
||||
|
break |
||||
|
time.sleep(0.1) |
||||
|
else: |
||||
|
print("\n\nTeleop is running! (2 min output window elapsed)") |
||||
|
print("Connect Quest 3 to: https://10.0.0.64:8012") |
||||
|
|
||||
|
ssh.close() |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue