From 9d84e7dbd4f58d249e40b82b75989b29d123c7c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:23:22 +0000 Subject: [PATCH 1/4] Initial plan From 39d0956bbc7fa30676e678444893b8092cfb4ade Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:34:03 +0000 Subject: [PATCH 2/4] Add XArm5 module with comprehensive documentation and examples Co-authored-by: sgbaird <45469701+sgbaird@users.noreply.github.com> --- CHANGELOG.md | 7 + docs/_static/images/README_xarm5.txt | 12 + docs/index.md | 1 + setup.cfg | 3 + src/ac_training_lab/xarm5/README.md | 290 ++++++++++++++++++ src/ac_training_lab/xarm5/__init__.py | 7 + .../xarm5/_scripts/basic_movement.py | 118 +++++++ .../xarm5/_scripts/gripper_demo.py | 207 +++++++++++++ .../xarm5/_scripts/safety_demo.py | 240 +++++++++++++++ .../xarm5/_scripts/track_integration.py | 197 ++++++++++++ src/ac_training_lab/xarm5/requirements.txt | 7 + 11 files changed, 1089 insertions(+) create mode 100644 docs/_static/images/README_xarm5.txt create mode 100644 src/ac_training_lab/xarm5/README.md create mode 100644 src/ac_training_lab/xarm5/__init__.py create mode 100644 src/ac_training_lab/xarm5/_scripts/basic_movement.py create mode 100644 src/ac_training_lab/xarm5/_scripts/gripper_demo.py create mode 100644 src/ac_training_lab/xarm5/_scripts/safety_demo.py create mode 100644 src/ac_training_lab/xarm5/_scripts/track_integration.py create mode 100644 src/ac_training_lab/xarm5/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index db43d22d..241ae4c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] ### Added +- **XArm5 Integration**: Complete module for controlling UFACTORY xArm5 robotic arm from Raspberry Pi 5 (`src/ac_training_lab/xarm5/`) + - Comprehensive README with setup instructions for Raspberry Pi 5 + - Requirements file with xarm-python-sdk dependency + - Example scripts for basic movement, gripper control, linear track integration, and safety features + - Support for rail/lift/arm combo integration with linear track control + - Network configuration guide for xArm5 control box + - Troubleshooting and safety documentation - Support for both `rpicam-vid` (Raspberry Pi OS Trixie) and `libcamera-vid` (Raspberry Pi OS Bookworm) camera commands in `src/ac_training_lab/picam/device.py` to ensure compatibility across different OS versions. - Comprehensive Unit Operations section in `docs/index.md` documenting all available capabilities including dispensing, synthesis, characterization, and robotics operations. - Expanded Training Workflows section in `docs/index.md` with 10 educational workflows including RGB/RYB color matching, titration, yeast growth optimization, vision-enabled 3D printing optimization, microscopy image stitching, and AprilTag robot path planning. diff --git a/docs/_static/images/README_xarm5.txt b/docs/_static/images/README_xarm5.txt new file mode 100644 index 00000000..8ce43acb --- /dev/null +++ b/docs/_static/images/README_xarm5.txt @@ -0,0 +1,12 @@ +Note: An image file "ufactory-xarm5.png" needs to be added to this directory. + +The image should be: +- A photo or render of the UFACTORY xArm5 robotic arm +- Approximately 50px height (width auto-scaled) +- PNG or WebP format +- Can be sourced from: + - UFACTORY official website: https://www.ufactory.cc/product-page/ufactory-xarm-5 + - Product documentation + - Community resources + +Until the image is added, the docs/index.md table entry references this image file. diff --git a/docs/index.md b/docs/index.md index f28aaa3b..723a95ee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -50,6 +50,7 @@ Here are some of the modules we have procured and are in the process of setting | [Hiwonder ArmPi FPV](https://www.hiwonder.com/products/armpi?_pos=4&_sid=a9741a308&_ss=r) | | 1 | An educational six-axis robotic arm | | | [Hiwonder JetAuto Pro](https://www.hiwonder.com/products/jetauto-pro?variant=40040875229271) | | 1 | An educational six-axis mobile cobot with a 3D depth camera and lidar | | | [MyCobot 280](https://shop.elephantrobotics.com/en-ca/collections/mycobot/products/mycobot-pi-worlds-smallest-and-lightest-six-axis-collaborative-robot) and [MyAGV](https://shop.elephantrobotics.com/en-ca/collections/myagv/products/myagv-2023-pi?variant=47262714069304) | | 1 | An educational six-axis mobile cobot | | +| [UFACTORY xArm5](https://www.ufactory.cc/product-page/ufactory-xarm-5) ([docs](../src/ac_training_lab/xarm5/)) | 🦾 | 1 | A 5-DOF collaborative robotic arm controlled from Raspberry Pi 5. Supports gripper, linear track integration, and network-based control for rail/lift/arm combo systems. | | | [AutoTrickler v4](https://autotrickler.com/pages/autotrickler-v4) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | | [ChargeMaster Supreme](https://www.rcbs.com/priming-and-powder-charging/powder-dispensers-and-scales/chargemaster-supreme-electronic-powder-dispenser/16-98943.html) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | | [Ingenuity Powder System](https://ingenuityprecision.com/product/ingenuity-powder-system/) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | diff --git a/setup.cfg b/setup.cfg index 1e2902a2..46e23589 100644 --- a/setup.cfg +++ b/setup.cfg @@ -74,6 +74,9 @@ a1-cam = boto3 wget +xarm5 = + xarm-python-sdk>=1.17.0 + [options.entry_points] # Add here console scripts like: # console_scripts = diff --git a/src/ac_training_lab/xarm5/README.md b/src/ac_training_lab/xarm5/README.md new file mode 100644 index 00000000..bfd26bfa --- /dev/null +++ b/src/ac_training_lab/xarm5/README.md @@ -0,0 +1,290 @@ +# XArm5 Control for Raspberry Pi 5 + +This module provides integration for controlling the UFACTORY xArm5 robotic arm from a Raspberry Pi 5 as part of the AC Training Lab's rail/lift/arm combo system. + +## Hardware Overview + +The **xArm5** is a 5-degree-of-freedom collaborative robotic arm manufactured by UFACTORY. It features: +- 5 servo joints with precise control +- Network-based control via TCP/IP +- Support for various end effectors (grippers, sensors) +- Compatible with linear tracks and additional accessories +- Payload capacity suitable for laboratory automation tasks + +## Prerequisites + +### Hardware Requirements +- Raspberry Pi 5 (4GB or 8GB RAM recommended) +- UFACTORY xArm5 robotic arm with control box +- Ethernet connection (recommended) or WiFi +- Power supply for both Raspberry Pi and xArm5 +- Optional: Linear track for extended workspace +- Optional: Gripper or end effector + +### Software Requirements +- Raspberry Pi OS (Bookworm or later) +- Python 3.8 or higher +- Network connectivity between Raspberry Pi and xArm5 control box + +## Installation on Raspberry Pi 5 + +### 1. Set Up Raspberry Pi OS + +If starting fresh, use the [Raspberry Pi Imager](https://www.raspberrypi.org/software/) to install Raspberry Pi OS on a microSD card. + +```bash +# Update system packages +sudo apt update +sudo apt upgrade -y +``` + +### 2. Install Python Dependencies + +```bash +# Install Python virtual environment tools +sudo apt install python3-pip python3-venv -y + +# Create a virtual environment (recommended for Bookworm OS) +python3 -m venv ~/xarm-env +source ~/xarm-env/bin/activate +``` + +### 3. Install xArm Python SDK + +**Option A: Install from PyPI (Recommended)** +```bash +pip install xarm-python-sdk +``` + +**Option B: Install from source** +```bash +git clone https://github.com/xArm-Developer/xArm-Python-SDK.git +cd xArm-Python-SDK +pip install . +``` + +### 4. Install AC Training Lab Package (Optional) + +To use this module as part of the ac-training-lab package: +```bash +# Clone the ac-training-lab repository +git clone https://github.com/AccelerationConsortium/ac-dev-lab.git +cd ac-dev-lab + +# Install with xarm5 dependencies +pip install -e . +``` + +## Network Configuration + +### Finding Your xArm5 IP Address + +1. Check the xArm5 control box display panel +2. Or check your router's DHCP client list +3. Or use network scanning tools: + ```bash + sudo apt install nmap + nmap -sn 192.168.1.0/24 # Adjust subnet to match your network + ``` + +### Static IP Configuration (Recommended) + +For reliable automation, configure a static IP for the xArm5 control box: +1. Access the control box web interface +2. Navigate to Network Settings +3. Set a static IP address (e.g., 192.168.1.100) +4. Configure gateway and DNS if needed + +## Quick Start Examples + +### Basic Connection Test + +```python +from xarm.wrapper import XArmAPI + +# Replace with your xArm5 control box IP +arm = XArmAPI('192.168.1.100') + +# Check connection and get robot info +print(f"Connected: {arm.connected}") +print(f"Version: {arm.version}") +print(f"State: {arm.state}") + +# Disconnect +arm.disconnect() +``` + +### Simple Movement Example + +```python +from xarm.wrapper import XArmAPI + +# Connect to xArm5 +arm = XArmAPI('192.168.1.100') + +# Enable motion +arm.motion_enable(enable=True) +arm.set_mode(0) # Position control mode +arm.set_state(0) # Ready state + +# Move to home position +arm.move_gohome(wait=True) + +# Move to a specific position (x, y, z in mm, angles in degrees) +arm.set_position(x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True) + +# Get current position +position = arm.get_position() +print(f"Current position: {position}") + +# Disconnect +arm.disconnect() +``` + +### Using with Gripper + +```python +from xarm.wrapper import XArmAPI + +# Connect +arm = XArmAPI('192.168.1.100') + +# Enable gripper +arm.set_gripper_enable(True) +arm.set_gripper_mode(0) + +# Open gripper +arm.set_gripper_position(800, wait=True) # 800 = fully open + +# Close gripper +arm.set_gripper_position(0, wait=True) # 0 = fully closed + +# Disconnect +arm.disconnect() +``` + +## Rail/Lift/Arm Integration + +For integration with linear tracks and vertical lifts: + +### Linear Track Control + +```python +from xarm.wrapper import XArmAPI + +arm = XArmAPI('192.168.1.100', enable_track=True) + +# Enable linear motor (track) +arm.set_linear_motor_enable(True) + +# Move track to specific position (in mm) +arm.set_linear_motor_pos(500, wait=True) + +# Get track position +track_pos = arm.get_linear_motor_pos() +print(f"Track position: {track_pos} mm") +``` + +## Example Scripts + +See the `_scripts/` directory for more examples: +- `basic_movement.py` - Basic arm movements +- `gripper_demo.py` - Gripper control demonstrations +- `track_integration.py` - Linear track coordination +- `safety_demo.py` - Emergency stop and safety features + +## Safety Considerations + +āš ļø **IMPORTANT SAFETY WARNINGS:** + +1. **Keep clear of the robot workspace** during operation +2. **Test in simulation first** before running on real hardware +3. **Use emergency stop** features in your code +4. **Start with slow speeds** when testing new movements +5. **Monitor the robot** during initial testing +6. **Set appropriate workspace limits** to prevent collisions +7. **Enable collision detection** when available + +### Emergency Stop + +```python +# In case of emergency +arm.emergency_stop() + +# Reset after emergency stop +arm.clean_error() +arm.set_state(0) +``` + +## Troubleshooting + +### Connection Issues + +**Problem:** Cannot connect to xArm5 +- Verify both devices are on the same network +- Check IP address is correct +- Ping the control box: `ping 192.168.1.100` +- Check firewall settings on Raspberry Pi +- Ensure control box is powered on and initialized + +**Problem:** Connection drops frequently +- Use wired Ethernet instead of WiFi +- Check network cable quality +- Configure static IP addresses +- Reduce network traffic on the subnet + +### Movement Issues + +**Problem:** Robot doesn't move +- Check if motion is enabled: `arm.motion_enable(True)` +- Verify robot state: `arm.get_state()` +- Clear any errors: `arm.clean_error()` +- Check if servo is attached: `arm.set_servo_attach()` + +**Problem:** Jerky or unexpected movements +- Reduce speed and acceleration parameters +- Check for network latency +- Use `wait=True` for sequential movements +- Verify trajectory planning settings + +### Python Environment Issues + +**Problem:** Module not found errors +- Ensure virtual environment is activated +- Reinstall xarm-python-sdk: `pip install --upgrade xarm-python-sdk` +- Check Python version compatibility (3.8+) + +## Additional Resources + +### Official Documentation +- [xArm Python SDK GitHub](https://github.com/xArm-Developer/xArm-Python-SDK) +- [xArm API Documentation](https://github.com/xArm-Developer/xArm-Python-SDK/blob/master/doc/api/xarm_api.md) +- [UFACTORY Official Website](https://www.ufactory.cc/) + +### Community Resources +- [AccelerationConsortium xarm-translocation](https://github.com/AccelerationConsortium/xarm-translocation) - Advanced control package +- [xArm ROS Integration](https://github.com/xArm-Developer/xarm_ros) - For ROS users +- [xArm ROS2 Integration](https://github.com/xArm-Developer/xarm_ros2) - For ROS2 users + +### AC Training Lab Resources +- [AC Training Lab Documentation](https://ac-training-lab.readthedocs.io/) +- [AC Training Lab GitHub](https://github.com/AccelerationConsortium/ac-training-lab) +- [Discussions](https://github.com/AccelerationConsortium/ac-training-lab/discussions) + +## Contributing + +To contribute improvements or report issues: +1. Open an issue on the [AC Training Lab GitHub](https://github.com/AccelerationConsortium/ac-training-lab/issues) +2. Join the discussion at [AC Discussions](https://github.com/AccelerationConsortium/ac-training-lab/discussions) +3. Submit a pull request with your changes + +## License + +This module is part of the ac-training-lab project and follows the same MIT License. + +## Support + +For support with: +- **Hardware issues**: Contact UFACTORY support +- **Software/integration issues**: Open an issue on GitHub +- **General questions**: Join our discussions or reach out to sterling.baird@utoronto.ca diff --git a/src/ac_training_lab/xarm5/__init__.py b/src/ac_training_lab/xarm5/__init__.py new file mode 100644 index 00000000..f380c29d --- /dev/null +++ b/src/ac_training_lab/xarm5/__init__.py @@ -0,0 +1,7 @@ +"""XArm5 control module for Raspberry Pi 5. + +This module provides integration for controlling the UFACTORY xArm5 robotic arm +from a Raspberry Pi 5 as part of the AC Training Lab's rail/lift/arm combo system. +""" + +__version__ = "0.1.0" diff --git a/src/ac_training_lab/xarm5/_scripts/basic_movement.py b/src/ac_training_lab/xarm5/_scripts/basic_movement.py new file mode 100644 index 00000000..0cbee376 --- /dev/null +++ b/src/ac_training_lab/xarm5/_scripts/basic_movement.py @@ -0,0 +1,118 @@ +"""Basic movement example for xArm5. + +This script demonstrates basic movement commands for the xArm5 robotic arm. +Includes connection, enabling motion, moving to positions, and safe shutdown. +""" + +from xarm.wrapper import XArmAPI +import time + +# Configuration +XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address + + +def main(): + """Execute basic movement demonstration.""" + print("=" * 60) + print("XArm5 Basic Movement Demo") + print("=" * 60) + + # Connect to xArm5 + print(f"\nConnecting to xArm5 at {XARM_IP}...") + arm = XArmAPI(XARM_IP) + + if not arm.connected: + print("āŒ Failed to connect to xArm5") + return + + print("āœ“ Connected successfully") + print(f" Robot version: {arm.version}") + print(f" Robot state: {arm.state}") + + try: + # Enable motion + print("\nEnabling motion...") + arm.motion_enable(enable=True) + arm.set_mode(0) # Position control mode + arm.set_state(0) # Ready state + time.sleep(1) + + # Move to home position + print("\nMoving to home position...") + arm.move_gohome(wait=True) + print("āœ“ Reached home position") + time.sleep(1) + + # Get current position + position = arm.get_position() + print(f"\nCurrent position:") + print(f" X: {position[1][0]:.2f} mm") + print(f" Y: {position[1][1]:.2f} mm") + print(f" Z: {position[1][2]:.2f} mm") + print(f" Roll: {position[1][3]:.2f}°") + print(f" Pitch: {position[1][4]:.2f}°") + print(f" Yaw: {position[1][5]:.2f}°") + + # Movement sequence + print("\nExecuting movement sequence...") + + # Position 1 + print("\n Moving to position 1 (forward)...") + arm.set_position( + x=300, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print(" āœ“ Position 1 reached") + time.sleep(1) + + # Position 2 + print("\n Moving to position 2 (left)...") + arm.set_position( + x=300, y=100, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print(" āœ“ Position 2 reached") + time.sleep(1) + + # Position 3 + print("\n Moving to position 3 (right)...") + arm.set_position( + x=300, y=-100, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print(" āœ“ Position 3 reached") + time.sleep(1) + + # Return to home + print("\nReturning to home position...") + arm.move_gohome(wait=True) + print("āœ“ Returned to home") + + print("\n" + "=" * 60) + print("Movement sequence completed successfully!") + print("=" * 60) + + except KeyboardInterrupt: + print("\n\nāš ļø Interrupted by user - stopping robot...") + arm.emergency_stop() + + except Exception as e: + print(f"\nāŒ Error occurred: {e}") + print("Attempting emergency stop...") + arm.emergency_stop() + + finally: + # Clean up + print("\nDisconnecting...") + arm.disconnect() + print("āœ“ Disconnected safely") + + +if __name__ == "__main__": + main() diff --git a/src/ac_training_lab/xarm5/_scripts/gripper_demo.py b/src/ac_training_lab/xarm5/_scripts/gripper_demo.py new file mode 100644 index 00000000..2c92bb71 --- /dev/null +++ b/src/ac_training_lab/xarm5/_scripts/gripper_demo.py @@ -0,0 +1,207 @@ +"""Gripper control demonstration for xArm5. + +This script demonstrates how to control various gripper types with the xArm5. +Supports standard gripper, BIO gripper, and RobotIQ gripper. +""" + +from xarm.wrapper import XArmAPI +import time + +# Configuration +XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address +GRIPPER_TYPE = "standard" # Options: "standard", "bio", "robotiq" + + +def demo_standard_gripper(arm): + """Demonstrate standard xArm gripper.""" + print("\n" + "-" * 60) + print("Standard Gripper Demo") + print("-" * 60) + + # Enable gripper + print("\nEnabling gripper...") + arm.set_gripper_enable(True) + arm.set_gripper_mode(0) + time.sleep(1) + + # Get current gripper position + ret = arm.get_gripper_position() + if ret[0] == 0: + print(f"Current gripper position: {ret[1]} (0=closed, 800=open)") + + # Open gripper + print("\nOpening gripper...") + arm.set_gripper_position(800, wait=True) + print("āœ“ Gripper opened") + time.sleep(1) + + # Partially close + print("\nPartially closing gripper (400/800)...") + arm.set_gripper_position(400, wait=True) + print("āœ“ Gripper at 50% position") + time.sleep(1) + + # Close gripper + print("\nClosing gripper...") + arm.set_gripper_position(0, wait=True) + print("āœ“ Gripper closed") + time.sleep(1) + + # Open again + print("\nOpening gripper again...") + arm.set_gripper_position(800, wait=True) + print("āœ“ Gripper opened") + + +def demo_bio_gripper(arm): + """Demonstrate BIO gripper.""" + print("\n" + "-" * 60) + print("BIO Gripper Demo") + print("-" * 60) + + # Enable BIO gripper + print("\nEnabling BIO gripper...") + arm.set_bio_gripper_enable(True) + time.sleep(1) + + # Set speed + arm.set_bio_gripper_speed(500) + + # Get status + ret = arm.get_bio_gripper_status() + if ret[0] == 0: + print(f"BIO Gripper status: {ret[1]}") + + # Open gripper + print("\nOpening BIO gripper...") + arm.open_bio_gripper(wait=True) + print("āœ“ BIO gripper opened") + time.sleep(1) + + # Close gripper + print("\nClosing BIO gripper...") + arm.close_bio_gripper(wait=True) + print("āœ“ BIO gripper closed") + time.sleep(1) + + # Open again + print("\nOpening BIO gripper again...") + arm.open_bio_gripper(wait=True) + print("āœ“ BIO gripper opened") + + +def demo_robotiq_gripper(arm): + """Demonstrate RobotIQ gripper.""" + print("\n" + "-" * 60) + print("RobotIQ Gripper Demo") + print("-" * 60) + + # Reset and activate + print("\nInitializing RobotIQ gripper...") + arm.robotiq_reset() + time.sleep(2) + arm.robotiq_set_activate(True) + time.sleep(2) + + # Get status + ret = arm.robotiq_get_status() + if ret[0] == 0: + print(f"RobotIQ status: {ret[1]}") + + # Open gripper + print("\nOpening RobotIQ gripper...") + arm.robotiq_open(wait=True) + print("āœ“ RobotIQ gripper opened") + time.sleep(1) + + # Set specific position + print("\nSetting RobotIQ gripper to 50% (128/255)...") + arm.robotiq_set_position(128, wait=True) + print("āœ“ RobotIQ gripper at 50% position") + time.sleep(1) + + # Close gripper + print("\nClosing RobotIQ gripper...") + arm.robotiq_close(wait=True) + print("āœ“ RobotIQ gripper closed") + time.sleep(1) + + # Open again + print("\nOpening RobotIQ gripper again...") + arm.robotiq_open(wait=True) + print("āœ“ RobotIQ gripper opened") + + +def main(): + """Execute gripper demonstration.""" + print("=" * 60) + print("XArm5 Gripper Control Demo") + print(f"Gripper Type: {GRIPPER_TYPE.upper()}") + print("=" * 60) + + # Connect to xArm5 + print(f"\nConnecting to xArm5 at {XARM_IP}...") + arm = XArmAPI(XARM_IP) + + if not arm.connected: + print("āŒ Failed to connect to xArm5") + return + + print("āœ“ Connected successfully") + + try: + # Enable motion + print("\nEnabling motion...") + arm.motion_enable(enable=True) + arm.set_mode(0) + arm.set_state(0) + time.sleep(1) + + # Move to working position + print("\nMoving to working position...") + arm.set_position( + x=300, y=0, z=300, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print("āœ“ In working position") + + # Execute gripper demo based on type + if GRIPPER_TYPE.lower() == "standard": + demo_standard_gripper(arm) + elif GRIPPER_TYPE.lower() == "bio": + demo_bio_gripper(arm) + elif GRIPPER_TYPE.lower() == "robotiq": + demo_robotiq_gripper(arm) + else: + print(f"\nāŒ Unknown gripper type: {GRIPPER_TYPE}") + print(" Valid options: standard, bio, robotiq") + + # Return to home + print("\nReturning to home position...") + arm.move_gohome(wait=True) + print("āœ“ Returned to home") + + print("\n" + "=" * 60) + print("Gripper demo completed successfully!") + print("=" * 60) + + except KeyboardInterrupt: + print("\n\nāš ļø Interrupted by user - stopping robot...") + arm.emergency_stop() + + except Exception as e: + print(f"\nāŒ Error occurred: {e}") + print("Attempting emergency stop...") + arm.emergency_stop() + + finally: + # Clean up + print("\nDisconnecting...") + arm.disconnect() + print("āœ“ Disconnected safely") + + +if __name__ == "__main__": + main() diff --git a/src/ac_training_lab/xarm5/_scripts/safety_demo.py b/src/ac_training_lab/xarm5/_scripts/safety_demo.py new file mode 100644 index 00000000..82ebd25c --- /dev/null +++ b/src/ac_training_lab/xarm5/_scripts/safety_demo.py @@ -0,0 +1,240 @@ +"""Safety features demonstration for xArm5. + +This script demonstrates safety features including emergency stop, +error handling, collision detection, and safe workspace limits. +""" + +from xarm.wrapper import XArmAPI +import time + +# Configuration +XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address + +# Workspace limits (in mm) +WORKSPACE_LIMITS = { + 'x_min': 100, + 'x_max': 500, + 'y_min': -300, + 'y_max': 300, + 'z_min': 50, + 'z_max': 500 +} + + +def check_workspace_limits(position): + """Check if position is within safe workspace limits. + + Args: + position: Tuple of (x, y, z) coordinates in mm + + Returns: + bool: True if position is safe, False otherwise + """ + x, y, z = position + + if not (WORKSPACE_LIMITS['x_min'] <= x <= WORKSPACE_LIMITS['x_max']): + print(f"āš ļø X position {x} outside limits [{WORKSPACE_LIMITS['x_min']}, {WORKSPACE_LIMITS['x_max']}]") + return False + + if not (WORKSPACE_LIMITS['y_min'] <= y <= WORKSPACE_LIMITS['y_max']): + print(f"āš ļø Y position {y} outside limits [{WORKSPACE_LIMITS['y_min']}, {WORKSPACE_LIMITS['y_max']}]") + return False + + if not (WORKSPACE_LIMITS['z_min'] <= z <= WORKSPACE_LIMITS['z_max']): + print(f"āš ļø Z position {z} outside limits [{WORKSPACE_LIMITS['z_min']}, {WORKSPACE_LIMITS['z_max']}]") + return False + + return True + + +def demo_safe_movement(arm): + """Demonstrate safe movement with workspace limits.""" + print("\n" + "-" * 60) + print("Safe Movement Demo") + print("-" * 60) + + # Test safe position + safe_position = (300, 0, 200) + print(f"\nTesting safe position {safe_position}...") + if check_workspace_limits(safe_position): + print("āœ“ Position is safe - executing movement") + arm.set_position( + x=safe_position[0], + y=safe_position[1], + z=safe_position[2], + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print("āœ“ Movement completed") + time.sleep(1) + else: + print("āŒ Position rejected - outside workspace limits") + + # Test unsafe position (too far) + unsafe_position = (700, 0, 200) + print(f"\nTesting unsafe position {unsafe_position}...") + if check_workspace_limits(unsafe_position): + print("āœ“ Position is safe - would execute movement") + else: + print("āŒ Position rejected - outside workspace limits") + print(" Movement NOT executed for safety") + + +def demo_error_handling(arm): + """Demonstrate error detection and recovery.""" + print("\n" + "-" * 60) + print("Error Handling Demo") + print("-" * 60) + + # Check for errors + err_code = arm.error_code + warn_code = arm.warn_code + + print(f"\nCurrent error code: {err_code}") + print(f"Current warning code: {warn_code}") + + if arm.has_error: + print("āš ļø Robot has errors - attempting to clear...") + arm.clean_error() + time.sleep(1) + + if arm.has_error: + print("āŒ Failed to clear errors") + else: + print("āœ“ Errors cleared successfully") + else: + print("āœ“ No errors detected") + + if arm.has_warn: + print("āš ļø Robot has warnings - attempting to clear...") + arm.clean_warn() + time.sleep(1) + + if arm.has_warn: + print("āŒ Failed to clear warnings") + else: + print("āœ“ Warnings cleared successfully") + else: + print("āœ“ No warnings detected") + + +def demo_collision_sensitivity(arm): + """Demonstrate collision sensitivity settings.""" + print("\n" + "-" * 60) + print("Collision Sensitivity Demo") + print("-" * 60) + + # Set collision sensitivity (0-5, where 0 is least sensitive) + sensitivity = 3 + print(f"\nSetting collision sensitivity to {sensitivity}...") + arm.set_collision_sensitivity(sensitivity) + time.sleep(0.5) + + print(f"āœ“ Collision sensitivity set to {arm.collision_sensitivity}") + print(" Note: Higher values = more sensitive collision detection") + + +def demo_emergency_stop(arm): + """Demonstrate emergency stop functionality.""" + print("\n" + "-" * 60) + print("Emergency Stop Demo") + print("-" * 60) + + print("\nStarting a slow movement...") + print("(In a real emergency, you would call arm.emergency_stop())") + + # Start a movement + arm.set_position( + x=350, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=50, # Slow speed for demonstration + wait=False # Don't wait for completion + ) + + # Simulate waiting a bit + time.sleep(0.5) + + # Emergency stop + print("\nāš ļø EMERGENCY STOP triggered!") + arm.emergency_stop() + print("āœ“ Robot stopped immediately") + + # Recovery process + print("\nRecovering from emergency stop...") + time.sleep(1) + + # Clear errors and reset state + arm.clean_error() + arm.set_state(0) + time.sleep(1) + + print("āœ“ Robot recovered and ready") + + +def main(): + """Execute safety features demonstration.""" + print("=" * 60) + print("XArm5 Safety Features Demo") + print("=" * 60) + + print("\nWorkspace Limits:") + for key, value in WORKSPACE_LIMITS.items(): + print(f" {key}: {value} mm") + + # Connect to xArm5 + print(f"\nConnecting to xArm5 at {XARM_IP}...") + arm = XArmAPI(XARM_IP) + + if not arm.connected: + print("āŒ Failed to connect to xArm5") + return + + print("āœ“ Connected successfully") + + try: + # Enable motion + print("\nEnabling motion...") + arm.motion_enable(enable=True) + arm.set_mode(0) + arm.set_state(0) + time.sleep(1) + + # Move to starting position + print("\nMoving to starting position...") + arm.move_gohome(wait=True) + print("āœ“ At starting position") + + # Run safety demonstrations + demo_error_handling(arm) + demo_collision_sensitivity(arm) + demo_safe_movement(arm) + demo_emergency_stop(arm) + + # Return to home + print("\nReturning to home position...") + arm.move_gohome(wait=True) + print("āœ“ Returned to home") + + print("\n" + "=" * 60) + print("Safety demo completed successfully!") + print("=" * 60) + + except KeyboardInterrupt: + print("\n\nāš ļø Interrupted by user - stopping robot...") + arm.emergency_stop() + + except Exception as e: + print(f"\nāŒ Error occurred: {e}") + print("Attempting emergency stop...") + arm.emergency_stop() + + finally: + # Clean up + print("\nDisconnecting...") + arm.disconnect() + print("āœ“ Disconnected safely") + + +if __name__ == "__main__": + main() diff --git a/src/ac_training_lab/xarm5/_scripts/track_integration.py b/src/ac_training_lab/xarm5/_scripts/track_integration.py new file mode 100644 index 00000000..1dc0c625 --- /dev/null +++ b/src/ac_training_lab/xarm5/_scripts/track_integration.py @@ -0,0 +1,197 @@ +"""Linear track integration demonstration for xArm5. + +This script demonstrates how to control the xArm5 with a linear track (rail) +for extended workspace and coordinated movements. +""" + +from xarm.wrapper import XArmAPI +import time + +# Configuration +XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address +TRACK_ENABLED = True # Set to False if no track is attached + + +def main(): + """Execute linear track integration demonstration.""" + print("=" * 60) + print("XArm5 Linear Track Integration Demo") + print("=" * 60) + + # Connect to xArm5 with track enabled + print(f"\nConnecting to xArm5 at {XARM_IP}...") + if TRACK_ENABLED: + print("Track mode: ENABLED") + arm = XArmAPI(XARM_IP, enable_track=True) + else: + print("Track mode: DISABLED") + arm = XArmAPI(XARM_IP) + + if not arm.connected: + print("āŒ Failed to connect to xArm5") + return + + print("āœ“ Connected successfully") + print(f" Robot version: {arm.version}") + + try: + # Enable motion + print("\nEnabling motion...") + arm.motion_enable(enable=True) + arm.set_mode(0) + arm.set_state(0) + time.sleep(1) + + if TRACK_ENABLED: + # Linear track operations + print("\n" + "-" * 60) + print("Linear Track Control") + print("-" * 60) + + # Enable linear motor + print("\nEnabling linear motor (track)...") + arm.set_linear_motor_enable(True) + time.sleep(1) + + # Get track status + ret = arm.get_linear_motor_status() + print(f"Track status: {ret}") + + # Get current track position + ret = arm.get_linear_motor_pos() + if ret[0] == 0: + current_pos = ret[1] + print(f"Current track position: {current_pos} mm") + + # Move track to position 1 (0 mm - home) + print("\nMoving track to home position (0 mm)...") + arm.set_linear_motor_pos(0, wait=True) + print("āœ“ Track at home position") + time.sleep(1) + + # Verify position + ret = arm.get_linear_motor_pos() + if ret[0] == 0: + print(f" Confirmed position: {ret[1]} mm") + + # Move track to position 2 (300 mm) + print("\nMoving track to position 300 mm...") + arm.set_linear_motor_pos(300, wait=True) + print("āœ“ Track at 300 mm") + time.sleep(1) + + # Verify position + ret = arm.get_linear_motor_pos() + if ret[0] == 0: + print(f" Confirmed position: {ret[1]} mm") + + # Move track to position 3 (600 mm) + print("\nMoving track to position 600 mm...") + arm.set_linear_motor_pos(600, wait=True) + print("āœ“ Track at 600 mm") + time.sleep(1) + + # Verify position + ret = arm.get_linear_motor_pos() + if ret[0] == 0: + print(f" Confirmed position: {ret[1]} mm") + + # Coordinated arm and track movement + print("\n" + "-" * 60) + print("Coordinated Arm + Track Movement") + print("-" * 60) + + # Position 1: Track at 0, arm reaching forward + print("\nPosition 1: Track home, arm forward...") + arm.set_linear_motor_pos(0, wait=True) + arm.set_position( + x=400, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print("āœ“ Position 1 reached") + time.sleep(1) + + # Position 2: Track at 300, arm centered + print("\nPosition 2: Track 300mm, arm centered...") + arm.set_linear_motor_pos(300, wait=True) + arm.set_position( + x=300, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print("āœ“ Position 2 reached") + time.sleep(1) + + # Position 3: Track at 600, arm reaching back + print("\nPosition 3: Track 600mm, arm back...") + arm.set_linear_motor_pos(600, wait=True) + arm.set_position( + x=200, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + print("āœ“ Position 3 reached") + time.sleep(1) + + # Return track to home + print("\nReturning track to home position...") + arm.set_linear_motor_pos(0, wait=True) + print("āœ“ Track at home") + + else: + # Just arm movements without track + print("\n(Track disabled - performing arm-only movements)") + + print("\nMoving to position 1...") + arm.set_position( + x=300, y=0, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + time.sleep(1) + + print("\nMoving to position 2...") + arm.set_position( + x=300, y=100, z=200, + roll=180, pitch=0, yaw=0, + speed=100, + wait=True + ) + time.sleep(1) + + # Return arm to home + print("\nReturning arm to home position...") + arm.move_gohome(wait=True) + print("āœ“ Arm at home") + + print("\n" + "=" * 60) + print("Track integration demo completed successfully!") + print("=" * 60) + + except KeyboardInterrupt: + print("\n\nāš ļø Interrupted by user - stopping robot...") + arm.emergency_stop() + if TRACK_ENABLED: + arm.set_linear_motor_stop() + + except Exception as e: + print(f"\nāŒ Error occurred: {e}") + print("Attempting emergency stop...") + arm.emergency_stop() + if TRACK_ENABLED: + arm.set_linear_motor_stop() + + finally: + # Clean up + print("\nDisconnecting...") + arm.disconnect() + print("āœ“ Disconnected safely") + + +if __name__ == "__main__": + main() diff --git a/src/ac_training_lab/xarm5/requirements.txt b/src/ac_training_lab/xarm5/requirements.txt new file mode 100644 index 00000000..3e16de63 --- /dev/null +++ b/src/ac_training_lab/xarm5/requirements.txt @@ -0,0 +1,7 @@ +# XArm5 Raspberry Pi Control Requirements + +# Official UFACTORY xArm Python SDK +xarm-python-sdk>=1.17.0 + +# Core dependencies (usually included with SDK) +numpy>=1.20.0 From 20f720228507256c4d889f2513acdb5980210a83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:36:32 +0000 Subject: [PATCH 3/4] Fix linting issues in xarm5 module Co-authored-by: sgbaird <45469701+sgbaird@users.noreply.github.com> --- .../xarm5/_scripts/basic_movement.py | 50 ++++---- .../xarm5/_scripts/gripper_demo.py | 66 +++++----- .../xarm5/_scripts/safety_demo.py | 119 ++++++++++-------- .../xarm5/_scripts/track_integration.py | 82 +++++------- src/ac_training_lab/xarm5/requirements.txt | 4 +- 5 files changed, 155 insertions(+), 166 deletions(-) diff --git a/src/ac_training_lab/xarm5/_scripts/basic_movement.py b/src/ac_training_lab/xarm5/_scripts/basic_movement.py index 0cbee376..13920867 100644 --- a/src/ac_training_lab/xarm5/_scripts/basic_movement.py +++ b/src/ac_training_lab/xarm5/_scripts/basic_movement.py @@ -4,9 +4,10 @@ Includes connection, enabling motion, moving to positions, and safe shutdown. """ -from xarm.wrapper import XArmAPI import time +from xarm.wrapper import XArmAPI + # Configuration XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address @@ -16,19 +17,19 @@ def main(): print("=" * 60) print("XArm5 Basic Movement Demo") print("=" * 60) - + # Connect to xArm5 print(f"\nConnecting to xArm5 at {XARM_IP}...") arm = XArmAPI(XARM_IP) - + if not arm.connected: print("āŒ Failed to connect to xArm5") return - + print("āœ“ Connected successfully") print(f" Robot version: {arm.version}") print(f" Robot state: {arm.state}") - + try: # Enable motion print("\nEnabling motion...") @@ -36,77 +37,68 @@ def main(): arm.set_mode(0) # Position control mode arm.set_state(0) # Ready state time.sleep(1) - + # Move to home position print("\nMoving to home position...") arm.move_gohome(wait=True) print("āœ“ Reached home position") time.sleep(1) - + # Get current position position = arm.get_position() - print(f"\nCurrent position:") + print("\nCurrent position:") print(f" X: {position[1][0]:.2f} mm") print(f" Y: {position[1][1]:.2f} mm") print(f" Z: {position[1][2]:.2f} mm") print(f" Roll: {position[1][3]:.2f}°") print(f" Pitch: {position[1][4]:.2f}°") print(f" Yaw: {position[1][5]:.2f}°") - + # Movement sequence print("\nExecuting movement sequence...") - + # Position 1 print("\n Moving to position 1 (forward)...") arm.set_position( - x=300, y=0, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print(" āœ“ Position 1 reached") time.sleep(1) - + # Position 2 print("\n Moving to position 2 (left)...") arm.set_position( - x=300, y=100, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print(" āœ“ Position 2 reached") time.sleep(1) - + # Position 3 print("\n Moving to position 3 (right)...") arm.set_position( - x=300, y=-100, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=-100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print(" āœ“ Position 3 reached") time.sleep(1) - + # Return to home print("\nReturning to home position...") arm.move_gohome(wait=True) print("āœ“ Returned to home") - + print("\n" + "=" * 60) print("Movement sequence completed successfully!") print("=" * 60) - + except KeyboardInterrupt: print("\n\nāš ļø Interrupted by user - stopping robot...") arm.emergency_stop() - + except Exception as e: print(f"\nāŒ Error occurred: {e}") print("Attempting emergency stop...") arm.emergency_stop() - + finally: # Clean up print("\nDisconnecting...") diff --git a/src/ac_training_lab/xarm5/_scripts/gripper_demo.py b/src/ac_training_lab/xarm5/_scripts/gripper_demo.py index 2c92bb71..601cfc2e 100644 --- a/src/ac_training_lab/xarm5/_scripts/gripper_demo.py +++ b/src/ac_training_lab/xarm5/_scripts/gripper_demo.py @@ -4,9 +4,10 @@ Supports standard gripper, BIO gripper, and RobotIQ gripper. """ -from xarm.wrapper import XArmAPI import time +from xarm.wrapper import XArmAPI + # Configuration XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address GRIPPER_TYPE = "standard" # Options: "standard", "bio", "robotiq" @@ -17,36 +18,36 @@ def demo_standard_gripper(arm): print("\n" + "-" * 60) print("Standard Gripper Demo") print("-" * 60) - + # Enable gripper print("\nEnabling gripper...") arm.set_gripper_enable(True) arm.set_gripper_mode(0) time.sleep(1) - + # Get current gripper position ret = arm.get_gripper_position() if ret[0] == 0: print(f"Current gripper position: {ret[1]} (0=closed, 800=open)") - + # Open gripper print("\nOpening gripper...") arm.set_gripper_position(800, wait=True) print("āœ“ Gripper opened") time.sleep(1) - + # Partially close print("\nPartially closing gripper (400/800)...") arm.set_gripper_position(400, wait=True) print("āœ“ Gripper at 50% position") time.sleep(1) - + # Close gripper print("\nClosing gripper...") arm.set_gripper_position(0, wait=True) print("āœ“ Gripper closed") time.sleep(1) - + # Open again print("\nOpening gripper again...") arm.set_gripper_position(800, wait=True) @@ -58,32 +59,32 @@ def demo_bio_gripper(arm): print("\n" + "-" * 60) print("BIO Gripper Demo") print("-" * 60) - + # Enable BIO gripper print("\nEnabling BIO gripper...") arm.set_bio_gripper_enable(True) time.sleep(1) - + # Set speed arm.set_bio_gripper_speed(500) - + # Get status ret = arm.get_bio_gripper_status() if ret[0] == 0: print(f"BIO Gripper status: {ret[1]}") - + # Open gripper print("\nOpening BIO gripper...") arm.open_bio_gripper(wait=True) print("āœ“ BIO gripper opened") time.sleep(1) - + # Close gripper print("\nClosing BIO gripper...") arm.close_bio_gripper(wait=True) print("āœ“ BIO gripper closed") time.sleep(1) - + # Open again print("\nOpening BIO gripper again...") arm.open_bio_gripper(wait=True) @@ -95,37 +96,37 @@ def demo_robotiq_gripper(arm): print("\n" + "-" * 60) print("RobotIQ Gripper Demo") print("-" * 60) - + # Reset and activate print("\nInitializing RobotIQ gripper...") arm.robotiq_reset() time.sleep(2) arm.robotiq_set_activate(True) time.sleep(2) - + # Get status ret = arm.robotiq_get_status() if ret[0] == 0: print(f"RobotIQ status: {ret[1]}") - + # Open gripper print("\nOpening RobotIQ gripper...") arm.robotiq_open(wait=True) print("āœ“ RobotIQ gripper opened") time.sleep(1) - + # Set specific position print("\nSetting RobotIQ gripper to 50% (128/255)...") arm.robotiq_set_position(128, wait=True) print("āœ“ RobotIQ gripper at 50% position") time.sleep(1) - + # Close gripper print("\nClosing RobotIQ gripper...") arm.robotiq_close(wait=True) print("āœ“ RobotIQ gripper closed") time.sleep(1) - + # Open again print("\nOpening RobotIQ gripper again...") arm.robotiq_open(wait=True) @@ -138,17 +139,17 @@ def main(): print("XArm5 Gripper Control Demo") print(f"Gripper Type: {GRIPPER_TYPE.upper()}") print("=" * 60) - + # Connect to xArm5 print(f"\nConnecting to xArm5 at {XARM_IP}...") arm = XArmAPI(XARM_IP) - + if not arm.connected: print("āŒ Failed to connect to xArm5") return - + print("āœ“ Connected successfully") - + try: # Enable motion print("\nEnabling motion...") @@ -156,17 +157,14 @@ def main(): arm.set_mode(0) arm.set_state(0) time.sleep(1) - + # Move to working position print("\nMoving to working position...") arm.set_position( - x=300, y=0, z=300, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=0, z=300, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print("āœ“ In working position") - + # Execute gripper demo based on type if GRIPPER_TYPE.lower() == "standard": demo_standard_gripper(arm) @@ -177,25 +175,25 @@ def main(): else: print(f"\nāŒ Unknown gripper type: {GRIPPER_TYPE}") print(" Valid options: standard, bio, robotiq") - + # Return to home print("\nReturning to home position...") arm.move_gohome(wait=True) print("āœ“ Returned to home") - + print("\n" + "=" * 60) print("Gripper demo completed successfully!") print("=" * 60) - + except KeyboardInterrupt: print("\n\nāš ļø Interrupted by user - stopping robot...") arm.emergency_stop() - + except Exception as e: print(f"\nāŒ Error occurred: {e}") print("Attempting emergency stop...") arm.emergency_stop() - + finally: # Clean up print("\nDisconnecting...") diff --git a/src/ac_training_lab/xarm5/_scripts/safety_demo.py b/src/ac_training_lab/xarm5/_scripts/safety_demo.py index 82ebd25c..89f8159e 100644 --- a/src/ac_training_lab/xarm5/_scripts/safety_demo.py +++ b/src/ac_training_lab/xarm5/_scripts/safety_demo.py @@ -4,46 +4,53 @@ error handling, collision detection, and safe workspace limits. """ -from xarm.wrapper import XArmAPI import time +from xarm.wrapper import XArmAPI + # Configuration XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address # Workspace limits (in mm) WORKSPACE_LIMITS = { - 'x_min': 100, - 'x_max': 500, - 'y_min': -300, - 'y_max': 300, - 'z_min': 50, - 'z_max': 500 + "x_min": 100, + "x_max": 500, + "y_min": -300, + "y_max": 300, + "z_min": 50, + "z_max": 500, } def check_workspace_limits(position): """Check if position is within safe workspace limits. - + Args: position: Tuple of (x, y, z) coordinates in mm - + Returns: bool: True if position is safe, False otherwise """ x, y, z = position - - if not (WORKSPACE_LIMITS['x_min'] <= x <= WORKSPACE_LIMITS['x_max']): - print(f"āš ļø X position {x} outside limits [{WORKSPACE_LIMITS['x_min']}, {WORKSPACE_LIMITS['x_max']}]") + + if not (WORKSPACE_LIMITS["x_min"] <= x <= WORKSPACE_LIMITS["x_max"]): + x_min = WORKSPACE_LIMITS["x_min"] + x_max = WORKSPACE_LIMITS["x_max"] + print(f"āš ļø X position {x} outside limits [{x_min}, {x_max}]") return False - - if not (WORKSPACE_LIMITS['y_min'] <= y <= WORKSPACE_LIMITS['y_max']): - print(f"āš ļø Y position {y} outside limits [{WORKSPACE_LIMITS['y_min']}, {WORKSPACE_LIMITS['y_max']}]") + + if not (WORKSPACE_LIMITS["y_min"] <= y <= WORKSPACE_LIMITS["y_max"]): + y_min = WORKSPACE_LIMITS["y_min"] + y_max = WORKSPACE_LIMITS["y_max"] + print(f"āš ļø Y position {y} outside limits [{y_min}, {y_max}]") return False - - if not (WORKSPACE_LIMITS['z_min'] <= z <= WORKSPACE_LIMITS['z_max']): - print(f"āš ļø Z position {z} outside limits [{WORKSPACE_LIMITS['z_min']}, {WORKSPACE_LIMITS['z_max']}]") + + if not (WORKSPACE_LIMITS["z_min"] <= z <= WORKSPACE_LIMITS["z_max"]): + z_min = WORKSPACE_LIMITS["z_min"] + z_max = WORKSPACE_LIMITS["z_max"] + print(f"āš ļø Z position {z} outside limits [{z_min}, {z_max}]") return False - + return True @@ -52,7 +59,7 @@ def demo_safe_movement(arm): print("\n" + "-" * 60) print("Safe Movement Demo") print("-" * 60) - + # Test safe position safe_position = (300, 0, 200) print(f"\nTesting safe position {safe_position}...") @@ -62,15 +69,17 @@ def demo_safe_movement(arm): x=safe_position[0], y=safe_position[1], z=safe_position[2], - roll=180, pitch=0, yaw=0, + roll=180, + pitch=0, + yaw=0, speed=100, - wait=True + wait=True, ) print("āœ“ Movement completed") time.sleep(1) else: print("āŒ Position rejected - outside workspace limits") - + # Test unsafe position (too far) unsafe_position = (700, 0, 200) print(f"\nTesting unsafe position {unsafe_position}...") @@ -86,31 +95,31 @@ def demo_error_handling(arm): print("\n" + "-" * 60) print("Error Handling Demo") print("-" * 60) - + # Check for errors err_code = arm.error_code warn_code = arm.warn_code - + print(f"\nCurrent error code: {err_code}") print(f"Current warning code: {warn_code}") - + if arm.has_error: print("āš ļø Robot has errors - attempting to clear...") arm.clean_error() time.sleep(1) - + if arm.has_error: print("āŒ Failed to clear errors") else: print("āœ“ Errors cleared successfully") else: print("āœ“ No errors detected") - + if arm.has_warn: print("āš ļø Robot has warnings - attempting to clear...") arm.clean_warn() time.sleep(1) - + if arm.has_warn: print("āŒ Failed to clear warnings") else: @@ -124,13 +133,13 @@ def demo_collision_sensitivity(arm): print("\n" + "-" * 60) print("Collision Sensitivity Demo") print("-" * 60) - + # Set collision sensitivity (0-5, where 0 is least sensitive) sensitivity = 3 print(f"\nSetting collision sensitivity to {sensitivity}...") arm.set_collision_sensitivity(sensitivity) time.sleep(0.5) - + print(f"āœ“ Collision sensitivity set to {arm.collision_sensitivity}") print(" Note: Higher values = more sensitive collision detection") @@ -140,35 +149,39 @@ def demo_emergency_stop(arm): print("\n" + "-" * 60) print("Emergency Stop Demo") print("-" * 60) - + print("\nStarting a slow movement...") print("(In a real emergency, you would call arm.emergency_stop())") - + # Start a movement arm.set_position( - x=350, y=0, z=200, - roll=180, pitch=0, yaw=0, + x=350, + y=0, + z=200, + roll=180, + pitch=0, + yaw=0, speed=50, # Slow speed for demonstration - wait=False # Don't wait for completion + wait=False, # Don't wait for completion ) - + # Simulate waiting a bit time.sleep(0.5) - + # Emergency stop print("\nāš ļø EMERGENCY STOP triggered!") arm.emergency_stop() print("āœ“ Robot stopped immediately") - + # Recovery process print("\nRecovering from emergency stop...") time.sleep(1) - + # Clear errors and reset state arm.clean_error() arm.set_state(0) time.sleep(1) - + print("āœ“ Robot recovered and ready") @@ -177,21 +190,21 @@ def main(): print("=" * 60) print("XArm5 Safety Features Demo") print("=" * 60) - + print("\nWorkspace Limits:") for key, value in WORKSPACE_LIMITS.items(): print(f" {key}: {value} mm") - + # Connect to xArm5 print(f"\nConnecting to xArm5 at {XARM_IP}...") arm = XArmAPI(XARM_IP) - + if not arm.connected: print("āŒ Failed to connect to xArm5") return - + print("āœ“ Connected successfully") - + try: # Enable motion print("\nEnabling motion...") @@ -199,36 +212,36 @@ def main(): arm.set_mode(0) arm.set_state(0) time.sleep(1) - + # Move to starting position print("\nMoving to starting position...") arm.move_gohome(wait=True) print("āœ“ At starting position") - + # Run safety demonstrations demo_error_handling(arm) demo_collision_sensitivity(arm) demo_safe_movement(arm) demo_emergency_stop(arm) - + # Return to home print("\nReturning to home position...") arm.move_gohome(wait=True) print("āœ“ Returned to home") - + print("\n" + "=" * 60) print("Safety demo completed successfully!") print("=" * 60) - + except KeyboardInterrupt: print("\n\nāš ļø Interrupted by user - stopping robot...") arm.emergency_stop() - + except Exception as e: print(f"\nāŒ Error occurred: {e}") print("Attempting emergency stop...") arm.emergency_stop() - + finally: # Clean up print("\nDisconnecting...") diff --git a/src/ac_training_lab/xarm5/_scripts/track_integration.py b/src/ac_training_lab/xarm5/_scripts/track_integration.py index 1dc0c625..d7cbba37 100644 --- a/src/ac_training_lab/xarm5/_scripts/track_integration.py +++ b/src/ac_training_lab/xarm5/_scripts/track_integration.py @@ -4,9 +4,10 @@ for extended workspace and coordinated movements. """ -from xarm.wrapper import XArmAPI import time +from xarm.wrapper import XArmAPI + # Configuration XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address TRACK_ENABLED = True # Set to False if no track is attached @@ -17,7 +18,7 @@ def main(): print("=" * 60) print("XArm5 Linear Track Integration Demo") print("=" * 60) - + # Connect to xArm5 with track enabled print(f"\nConnecting to xArm5 at {XARM_IP}...") if TRACK_ENABLED: @@ -26,14 +27,14 @@ def main(): else: print("Track mode: DISABLED") arm = XArmAPI(XARM_IP) - + if not arm.connected: print("āŒ Failed to connect to xArm5") return - + print("āœ“ Connected successfully") print(f" Robot version: {arm.version}") - + try: # Enable motion print("\nEnabling motion...") @@ -41,151 +42,136 @@ def main(): arm.set_mode(0) arm.set_state(0) time.sleep(1) - + if TRACK_ENABLED: # Linear track operations print("\n" + "-" * 60) print("Linear Track Control") print("-" * 60) - + # Enable linear motor print("\nEnabling linear motor (track)...") arm.set_linear_motor_enable(True) time.sleep(1) - + # Get track status ret = arm.get_linear_motor_status() print(f"Track status: {ret}") - + # Get current track position ret = arm.get_linear_motor_pos() if ret[0] == 0: current_pos = ret[1] print(f"Current track position: {current_pos} mm") - + # Move track to position 1 (0 mm - home) print("\nMoving track to home position (0 mm)...") arm.set_linear_motor_pos(0, wait=True) print("āœ“ Track at home position") time.sleep(1) - + # Verify position ret = arm.get_linear_motor_pos() if ret[0] == 0: print(f" Confirmed position: {ret[1]} mm") - + # Move track to position 2 (300 mm) print("\nMoving track to position 300 mm...") arm.set_linear_motor_pos(300, wait=True) print("āœ“ Track at 300 mm") time.sleep(1) - + # Verify position ret = arm.get_linear_motor_pos() if ret[0] == 0: print(f" Confirmed position: {ret[1]} mm") - + # Move track to position 3 (600 mm) print("\nMoving track to position 600 mm...") arm.set_linear_motor_pos(600, wait=True) print("āœ“ Track at 600 mm") time.sleep(1) - + # Verify position ret = arm.get_linear_motor_pos() if ret[0] == 0: print(f" Confirmed position: {ret[1]} mm") - + # Coordinated arm and track movement print("\n" + "-" * 60) print("Coordinated Arm + Track Movement") print("-" * 60) - + # Position 1: Track at 0, arm reaching forward print("\nPosition 1: Track home, arm forward...") arm.set_linear_motor_pos(0, wait=True) arm.set_position( - x=400, y=0, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=400, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print("āœ“ Position 1 reached") time.sleep(1) - + # Position 2: Track at 300, arm centered print("\nPosition 2: Track 300mm, arm centered...") arm.set_linear_motor_pos(300, wait=True) arm.set_position( - x=300, y=0, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print("āœ“ Position 2 reached") time.sleep(1) - + # Position 3: Track at 600, arm reaching back print("\nPosition 3: Track 600mm, arm back...") arm.set_linear_motor_pos(600, wait=True) arm.set_position( - x=200, y=0, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=200, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) print("āœ“ Position 3 reached") time.sleep(1) - + # Return track to home print("\nReturning track to home position...") arm.set_linear_motor_pos(0, wait=True) print("āœ“ Track at home") - + else: # Just arm movements without track print("\n(Track disabled - performing arm-only movements)") - + print("\nMoving to position 1...") arm.set_position( - x=300, y=0, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) time.sleep(1) - + print("\nMoving to position 2...") arm.set_position( - x=300, y=100, z=200, - roll=180, pitch=0, yaw=0, - speed=100, - wait=True + x=300, y=100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True ) time.sleep(1) - + # Return arm to home print("\nReturning arm to home position...") arm.move_gohome(wait=True) print("āœ“ Arm at home") - + print("\n" + "=" * 60) print("Track integration demo completed successfully!") print("=" * 60) - + except KeyboardInterrupt: print("\n\nāš ļø Interrupted by user - stopping robot...") arm.emergency_stop() if TRACK_ENABLED: arm.set_linear_motor_stop() - + except Exception as e: print(f"\nāŒ Error occurred: {e}") print("Attempting emergency stop...") arm.emergency_stop() if TRACK_ENABLED: arm.set_linear_motor_stop() - + finally: # Clean up print("\nDisconnecting...") diff --git a/src/ac_training_lab/xarm5/requirements.txt b/src/ac_training_lab/xarm5/requirements.txt index 3e16de63..c6c9a58f 100644 --- a/src/ac_training_lab/xarm5/requirements.txt +++ b/src/ac_training_lab/xarm5/requirements.txt @@ -1,7 +1,7 @@ # XArm5 Raspberry Pi Control Requirements -# Official UFACTORY xArm Python SDK -xarm-python-sdk>=1.17.0 # Core dependencies (usually included with SDK) numpy>=1.20.0 +# Official UFACTORY xArm Python SDK +xarm-python-sdk>=1.17.0 From 25ccd346f5acd554a9f481ea8d06883f98234a72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 03:19:40 +0000 Subject: [PATCH 4/4] Revert extensive xArm5 module, replace with minimal starter example Co-authored-by: sgbaird <45469701+sgbaird@users.noreply.github.com> --- CHANGELOG.md | 7 - docs/_static/images/README_xarm5.txt | 12 - docs/index.md | 1 - setup.cfg | 3 - src/ac_training_lab/xarm5/README.md | 290 ------------------ src/ac_training_lab/xarm5/__init__.py | 7 - .../xarm5/_scripts/basic_movement.py | 110 ------- .../xarm5/_scripts/gripper_demo.py | 205 ------------- .../xarm5/_scripts/safety_demo.py | 253 --------------- .../xarm5/_scripts/track_integration.py | 183 ----------- src/ac_training_lab/xarm5/requirements.txt | 7 - src/ac_training_lab/xarm5_minimal_example.py | 39 +++ 12 files changed, 39 insertions(+), 1078 deletions(-) delete mode 100644 docs/_static/images/README_xarm5.txt delete mode 100644 src/ac_training_lab/xarm5/README.md delete mode 100644 src/ac_training_lab/xarm5/__init__.py delete mode 100644 src/ac_training_lab/xarm5/_scripts/basic_movement.py delete mode 100644 src/ac_training_lab/xarm5/_scripts/gripper_demo.py delete mode 100644 src/ac_training_lab/xarm5/_scripts/safety_demo.py delete mode 100644 src/ac_training_lab/xarm5/_scripts/track_integration.py delete mode 100644 src/ac_training_lab/xarm5/requirements.txt create mode 100644 src/ac_training_lab/xarm5_minimal_example.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 241ae4c4..db43d22d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,6 @@ ## [Unreleased] ### Added -- **XArm5 Integration**: Complete module for controlling UFACTORY xArm5 robotic arm from Raspberry Pi 5 (`src/ac_training_lab/xarm5/`) - - Comprehensive README with setup instructions for Raspberry Pi 5 - - Requirements file with xarm-python-sdk dependency - - Example scripts for basic movement, gripper control, linear track integration, and safety features - - Support for rail/lift/arm combo integration with linear track control - - Network configuration guide for xArm5 control box - - Troubleshooting and safety documentation - Support for both `rpicam-vid` (Raspberry Pi OS Trixie) and `libcamera-vid` (Raspberry Pi OS Bookworm) camera commands in `src/ac_training_lab/picam/device.py` to ensure compatibility across different OS versions. - Comprehensive Unit Operations section in `docs/index.md` documenting all available capabilities including dispensing, synthesis, characterization, and robotics operations. - Expanded Training Workflows section in `docs/index.md` with 10 educational workflows including RGB/RYB color matching, titration, yeast growth optimization, vision-enabled 3D printing optimization, microscopy image stitching, and AprilTag robot path planning. diff --git a/docs/_static/images/README_xarm5.txt b/docs/_static/images/README_xarm5.txt deleted file mode 100644 index 8ce43acb..00000000 --- a/docs/_static/images/README_xarm5.txt +++ /dev/null @@ -1,12 +0,0 @@ -Note: An image file "ufactory-xarm5.png" needs to be added to this directory. - -The image should be: -- A photo or render of the UFACTORY xArm5 robotic arm -- Approximately 50px height (width auto-scaled) -- PNG or WebP format -- Can be sourced from: - - UFACTORY official website: https://www.ufactory.cc/product-page/ufactory-xarm-5 - - Product documentation - - Community resources - -Until the image is added, the docs/index.md table entry references this image file. diff --git a/docs/index.md b/docs/index.md index 723a95ee..f28aaa3b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -50,7 +50,6 @@ Here are some of the modules we have procured and are in the process of setting | [Hiwonder ArmPi FPV](https://www.hiwonder.com/products/armpi?_pos=4&_sid=a9741a308&_ss=r) | | 1 | An educational six-axis robotic arm | | | [Hiwonder JetAuto Pro](https://www.hiwonder.com/products/jetauto-pro?variant=40040875229271) | | 1 | An educational six-axis mobile cobot with a 3D depth camera and lidar | | | [MyCobot 280](https://shop.elephantrobotics.com/en-ca/collections/mycobot/products/mycobot-pi-worlds-smallest-and-lightest-six-axis-collaborative-robot) and [MyAGV](https://shop.elephantrobotics.com/en-ca/collections/myagv/products/myagv-2023-pi?variant=47262714069304) | | 1 | An educational six-axis mobile cobot | | -| [UFACTORY xArm5](https://www.ufactory.cc/product-page/ufactory-xarm-5) ([docs](../src/ac_training_lab/xarm5/)) | 🦾 | 1 | A 5-DOF collaborative robotic arm controlled from Raspberry Pi 5. Supports gripper, linear track integration, and network-based control for rail/lift/arm combo systems. | | | [AutoTrickler v4](https://autotrickler.com/pages/autotrickler-v4) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | | [ChargeMaster Supreme](https://www.rcbs.com/priming-and-powder-charging/powder-dispensers-and-scales/chargemaster-supreme-electronic-powder-dispenser/16-98943.html) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | | [Ingenuity Powder System](https://ingenuityprecision.com/product/ingenuity-powder-system/) | | 1 | An automated solid dispensing station, usually marketed for ammunition reloading, but to be used as a general-purpose powder doser | | diff --git a/setup.cfg b/setup.cfg index 46e23589..1e2902a2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -74,9 +74,6 @@ a1-cam = boto3 wget -xarm5 = - xarm-python-sdk>=1.17.0 - [options.entry_points] # Add here console scripts like: # console_scripts = diff --git a/src/ac_training_lab/xarm5/README.md b/src/ac_training_lab/xarm5/README.md deleted file mode 100644 index bfd26bfa..00000000 --- a/src/ac_training_lab/xarm5/README.md +++ /dev/null @@ -1,290 +0,0 @@ -# XArm5 Control for Raspberry Pi 5 - -This module provides integration for controlling the UFACTORY xArm5 robotic arm from a Raspberry Pi 5 as part of the AC Training Lab's rail/lift/arm combo system. - -## Hardware Overview - -The **xArm5** is a 5-degree-of-freedom collaborative robotic arm manufactured by UFACTORY. It features: -- 5 servo joints with precise control -- Network-based control via TCP/IP -- Support for various end effectors (grippers, sensors) -- Compatible with linear tracks and additional accessories -- Payload capacity suitable for laboratory automation tasks - -## Prerequisites - -### Hardware Requirements -- Raspberry Pi 5 (4GB or 8GB RAM recommended) -- UFACTORY xArm5 robotic arm with control box -- Ethernet connection (recommended) or WiFi -- Power supply for both Raspberry Pi and xArm5 -- Optional: Linear track for extended workspace -- Optional: Gripper or end effector - -### Software Requirements -- Raspberry Pi OS (Bookworm or later) -- Python 3.8 or higher -- Network connectivity between Raspberry Pi and xArm5 control box - -## Installation on Raspberry Pi 5 - -### 1. Set Up Raspberry Pi OS - -If starting fresh, use the [Raspberry Pi Imager](https://www.raspberrypi.org/software/) to install Raspberry Pi OS on a microSD card. - -```bash -# Update system packages -sudo apt update -sudo apt upgrade -y -``` - -### 2. Install Python Dependencies - -```bash -# Install Python virtual environment tools -sudo apt install python3-pip python3-venv -y - -# Create a virtual environment (recommended for Bookworm OS) -python3 -m venv ~/xarm-env -source ~/xarm-env/bin/activate -``` - -### 3. Install xArm Python SDK - -**Option A: Install from PyPI (Recommended)** -```bash -pip install xarm-python-sdk -``` - -**Option B: Install from source** -```bash -git clone https://github.com/xArm-Developer/xArm-Python-SDK.git -cd xArm-Python-SDK -pip install . -``` - -### 4. Install AC Training Lab Package (Optional) - -To use this module as part of the ac-training-lab package: -```bash -# Clone the ac-training-lab repository -git clone https://github.com/AccelerationConsortium/ac-dev-lab.git -cd ac-dev-lab - -# Install with xarm5 dependencies -pip install -e . -``` - -## Network Configuration - -### Finding Your xArm5 IP Address - -1. Check the xArm5 control box display panel -2. Or check your router's DHCP client list -3. Or use network scanning tools: - ```bash - sudo apt install nmap - nmap -sn 192.168.1.0/24 # Adjust subnet to match your network - ``` - -### Static IP Configuration (Recommended) - -For reliable automation, configure a static IP for the xArm5 control box: -1. Access the control box web interface -2. Navigate to Network Settings -3. Set a static IP address (e.g., 192.168.1.100) -4. Configure gateway and DNS if needed - -## Quick Start Examples - -### Basic Connection Test - -```python -from xarm.wrapper import XArmAPI - -# Replace with your xArm5 control box IP -arm = XArmAPI('192.168.1.100') - -# Check connection and get robot info -print(f"Connected: {arm.connected}") -print(f"Version: {arm.version}") -print(f"State: {arm.state}") - -# Disconnect -arm.disconnect() -``` - -### Simple Movement Example - -```python -from xarm.wrapper import XArmAPI - -# Connect to xArm5 -arm = XArmAPI('192.168.1.100') - -# Enable motion -arm.motion_enable(enable=True) -arm.set_mode(0) # Position control mode -arm.set_state(0) # Ready state - -# Move to home position -arm.move_gohome(wait=True) - -# Move to a specific position (x, y, z in mm, angles in degrees) -arm.set_position(x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True) - -# Get current position -position = arm.get_position() -print(f"Current position: {position}") - -# Disconnect -arm.disconnect() -``` - -### Using with Gripper - -```python -from xarm.wrapper import XArmAPI - -# Connect -arm = XArmAPI('192.168.1.100') - -# Enable gripper -arm.set_gripper_enable(True) -arm.set_gripper_mode(0) - -# Open gripper -arm.set_gripper_position(800, wait=True) # 800 = fully open - -# Close gripper -arm.set_gripper_position(0, wait=True) # 0 = fully closed - -# Disconnect -arm.disconnect() -``` - -## Rail/Lift/Arm Integration - -For integration with linear tracks and vertical lifts: - -### Linear Track Control - -```python -from xarm.wrapper import XArmAPI - -arm = XArmAPI('192.168.1.100', enable_track=True) - -# Enable linear motor (track) -arm.set_linear_motor_enable(True) - -# Move track to specific position (in mm) -arm.set_linear_motor_pos(500, wait=True) - -# Get track position -track_pos = arm.get_linear_motor_pos() -print(f"Track position: {track_pos} mm") -``` - -## Example Scripts - -See the `_scripts/` directory for more examples: -- `basic_movement.py` - Basic arm movements -- `gripper_demo.py` - Gripper control demonstrations -- `track_integration.py` - Linear track coordination -- `safety_demo.py` - Emergency stop and safety features - -## Safety Considerations - -āš ļø **IMPORTANT SAFETY WARNINGS:** - -1. **Keep clear of the robot workspace** during operation -2. **Test in simulation first** before running on real hardware -3. **Use emergency stop** features in your code -4. **Start with slow speeds** when testing new movements -5. **Monitor the robot** during initial testing -6. **Set appropriate workspace limits** to prevent collisions -7. **Enable collision detection** when available - -### Emergency Stop - -```python -# In case of emergency -arm.emergency_stop() - -# Reset after emergency stop -arm.clean_error() -arm.set_state(0) -``` - -## Troubleshooting - -### Connection Issues - -**Problem:** Cannot connect to xArm5 -- Verify both devices are on the same network -- Check IP address is correct -- Ping the control box: `ping 192.168.1.100` -- Check firewall settings on Raspberry Pi -- Ensure control box is powered on and initialized - -**Problem:** Connection drops frequently -- Use wired Ethernet instead of WiFi -- Check network cable quality -- Configure static IP addresses -- Reduce network traffic on the subnet - -### Movement Issues - -**Problem:** Robot doesn't move -- Check if motion is enabled: `arm.motion_enable(True)` -- Verify robot state: `arm.get_state()` -- Clear any errors: `arm.clean_error()` -- Check if servo is attached: `arm.set_servo_attach()` - -**Problem:** Jerky or unexpected movements -- Reduce speed and acceleration parameters -- Check for network latency -- Use `wait=True` for sequential movements -- Verify trajectory planning settings - -### Python Environment Issues - -**Problem:** Module not found errors -- Ensure virtual environment is activated -- Reinstall xarm-python-sdk: `pip install --upgrade xarm-python-sdk` -- Check Python version compatibility (3.8+) - -## Additional Resources - -### Official Documentation -- [xArm Python SDK GitHub](https://github.com/xArm-Developer/xArm-Python-SDK) -- [xArm API Documentation](https://github.com/xArm-Developer/xArm-Python-SDK/blob/master/doc/api/xarm_api.md) -- [UFACTORY Official Website](https://www.ufactory.cc/) - -### Community Resources -- [AccelerationConsortium xarm-translocation](https://github.com/AccelerationConsortium/xarm-translocation) - Advanced control package -- [xArm ROS Integration](https://github.com/xArm-Developer/xarm_ros) - For ROS users -- [xArm ROS2 Integration](https://github.com/xArm-Developer/xarm_ros2) - For ROS2 users - -### AC Training Lab Resources -- [AC Training Lab Documentation](https://ac-training-lab.readthedocs.io/) -- [AC Training Lab GitHub](https://github.com/AccelerationConsortium/ac-training-lab) -- [Discussions](https://github.com/AccelerationConsortium/ac-training-lab/discussions) - -## Contributing - -To contribute improvements or report issues: -1. Open an issue on the [AC Training Lab GitHub](https://github.com/AccelerationConsortium/ac-training-lab/issues) -2. Join the discussion at [AC Discussions](https://github.com/AccelerationConsortium/ac-training-lab/discussions) -3. Submit a pull request with your changes - -## License - -This module is part of the ac-training-lab project and follows the same MIT License. - -## Support - -For support with: -- **Hardware issues**: Contact UFACTORY support -- **Software/integration issues**: Open an issue on GitHub -- **General questions**: Join our discussions or reach out to sterling.baird@utoronto.ca diff --git a/src/ac_training_lab/xarm5/__init__.py b/src/ac_training_lab/xarm5/__init__.py deleted file mode 100644 index f380c29d..00000000 --- a/src/ac_training_lab/xarm5/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""XArm5 control module for Raspberry Pi 5. - -This module provides integration for controlling the UFACTORY xArm5 robotic arm -from a Raspberry Pi 5 as part of the AC Training Lab's rail/lift/arm combo system. -""" - -__version__ = "0.1.0" diff --git a/src/ac_training_lab/xarm5/_scripts/basic_movement.py b/src/ac_training_lab/xarm5/_scripts/basic_movement.py deleted file mode 100644 index 13920867..00000000 --- a/src/ac_training_lab/xarm5/_scripts/basic_movement.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Basic movement example for xArm5. - -This script demonstrates basic movement commands for the xArm5 robotic arm. -Includes connection, enabling motion, moving to positions, and safe shutdown. -""" - -import time - -from xarm.wrapper import XArmAPI - -# Configuration -XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address - - -def main(): - """Execute basic movement demonstration.""" - print("=" * 60) - print("XArm5 Basic Movement Demo") - print("=" * 60) - - # Connect to xArm5 - print(f"\nConnecting to xArm5 at {XARM_IP}...") - arm = XArmAPI(XARM_IP) - - if not arm.connected: - print("āŒ Failed to connect to xArm5") - return - - print("āœ“ Connected successfully") - print(f" Robot version: {arm.version}") - print(f" Robot state: {arm.state}") - - try: - # Enable motion - print("\nEnabling motion...") - arm.motion_enable(enable=True) - arm.set_mode(0) # Position control mode - arm.set_state(0) # Ready state - time.sleep(1) - - # Move to home position - print("\nMoving to home position...") - arm.move_gohome(wait=True) - print("āœ“ Reached home position") - time.sleep(1) - - # Get current position - position = arm.get_position() - print("\nCurrent position:") - print(f" X: {position[1][0]:.2f} mm") - print(f" Y: {position[1][1]:.2f} mm") - print(f" Z: {position[1][2]:.2f} mm") - print(f" Roll: {position[1][3]:.2f}°") - print(f" Pitch: {position[1][4]:.2f}°") - print(f" Yaw: {position[1][5]:.2f}°") - - # Movement sequence - print("\nExecuting movement sequence...") - - # Position 1 - print("\n Moving to position 1 (forward)...") - arm.set_position( - x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print(" āœ“ Position 1 reached") - time.sleep(1) - - # Position 2 - print("\n Moving to position 2 (left)...") - arm.set_position( - x=300, y=100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print(" āœ“ Position 2 reached") - time.sleep(1) - - # Position 3 - print("\n Moving to position 3 (right)...") - arm.set_position( - x=300, y=-100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print(" āœ“ Position 3 reached") - time.sleep(1) - - # Return to home - print("\nReturning to home position...") - arm.move_gohome(wait=True) - print("āœ“ Returned to home") - - print("\n" + "=" * 60) - print("Movement sequence completed successfully!") - print("=" * 60) - - except KeyboardInterrupt: - print("\n\nāš ļø Interrupted by user - stopping robot...") - arm.emergency_stop() - - except Exception as e: - print(f"\nāŒ Error occurred: {e}") - print("Attempting emergency stop...") - arm.emergency_stop() - - finally: - # Clean up - print("\nDisconnecting...") - arm.disconnect() - print("āœ“ Disconnected safely") - - -if __name__ == "__main__": - main() diff --git a/src/ac_training_lab/xarm5/_scripts/gripper_demo.py b/src/ac_training_lab/xarm5/_scripts/gripper_demo.py deleted file mode 100644 index 601cfc2e..00000000 --- a/src/ac_training_lab/xarm5/_scripts/gripper_demo.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Gripper control demonstration for xArm5. - -This script demonstrates how to control various gripper types with the xArm5. -Supports standard gripper, BIO gripper, and RobotIQ gripper. -""" - -import time - -from xarm.wrapper import XArmAPI - -# Configuration -XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address -GRIPPER_TYPE = "standard" # Options: "standard", "bio", "robotiq" - - -def demo_standard_gripper(arm): - """Demonstrate standard xArm gripper.""" - print("\n" + "-" * 60) - print("Standard Gripper Demo") - print("-" * 60) - - # Enable gripper - print("\nEnabling gripper...") - arm.set_gripper_enable(True) - arm.set_gripper_mode(0) - time.sleep(1) - - # Get current gripper position - ret = arm.get_gripper_position() - if ret[0] == 0: - print(f"Current gripper position: {ret[1]} (0=closed, 800=open)") - - # Open gripper - print("\nOpening gripper...") - arm.set_gripper_position(800, wait=True) - print("āœ“ Gripper opened") - time.sleep(1) - - # Partially close - print("\nPartially closing gripper (400/800)...") - arm.set_gripper_position(400, wait=True) - print("āœ“ Gripper at 50% position") - time.sleep(1) - - # Close gripper - print("\nClosing gripper...") - arm.set_gripper_position(0, wait=True) - print("āœ“ Gripper closed") - time.sleep(1) - - # Open again - print("\nOpening gripper again...") - arm.set_gripper_position(800, wait=True) - print("āœ“ Gripper opened") - - -def demo_bio_gripper(arm): - """Demonstrate BIO gripper.""" - print("\n" + "-" * 60) - print("BIO Gripper Demo") - print("-" * 60) - - # Enable BIO gripper - print("\nEnabling BIO gripper...") - arm.set_bio_gripper_enable(True) - time.sleep(1) - - # Set speed - arm.set_bio_gripper_speed(500) - - # Get status - ret = arm.get_bio_gripper_status() - if ret[0] == 0: - print(f"BIO Gripper status: {ret[1]}") - - # Open gripper - print("\nOpening BIO gripper...") - arm.open_bio_gripper(wait=True) - print("āœ“ BIO gripper opened") - time.sleep(1) - - # Close gripper - print("\nClosing BIO gripper...") - arm.close_bio_gripper(wait=True) - print("āœ“ BIO gripper closed") - time.sleep(1) - - # Open again - print("\nOpening BIO gripper again...") - arm.open_bio_gripper(wait=True) - print("āœ“ BIO gripper opened") - - -def demo_robotiq_gripper(arm): - """Demonstrate RobotIQ gripper.""" - print("\n" + "-" * 60) - print("RobotIQ Gripper Demo") - print("-" * 60) - - # Reset and activate - print("\nInitializing RobotIQ gripper...") - arm.robotiq_reset() - time.sleep(2) - arm.robotiq_set_activate(True) - time.sleep(2) - - # Get status - ret = arm.robotiq_get_status() - if ret[0] == 0: - print(f"RobotIQ status: {ret[1]}") - - # Open gripper - print("\nOpening RobotIQ gripper...") - arm.robotiq_open(wait=True) - print("āœ“ RobotIQ gripper opened") - time.sleep(1) - - # Set specific position - print("\nSetting RobotIQ gripper to 50% (128/255)...") - arm.robotiq_set_position(128, wait=True) - print("āœ“ RobotIQ gripper at 50% position") - time.sleep(1) - - # Close gripper - print("\nClosing RobotIQ gripper...") - arm.robotiq_close(wait=True) - print("āœ“ RobotIQ gripper closed") - time.sleep(1) - - # Open again - print("\nOpening RobotIQ gripper again...") - arm.robotiq_open(wait=True) - print("āœ“ RobotIQ gripper opened") - - -def main(): - """Execute gripper demonstration.""" - print("=" * 60) - print("XArm5 Gripper Control Demo") - print(f"Gripper Type: {GRIPPER_TYPE.upper()}") - print("=" * 60) - - # Connect to xArm5 - print(f"\nConnecting to xArm5 at {XARM_IP}...") - arm = XArmAPI(XARM_IP) - - if not arm.connected: - print("āŒ Failed to connect to xArm5") - return - - print("āœ“ Connected successfully") - - try: - # Enable motion - print("\nEnabling motion...") - arm.motion_enable(enable=True) - arm.set_mode(0) - arm.set_state(0) - time.sleep(1) - - # Move to working position - print("\nMoving to working position...") - arm.set_position( - x=300, y=0, z=300, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print("āœ“ In working position") - - # Execute gripper demo based on type - if GRIPPER_TYPE.lower() == "standard": - demo_standard_gripper(arm) - elif GRIPPER_TYPE.lower() == "bio": - demo_bio_gripper(arm) - elif GRIPPER_TYPE.lower() == "robotiq": - demo_robotiq_gripper(arm) - else: - print(f"\nāŒ Unknown gripper type: {GRIPPER_TYPE}") - print(" Valid options: standard, bio, robotiq") - - # Return to home - print("\nReturning to home position...") - arm.move_gohome(wait=True) - print("āœ“ Returned to home") - - print("\n" + "=" * 60) - print("Gripper demo completed successfully!") - print("=" * 60) - - except KeyboardInterrupt: - print("\n\nāš ļø Interrupted by user - stopping robot...") - arm.emergency_stop() - - except Exception as e: - print(f"\nāŒ Error occurred: {e}") - print("Attempting emergency stop...") - arm.emergency_stop() - - finally: - # Clean up - print("\nDisconnecting...") - arm.disconnect() - print("āœ“ Disconnected safely") - - -if __name__ == "__main__": - main() diff --git a/src/ac_training_lab/xarm5/_scripts/safety_demo.py b/src/ac_training_lab/xarm5/_scripts/safety_demo.py deleted file mode 100644 index 89f8159e..00000000 --- a/src/ac_training_lab/xarm5/_scripts/safety_demo.py +++ /dev/null @@ -1,253 +0,0 @@ -"""Safety features demonstration for xArm5. - -This script demonstrates safety features including emergency stop, -error handling, collision detection, and safe workspace limits. -""" - -import time - -from xarm.wrapper import XArmAPI - -# Configuration -XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address - -# Workspace limits (in mm) -WORKSPACE_LIMITS = { - "x_min": 100, - "x_max": 500, - "y_min": -300, - "y_max": 300, - "z_min": 50, - "z_max": 500, -} - - -def check_workspace_limits(position): - """Check if position is within safe workspace limits. - - Args: - position: Tuple of (x, y, z) coordinates in mm - - Returns: - bool: True if position is safe, False otherwise - """ - x, y, z = position - - if not (WORKSPACE_LIMITS["x_min"] <= x <= WORKSPACE_LIMITS["x_max"]): - x_min = WORKSPACE_LIMITS["x_min"] - x_max = WORKSPACE_LIMITS["x_max"] - print(f"āš ļø X position {x} outside limits [{x_min}, {x_max}]") - return False - - if not (WORKSPACE_LIMITS["y_min"] <= y <= WORKSPACE_LIMITS["y_max"]): - y_min = WORKSPACE_LIMITS["y_min"] - y_max = WORKSPACE_LIMITS["y_max"] - print(f"āš ļø Y position {y} outside limits [{y_min}, {y_max}]") - return False - - if not (WORKSPACE_LIMITS["z_min"] <= z <= WORKSPACE_LIMITS["z_max"]): - z_min = WORKSPACE_LIMITS["z_min"] - z_max = WORKSPACE_LIMITS["z_max"] - print(f"āš ļø Z position {z} outside limits [{z_min}, {z_max}]") - return False - - return True - - -def demo_safe_movement(arm): - """Demonstrate safe movement with workspace limits.""" - print("\n" + "-" * 60) - print("Safe Movement Demo") - print("-" * 60) - - # Test safe position - safe_position = (300, 0, 200) - print(f"\nTesting safe position {safe_position}...") - if check_workspace_limits(safe_position): - print("āœ“ Position is safe - executing movement") - arm.set_position( - x=safe_position[0], - y=safe_position[1], - z=safe_position[2], - roll=180, - pitch=0, - yaw=0, - speed=100, - wait=True, - ) - print("āœ“ Movement completed") - time.sleep(1) - else: - print("āŒ Position rejected - outside workspace limits") - - # Test unsafe position (too far) - unsafe_position = (700, 0, 200) - print(f"\nTesting unsafe position {unsafe_position}...") - if check_workspace_limits(unsafe_position): - print("āœ“ Position is safe - would execute movement") - else: - print("āŒ Position rejected - outside workspace limits") - print(" Movement NOT executed for safety") - - -def demo_error_handling(arm): - """Demonstrate error detection and recovery.""" - print("\n" + "-" * 60) - print("Error Handling Demo") - print("-" * 60) - - # Check for errors - err_code = arm.error_code - warn_code = arm.warn_code - - print(f"\nCurrent error code: {err_code}") - print(f"Current warning code: {warn_code}") - - if arm.has_error: - print("āš ļø Robot has errors - attempting to clear...") - arm.clean_error() - time.sleep(1) - - if arm.has_error: - print("āŒ Failed to clear errors") - else: - print("āœ“ Errors cleared successfully") - else: - print("āœ“ No errors detected") - - if arm.has_warn: - print("āš ļø Robot has warnings - attempting to clear...") - arm.clean_warn() - time.sleep(1) - - if arm.has_warn: - print("āŒ Failed to clear warnings") - else: - print("āœ“ Warnings cleared successfully") - else: - print("āœ“ No warnings detected") - - -def demo_collision_sensitivity(arm): - """Demonstrate collision sensitivity settings.""" - print("\n" + "-" * 60) - print("Collision Sensitivity Demo") - print("-" * 60) - - # Set collision sensitivity (0-5, where 0 is least sensitive) - sensitivity = 3 - print(f"\nSetting collision sensitivity to {sensitivity}...") - arm.set_collision_sensitivity(sensitivity) - time.sleep(0.5) - - print(f"āœ“ Collision sensitivity set to {arm.collision_sensitivity}") - print(" Note: Higher values = more sensitive collision detection") - - -def demo_emergency_stop(arm): - """Demonstrate emergency stop functionality.""" - print("\n" + "-" * 60) - print("Emergency Stop Demo") - print("-" * 60) - - print("\nStarting a slow movement...") - print("(In a real emergency, you would call arm.emergency_stop())") - - # Start a movement - arm.set_position( - x=350, - y=0, - z=200, - roll=180, - pitch=0, - yaw=0, - speed=50, # Slow speed for demonstration - wait=False, # Don't wait for completion - ) - - # Simulate waiting a bit - time.sleep(0.5) - - # Emergency stop - print("\nāš ļø EMERGENCY STOP triggered!") - arm.emergency_stop() - print("āœ“ Robot stopped immediately") - - # Recovery process - print("\nRecovering from emergency stop...") - time.sleep(1) - - # Clear errors and reset state - arm.clean_error() - arm.set_state(0) - time.sleep(1) - - print("āœ“ Robot recovered and ready") - - -def main(): - """Execute safety features demonstration.""" - print("=" * 60) - print("XArm5 Safety Features Demo") - print("=" * 60) - - print("\nWorkspace Limits:") - for key, value in WORKSPACE_LIMITS.items(): - print(f" {key}: {value} mm") - - # Connect to xArm5 - print(f"\nConnecting to xArm5 at {XARM_IP}...") - arm = XArmAPI(XARM_IP) - - if not arm.connected: - print("āŒ Failed to connect to xArm5") - return - - print("āœ“ Connected successfully") - - try: - # Enable motion - print("\nEnabling motion...") - arm.motion_enable(enable=True) - arm.set_mode(0) - arm.set_state(0) - time.sleep(1) - - # Move to starting position - print("\nMoving to starting position...") - arm.move_gohome(wait=True) - print("āœ“ At starting position") - - # Run safety demonstrations - demo_error_handling(arm) - demo_collision_sensitivity(arm) - demo_safe_movement(arm) - demo_emergency_stop(arm) - - # Return to home - print("\nReturning to home position...") - arm.move_gohome(wait=True) - print("āœ“ Returned to home") - - print("\n" + "=" * 60) - print("Safety demo completed successfully!") - print("=" * 60) - - except KeyboardInterrupt: - print("\n\nāš ļø Interrupted by user - stopping robot...") - arm.emergency_stop() - - except Exception as e: - print(f"\nāŒ Error occurred: {e}") - print("Attempting emergency stop...") - arm.emergency_stop() - - finally: - # Clean up - print("\nDisconnecting...") - arm.disconnect() - print("āœ“ Disconnected safely") - - -if __name__ == "__main__": - main() diff --git a/src/ac_training_lab/xarm5/_scripts/track_integration.py b/src/ac_training_lab/xarm5/_scripts/track_integration.py deleted file mode 100644 index d7cbba37..00000000 --- a/src/ac_training_lab/xarm5/_scripts/track_integration.py +++ /dev/null @@ -1,183 +0,0 @@ -"""Linear track integration demonstration for xArm5. - -This script demonstrates how to control the xArm5 with a linear track (rail) -for extended workspace and coordinated movements. -""" - -import time - -from xarm.wrapper import XArmAPI - -# Configuration -XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address -TRACK_ENABLED = True # Set to False if no track is attached - - -def main(): - """Execute linear track integration demonstration.""" - print("=" * 60) - print("XArm5 Linear Track Integration Demo") - print("=" * 60) - - # Connect to xArm5 with track enabled - print(f"\nConnecting to xArm5 at {XARM_IP}...") - if TRACK_ENABLED: - print("Track mode: ENABLED") - arm = XArmAPI(XARM_IP, enable_track=True) - else: - print("Track mode: DISABLED") - arm = XArmAPI(XARM_IP) - - if not arm.connected: - print("āŒ Failed to connect to xArm5") - return - - print("āœ“ Connected successfully") - print(f" Robot version: {arm.version}") - - try: - # Enable motion - print("\nEnabling motion...") - arm.motion_enable(enable=True) - arm.set_mode(0) - arm.set_state(0) - time.sleep(1) - - if TRACK_ENABLED: - # Linear track operations - print("\n" + "-" * 60) - print("Linear Track Control") - print("-" * 60) - - # Enable linear motor - print("\nEnabling linear motor (track)...") - arm.set_linear_motor_enable(True) - time.sleep(1) - - # Get track status - ret = arm.get_linear_motor_status() - print(f"Track status: {ret}") - - # Get current track position - ret = arm.get_linear_motor_pos() - if ret[0] == 0: - current_pos = ret[1] - print(f"Current track position: {current_pos} mm") - - # Move track to position 1 (0 mm - home) - print("\nMoving track to home position (0 mm)...") - arm.set_linear_motor_pos(0, wait=True) - print("āœ“ Track at home position") - time.sleep(1) - - # Verify position - ret = arm.get_linear_motor_pos() - if ret[0] == 0: - print(f" Confirmed position: {ret[1]} mm") - - # Move track to position 2 (300 mm) - print("\nMoving track to position 300 mm...") - arm.set_linear_motor_pos(300, wait=True) - print("āœ“ Track at 300 mm") - time.sleep(1) - - # Verify position - ret = arm.get_linear_motor_pos() - if ret[0] == 0: - print(f" Confirmed position: {ret[1]} mm") - - # Move track to position 3 (600 mm) - print("\nMoving track to position 600 mm...") - arm.set_linear_motor_pos(600, wait=True) - print("āœ“ Track at 600 mm") - time.sleep(1) - - # Verify position - ret = arm.get_linear_motor_pos() - if ret[0] == 0: - print(f" Confirmed position: {ret[1]} mm") - - # Coordinated arm and track movement - print("\n" + "-" * 60) - print("Coordinated Arm + Track Movement") - print("-" * 60) - - # Position 1: Track at 0, arm reaching forward - print("\nPosition 1: Track home, arm forward...") - arm.set_linear_motor_pos(0, wait=True) - arm.set_position( - x=400, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print("āœ“ Position 1 reached") - time.sleep(1) - - # Position 2: Track at 300, arm centered - print("\nPosition 2: Track 300mm, arm centered...") - arm.set_linear_motor_pos(300, wait=True) - arm.set_position( - x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print("āœ“ Position 2 reached") - time.sleep(1) - - # Position 3: Track at 600, arm reaching back - print("\nPosition 3: Track 600mm, arm back...") - arm.set_linear_motor_pos(600, wait=True) - arm.set_position( - x=200, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - print("āœ“ Position 3 reached") - time.sleep(1) - - # Return track to home - print("\nReturning track to home position...") - arm.set_linear_motor_pos(0, wait=True) - print("āœ“ Track at home") - - else: - # Just arm movements without track - print("\n(Track disabled - performing arm-only movements)") - - print("\nMoving to position 1...") - arm.set_position( - x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - time.sleep(1) - - print("\nMoving to position 2...") - arm.set_position( - x=300, y=100, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True - ) - time.sleep(1) - - # Return arm to home - print("\nReturning arm to home position...") - arm.move_gohome(wait=True) - print("āœ“ Arm at home") - - print("\n" + "=" * 60) - print("Track integration demo completed successfully!") - print("=" * 60) - - except KeyboardInterrupt: - print("\n\nāš ļø Interrupted by user - stopping robot...") - arm.emergency_stop() - if TRACK_ENABLED: - arm.set_linear_motor_stop() - - except Exception as e: - print(f"\nāŒ Error occurred: {e}") - print("Attempting emergency stop...") - arm.emergency_stop() - if TRACK_ENABLED: - arm.set_linear_motor_stop() - - finally: - # Clean up - print("\nDisconnecting...") - arm.disconnect() - print("āœ“ Disconnected safely") - - -if __name__ == "__main__": - main() diff --git a/src/ac_training_lab/xarm5/requirements.txt b/src/ac_training_lab/xarm5/requirements.txt deleted file mode 100644 index c6c9a58f..00000000 --- a/src/ac_training_lab/xarm5/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# XArm5 Raspberry Pi Control Requirements - - -# Core dependencies (usually included with SDK) -numpy>=1.20.0 -# Official UFACTORY xArm Python SDK -xarm-python-sdk>=1.17.0 diff --git a/src/ac_training_lab/xarm5_minimal_example.py b/src/ac_training_lab/xarm5_minimal_example.py new file mode 100644 index 00000000..f0d03e9a --- /dev/null +++ b/src/ac_training_lab/xarm5_minimal_example.py @@ -0,0 +1,39 @@ +"""Minimal example for controlling UFACTORY xArm5 from Raspberry Pi 5. + +This script demonstrates basic programmatic control of the xArm5 robotic arm. + +Installation: + pip install xarm-python-sdk + +Usage: + python xarm5_minimal_example.py +""" + +from xarm.wrapper import XArmAPI + +# Configuration +XARM_IP = "192.168.1.100" # Replace with your xArm5 IP address + + +def main(): + """Minimal example of xArm5 control.""" + # Connect to xArm5 + arm = XArmAPI(XARM_IP) + + # Enable motion + arm.motion_enable(enable=True) + arm.set_mode(0) # Position control mode + arm.set_state(0) # Ready state + + # Move to home position + arm.move_gohome(wait=True) + + # Move to a position (x, y, z in mm, angles in degrees) + arm.set_position(x=300, y=0, z=200, roll=180, pitch=0, yaw=0, speed=100, wait=True) + + # Disconnect + arm.disconnect() + + +if __name__ == "__main__": + main()