| |
| """ |
| Test Multi-Axis Homing with Nova API v2 |
| |
| This script demonstrates using the v2 API's ability to move all joints simultaneously |
| to achieve faster homing. Instead of moving one joint at a time, all joints move |
| toward their home position concurrently. |
| """ |
|
|
| import os |
| import sys |
| import time |
| import urllib.request |
| import json |
| from pathlib import Path |
|
|
| |
| sys.path.insert(0, str(Path(__file__).parent.parent)) |
|
|
| |
| def load_env_file(filepath): |
| """Simple .env file loader.""" |
| if not filepath.exists(): |
| return |
| with open(filepath, 'r') as f: |
| for line in f: |
| line = line.strip() |
| if line and not line.startswith('#') and '=' in line: |
| key, value = line.split('=', 1) |
| os.environ[key.strip()] = value.strip() |
|
|
| def get_robot_state(): |
| """Get current robot state from Nova API.""" |
| instance_url = os.getenv("NOVA_INSTANCE_URL") |
| access_token = os.getenv("NOVA_ACCESS_TOKEN") |
| cell_id = os.getenv("NOVA_CELL_ID", "cell") |
| controller_id = os.getenv("NOVA_CONTROLLER_ID") |
| motion_group_id = os.getenv("NOVA_MOTION_GROUP_ID") |
|
|
| url = f"{instance_url}/api/v2/cells/{cell_id}/controllers/{controller_id}/motion-groups/{motion_group_id}/state" |
| headers = {"Authorization": f"Bearer {access_token}"} |
| req = urllib.request.Request(url, headers=headers) |
|
|
| with urllib.request.urlopen(req, timeout=5) as response: |
| data = json.loads(response.read()) |
| return data.get('joint_position', []) |
|
|
| def main(): |
| |
| env_path = Path(__file__).parent.parent / ".env.local" |
| if not env_path.exists(): |
| print(f"Error: {env_path} not found") |
| return 1 |
|
|
| print(f"Loading environment from {env_path}") |
| load_env_file(env_path) |
|
|
| |
| try: |
| from robots.ur5.nova_jogger import NovaJogger |
| print("✓ NovaJogger imported successfully") |
| except ImportError as e: |
| print(f"✗ Failed to import NovaJogger: {e}") |
| return 1 |
|
|
| |
| instance_url = os.getenv("NOVA_INSTANCE_URL") |
| access_token = os.getenv("NOVA_ACCESS_TOKEN") |
| cell_id = os.getenv("NOVA_CELL_ID", "cell") |
| controller_id = os.getenv("NOVA_CONTROLLER_ID") |
| motion_group_id = os.getenv("NOVA_MOTION_GROUP_ID") |
|
|
| if not all([instance_url, access_token, controller_id, motion_group_id]): |
| print("✗ Missing required environment variables") |
| return 1 |
|
|
| |
| home_position = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] |
|
|
| print("\n" + "=" * 70) |
| print("Multi-Axis Homing Test with Nova API v2") |
| print("=" * 70) |
| print(f"Instance: {instance_url}") |
| print(f"Controller: {controller_id}") |
| print(f"Motion Group: {motion_group_id}") |
| print(f"Home Position: {home_position}") |
| print() |
|
|
| |
| print("[1] Getting initial robot position...") |
| try: |
| initial_joints = get_robot_state() |
| print(f"Initial position: {[f'{j:.3f}' for j in initial_joints]}") |
| except Exception as e: |
| print(f"✗ Failed to get robot state: {e}") |
| return 1 |
|
|
| |
| deltas = [home_position[i] - initial_joints[i] for i in range(6)] |
| print(f"Deltas from home: {[f'{d:.3f}' for d in deltas]}") |
|
|
| |
| max_delta = max(abs(d) for d in deltas) |
| if max_delta < 0.001: |
| print("\n✓ Already at home position!") |
| return 0 |
|
|
| |
| base_velocity = 0.1 |
| velocities = [base_velocity * (d / max_delta) if abs(d) > 0.001 else 0.0 for d in deltas] |
|
|
| print(f"Calculated velocities: {[f'{v:.3f}' for v in velocities]} rad/s") |
| print(f"Estimated time to home: {max_delta / base_velocity:.1f} seconds") |
| print() |
|
|
| |
| jogger = NovaJogger( |
| instance_url=instance_url, |
| access_token=access_token, |
| cell_id=cell_id, |
| controller_id=controller_id, |
| motion_group_id=motion_group_id |
| ) |
|
|
| try: |
| |
| print("[2] Connecting to Nova API v2...") |
| if not jogger.connect(): |
| print("✗ Failed to connect") |
| return 1 |
| print("✓ Connected\n") |
|
|
| |
| print("[3] Starting multi-axis homing (all joints moving simultaneously)...") |
| if not jogger.start_multi_joint_jog(velocities): |
| print("✗ Failed to start multi-joint jog") |
| return 1 |
| print("✓ Homing in progress") |
|
|
| |
| print("\nProgress:") |
| start_time = time.time() |
| last_print = 0 |
|
|
| while True: |
| time.sleep(0.2) |
| elapsed = time.time() - start_time |
|
|
| |
| if elapsed - last_print >= 0.5: |
| try: |
| current_joints = get_robot_state() |
| current_deltas = [abs(home_position[i] - current_joints[i]) for i in range(6)] |
| max_current_delta = max(current_deltas) |
|
|
| print(f" t={elapsed:.1f}s | Max delta: {max_current_delta:.4f} rad | " + |
| f"Joints: {[f'{j:.3f}' for j in current_joints]}") |
|
|
| |
| if max_current_delta < 0.01: |
| print(f"\n✓ Reached home position in {elapsed:.1f} seconds!") |
| break |
|
|
| last_print = elapsed |
|
|
| except Exception as e: |
| print(f" Warning: Failed to get state: {e}") |
|
|
| |
| if elapsed > 30: |
| print("\n! Timeout after 30 seconds") |
| break |
|
|
| |
| print("\n[4] Stopping...") |
| if not jogger.stop(): |
| print("✗ Failed to stop") |
| return 1 |
| print("✓ Stopped") |
|
|
| |
| time.sleep(0.5) |
| final_joints = get_robot_state() |
| final_deltas = [abs(home_position[i] - final_joints[i]) for i in range(6)] |
|
|
| print("\n" + "=" * 70) |
| print("HOMING RESULTS") |
| print("=" * 70) |
| print(f"Initial position: {[f'{j:.3f}' for j in initial_joints]}") |
| print(f"Final position: {[f'{j:.3f}' for j in final_joints]}") |
| print(f"Home position: {[f'{j:.3f}' for j in home_position]}") |
| print(f"Final deltas: {[f'{d:.4f}' for d in final_deltas]}") |
| print(f"Max delta: {max(final_deltas):.4f} rad") |
| print() |
|
|
| if max(final_deltas) < 0.05: |
| print("✓ HOMING SUCCESSFUL - Robot is at home position") |
| else: |
| print("⚠ HOMING INCOMPLETE - Robot not fully at home") |
|
|
| |
| print("\n[5] Disconnecting...") |
| jogger.disconnect() |
| print("✓ Disconnected") |
|
|
| print("\n" + "=" * 70) |
| print("✓ MULTI-AXIS HOMING TEST COMPLETE") |
| print("=" * 70) |
| print("\nKey advantages of v2 API multi-axis homing:") |
| print("- All joints move simultaneously (parallel motion)") |
| print("- Faster homing compared to sequential joint movement") |
| print("- Smoother motion with proportional velocity control") |
| print("- More efficient use of robot capabilities") |
|
|
| return 0 |
|
|
| except Exception as e: |
| print(f"\n✗ Test failed: {type(e).__name__}: {e}") |
| import traceback |
| traceback.print_exc() |
| try: |
| jogger.disconnect() |
| except: |
| pass |
| return 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|