basic implementation of uFactory xArm - #965
Merged
rickwierenga merged 10 commits intoApr 13, 2026
Merged
Conversation
Introduces a parallel arm hierarchy for 6-axis articulated robots alongside the existing SCARA classes. Includes SixAxisBackend abstract base, SixAxisArm frontend, XArm6Backend wrapping the xArm Python SDK, vertical and horizontal access pattern sequences, freedrive mode, error recovery, and an interactive pick-and-place walkthrough script with JSON position persistence. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add speed, mvacc, joint_speed, joint_mvacc properties to XArm6Backend - Change gripper interface to use target position parameter instead of binary open/close, using set_gripper_position from the SDK - Add gripper_open_pos and gripper_close_pos constructor params for pick/place sequence defaults - Enable gripper during setup (set_gripper_mode, set_gripper_enable) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused `patch` import in xarm6_backend_tests.py - Sort imports alphabetically (HorizontalAccess before VerticalAccess) - Type `_arm` as `Any` to fix mypy attr-defined errors (xarm SDK has no stubs) - Add type: ignore comments for untyped xarm import and test mocking - Apply ruff formatting to all 6 files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use import-not-found instead of import-untyped for xarm SDK ignore - Add from __future__ import annotations to example_pick_place.py for X | Y union syntax compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drops the generic SixAxisArm frontend and SixAxisBackend base class; XArm6Backend is now a standalone class. Relocates xarm6 to pylabrobot/ufactory/xarm6 to match the existing top-level vendor layout. Renames XArm6Axis enum members to semantic names (BASE_ROTATION, etc.). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Splits the old monolithic XArm6Backend into an XArm6Driver (SDK lifecycle, retry-aware _call_sdk) and an XArm6ArmBackend implementing ArticulatedGripperArmBackend + HasJoints + CanFreedrive. Motion profile defaults live on per-call CartesianMoveParams/JointMoveParams dataclasses. Adds the xarm optional dependency group. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Walks through setup, gripper control, Cartesian and joint movement, position queries, pick/place, freedrive teaching, and park/halt/clear_errors using the new ArticulatedArm capability API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Member
|
hey Rob, i moved it over to the v1b1 architecture and ported the example to a notebook in |
Apply ruff format to driver/backend/tests and silence a mypy no-any-return on the xarm SDK mock helper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set gripper mode 1 (position/speed/force) instead of mode 0 (open/close), which is required for set_gripper_position to work - Re-assert set_mode(0) + set_state(0) after gripper init to ensure arm is in position control mode (gripper mode change can reset arm state) - Fix gripper unit conversion: account for 71-150mm physical range with proper linear mapping instead of assuming 0-based 0.1mm/unit - Fix notebook: use keyword args for pick_up_at_location rotation parameter, remove invalid resource_width from drop_at_location, update gripper widths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Member
|
thanks @Robert-Keyser-Calico ! |
Contributor
Author
|
A couple of comments that have been in the back of my head:
|
Member
|
Member
|
does the arm have something like a for sensing mode / plate detection? where if you close the gripper it hits resistance and raises an error if no resistance is hit? similar to iswap |
Contributor
Author
|
I'll have someone double check this to confirm. The SDK says yes:
def close_and_verify_grip(arm, speed=0):
"""
Closes the Bio Gripper G2 and verifies if the microtiter plate is
present.
Throws a ValueError if the grippers close but do not grip the object.
:param arm: An instantiated and connected XArmAPI object
:param speed: Gripper closing speed (0 uses default)
"""
# 1. Close the bio gripper
# wait=True blocks the script until the gripper stops moving
print("Closing Bio Gripper G2...")
code = arm.close_bio_gripper(speed=speed, wait=True, timeout=5)
if code != 0:
raise RuntimeError(f"Failed to execute close_bio_gripper. API
Return Code: {code}")
# 2. Check the gripper's internal status
code, status = arm.get_bio_gripper_status()
if code != 0:
raise RuntimeError(f"Failed to read bio gripper status. API Return
Code: {code}")
# UFACTORY Gripper Status mappings:
# 0: Stopped / Idle (Reached 71mm limit without catching anything)
# 1: Moving (Should not happen if wait=True)
# 2: Caught (Encountered object resistance at ~85mm or ~127mm)
# 3: Dropped / Slipped (Object was caught but then fell out)
if status == 0:
raise ValueError("Grip failed: The gripper closed fully to 71mm
without detecting a microtiter plate.")
elif status == 3:
raise ValueError("Grip failed: A microtiter plate was detected but
slipped out.")
elif status == 1:
raise TimeoutError("Grip timeout: The gripper is still moving.")
elif status == 2:
print("Success: Microtiter plate is securely gripped.")
else:
print(f"Warning: Gripper returned an undocumented status code:
{status}")
# 3. Double-check for hardware slip errors (Optional but recommended)
# Error code 12 specifically indicates: "The object slipped from the
BIO Gripper"
code, err = arm.get_bio_gripper_error()
if err == 12:
# Clear the error state so the gripper can be used again
arm.clean_bio_gripper_error()
raise ValueError("Hardware Error 12: The microtiter plate slipped
from the BIO Gripper.")
return True
…On Sat, May 16, 2026 at 9:27 PM Rick Wierenga ***@***.***> wrote:
*rickwierenga* left a comment (PyLabRobot/pylabrobot#965)
<#965 (comment)>
does the arm have something like a for sensing mode / plate detection?
where if you close the gripper it hits resistance and raises an error if no
resistance is hit? similar to iswap
—
Reply to this email directly, view it on GitHub
<#965 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AMMQJNDPCIWVRIP2PZARTOD43E5SFAVCNFSM6AAAAACW7TDCDWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DINRZGMZTMOBSGA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Robert Keyser - ***@***.***
|
Contributor
Author
|
Looks like the status messages are a little more nuanced. Here's one that
Crystal on my team tested out:
Press / for Help me write
Skip to content <https://mail.google.com/mail/u/0/>
Using Calico Life Sciences Mail with screen readers
<https://mail.google.com/mail/u/0/>
3 of 14
xArm gripper
Inbox
Summarize this email
Robert Keyser Mon, May 18, 6:25 AM (1 day ago)
Hi Crystal, Can you check something on the xArm if you have time this week?
Below is a method for checking the gripper status after a "get_plate"
operation. It
Crystal Nguyen Mon, May 18, 7:01 AM (1 day ago)
Will do! I’ll let you know how it goes. Hope the flight goes by quick!
Crystal Nguyen
[image: Attachments]8:55 AM (46 minutes ago)
to me
Hey Rob,
I made some small changes to the status codes, but yes it works!
Video_20260519_085356_979.mp4
<https://drive.google.com/file/d/1m4PkF5cqLlQjjuBocBcmHR2Rh89HDv1s/view?usp=drive_web>
2 Attachments • Scanned by Gmail
Crystal Nguyen (calicolabs.com)
Great, thanks a lot Crysta.
"""Bio Gripper G2 grasp verification tests.
Tests whether the gripper can detect if it actually grabbed a plate or
closed on air.
From Rob — validates wide and narrow grip with/without plate present.
Usage:
python3 gripper_test.py --ip 192.168.1.xxx # interactive test
python3 gripper_test.py --ip 192.168.1.xxx --auto # automated
(no plate)
Requires: xarm-python-sdk (pip install xarm-python-sdk)
"""
import argparse
import sys
from xarm.wrapper import XArmAPI
def close_and_verify_grip(arm, speed=0):
"""Closes the Bio Gripper G2 and verifies if the microtiter plate
is present.
Returns True if plate is gripped, raises ValueError/RuntimeError if not.
Status register is a bitmask:
Bits 0-1 (motion): 0=stopped, 1=moving, 2=caught/detected, 3=fault
Bits 2-3 (enable): 0=not enabled, 1=enabling, 2=enabled
"""
print("Closing Bio Gripper G2...", end=" ", flush=True)
code = arm.close_bio_gripper(speed=speed, wait=True, timeout=5)
if code != 0:
raise RuntimeError(f"Failed to execute close_bio_gripper. API
Return Code: {code}")
code, status = arm.get_bio_gripper_status()
if code != 0:
raise RuntimeError(f"Failed to read bio gripper status. API
Return Code: {code}")
motion = status & 0x03
enable = (status >> 2) & 0x03
print(f"(raw={status}, motion={motion}, enable={enable})", end="
", flush=True)
if enable != 2:
raise RuntimeError(f"Gripper not enabled (enable state={enable}).")
if motion == 0:
raise ValueError("Grip failed: closed fully without detecting a plate.")
elif motion == 3:
raise ValueError("Grip failed: gripper fault.")
elif motion == 1:
raise TimeoutError("Grip timeout: gripper is still moving.")
elif motion == 2:
print("Success: plate is securely gripped.")
# Check for hardware slip error (error code 12)
code, err = arm.get_bio_gripper_error()
if err == 12:
arm.clean_bio_gripper_error()
raise ValueError("Hardware Error 12: plate slipped from the
Bio Gripper.")
return True
def run_test(arm, grip_type, has_plate):
"""Run a single grip test and report pass/fail."""
label = f"{grip_type} grip, {'WITH' if has_plate else 'NO'} plate"
print(f"\n--- Test: {label} ---")
arm.open_bio_gripper(speed=0, wait=True)
print("Gripper opened.")
if has_plate:
input(f" Place plate in gripper ({grip_type} orientation),
press Enter...")
try:
result = close_and_verify_grip(arm)
if has_plate:
print(f" PASS: correctly detected plate ({label})")
return True
else:
print(f" FAIL: reported plate present when none exists ({label})")
return False
except (ValueError, TimeoutError) as e:
if not has_plate:
print(f" PASS: correctly detected no plate — {e}")
return True
else:
print(f" FAIL: didn't detect plate — {e}")
return False
def main():
parser = argparse.ArgumentParser(description='Bio Gripper G2 grasp
verification')
parser.add_argument('--ip', required=True, help='xArm IP address')
parser.add_argument('--auto', action='store_true', help='Run
no-plate tests only (no human interaction)')
args = parser.parse_args()
arm = XArmAPI(args.ip)
arm.motion_enable(enable=True)
arm.set_mode(0)
arm.set_state(0)
print("=== Bio Gripper G2 Grasp Verification ===\n")
results = []
if args.auto:
results.append(("Wide, no plate", run_test(arm, "wide",
has_plate=False)))
results.append(("Narrow, no plate", run_test(arm, "narrow",
has_plate=False)))
else:
results.append(("Wide, no plate", run_test(arm, "wide",
has_plate=False)))
results.append(("Wide, with plate", run_test(arm, "wide",
has_plate=True)))
arm.open_bio_gripper(speed=0, wait=True)
input("\n Remove plate, press Enter to continue...")
results.append(("Narrow, no plate", run_test(arm, "narrow",
has_plate=False)))
results.append(("Narrow, with plate", run_test(arm, "narrow",
has_plate=True)))
arm.open_bio_gripper(speed=0, wait=True)
print("\n=== Results ===")
all_pass = True
for name, passed in results:
status = "PASS" if passed else "FAIL"
print(f" {name}: {status}")
if not passed:
all_pass = False
print(f"\n{'All tests passed!' if all_pass else 'Some tests FAILED'}")
arm.disconnect()
if __name__ == '__main__':
main()
gripper_test.py
Displaying gripper_test.py.
…On Mon, May 18, 2026 at 6:29 AM Robert Keyser ***@***.***> wrote:
I'll have someone double check this to confirm. The SDK says yes:
def close_and_verify_grip(arm, speed=0):
"""
Closes the Bio Gripper G2 and verifies if the microtiter plate is
present.
Throws a ValueError if the grippers close but do not grip the object.
:param arm: An instantiated and connected XArmAPI object
:param speed: Gripper closing speed (0 uses default)
"""
# 1. Close the bio gripper
# wait=True blocks the script until the gripper stops moving
print("Closing Bio Gripper G2...")
code = arm.close_bio_gripper(speed=speed, wait=True, timeout=5)
if code != 0:
raise RuntimeError(f"Failed to execute close_bio_gripper. API
Return Code: {code}")
# 2. Check the gripper's internal status
code, status = arm.get_bio_gripper_status()
if code != 0:
raise RuntimeError(f"Failed to read bio gripper status. API Return
Code: {code}")
# UFACTORY Gripper Status mappings:
# 0: Stopped / Idle (Reached 71mm limit without catching anything)
# 1: Moving (Should not happen if wait=True)
# 2: Caught (Encountered object resistance at ~85mm or ~127mm)
# 3: Dropped / Slipped (Object was caught but then fell out)
if status == 0:
raise ValueError("Grip failed: The gripper closed fully to 71mm
without detecting a microtiter plate.")
elif status == 3:
raise ValueError("Grip failed: A microtiter plate was detected but
slipped out.")
elif status == 1:
raise TimeoutError("Grip timeout: The gripper is still moving.")
elif status == 2:
print("Success: Microtiter plate is securely gripped.")
else:
print(f"Warning: Gripper returned an undocumented status code:
{status}")
# 3. Double-check for hardware slip errors (Optional but recommended)
# Error code 12 specifically indicates: "The object slipped from the
BIO Gripper"
code, err = arm.get_bio_gripper_error()
if err == 12:
# Clear the error state so the gripper can be used again
arm.clean_bio_gripper_error()
raise ValueError("Hardware Error 12: The microtiter plate slipped
from the BIO Gripper.")
return True
On Sat, May 16, 2026 at 9:27 PM Rick Wierenga ***@***.***>
wrote:
> *rickwierenga* left a comment (PyLabRobot/pylabrobot#965)
> <#965 (comment)>
>
> does the arm have something like a for sensing mode / plate detection?
> where if you close the gripper it hits resistance and raises an error if no
> resistance is hit? similar to iswap
>
> —
> Reply to this email directly, view it on GitHub
> <#965 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AMMQJNDPCIWVRIP2PZARTOD43E5SFAVCNFSM6AAAAACW7TDCDWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DINRZGMZTMOBSGA>
> .
> Triage notifications on the go with GitHub Mobile for iOS
> <https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
> or Android
> <https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
>
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
--
Robert Keyser - ***@***.***
--
Robert Keyser - ***@***.***
|
Contributor
Author
|
Looks like the status codes are different than expected. Here's a test
script from Crystal that she verified on the robot:
"""Bio Gripper G2 grasp verification tests.
Tests whether the gripper can detect if it actually grabbed a plate or
closed on air.
From Rob — validates wide and narrow grip with/without plate present.
Usage:
python3 gripper_test.py --ip 192.168.1.xxx # interactive test
python3 gripper_test.py --ip 192.168.1.xxx --auto # automated (no
plate)
Requires: xarm-python-sdk (pip install xarm-python-sdk)
"""
import argparse
import sys
from xarm.wrapper import XArmAPI
def close_and_verify_grip(arm, speed=0):
"""Closes the Bio Gripper G2 and verifies if the microtiter plate is
present.
Returns True if plate is gripped, raises ValueError/RuntimeError if not.
Status register is a bitmask:
Bits 0-1 (motion): 0=stopped, 1=moving, 2=caught/detected, 3=fault
Bits 2-3 (enable): 0=not enabled, 1=enabling, 2=enabled
"""
print("Closing Bio Gripper G2...", end=" ", flush=True)
code = arm.close_bio_gripper(speed=speed, wait=True, timeout=5)
if code != 0:
raise RuntimeError(f"Failed to execute close_bio_gripper. API
Return Code: {code}")
code, status = arm.get_bio_gripper_status()
if code != 0:
raise RuntimeError(f"Failed to read bio gripper status. API Return
Code: {code}")
motion = status & 0x03
enable = (status >> 2) & 0x03
print(f"(raw={status}, motion={motion}, enable={enable})", end=" ",
flush=True)
if enable != 2:
raise RuntimeError(f"Gripper not enabled (enable state={enable}).")
if motion == 0:
raise ValueError("Grip failed: closed fully without detecting a
plate.")
elif motion == 3:
raise ValueError("Grip failed: gripper fault.")
elif motion == 1:
raise TimeoutError("Grip timeout: gripper is still moving.")
elif motion == 2:
print("Success: plate is securely gripped.")
# Check for hardware slip error (error code 12)
code, err = arm.get_bio_gripper_error()
if err == 12:
arm.clean_bio_gripper_error()
raise ValueError("Hardware Error 12: plate slipped from the Bio
Gripper.")
return True
def run_test(arm, grip_type, has_plate):
"""Run a single grip test and report pass/fail."""
label = f"{grip_type} grip, {'WITH' if has_plate else 'NO'} plate"
print(f"\n--- Test: {label} ---")
arm.open_bio_gripper(speed=0, wait=True)
print("Gripper opened.")
if has_plate:
input(f" Place plate in gripper ({grip_type} orientation), press
Enter...")
try:
result = close_and_verify_grip(arm)
if has_plate:
print(f" PASS: correctly detected plate ({label})")
return True
else:
print(f" FAIL: reported plate present when none exists ({label}
)")
return False
except (ValueError, TimeoutError) as e:
if not has_plate:
print(f" PASS: correctly detected no plate — {e}")
return True
else:
print(f" FAIL: didn't detect plate — {e}")
return False
def main():
parser = argparse.ArgumentParser(description='Bio Gripper G2 grasp
verification')
parser.add_argument('--ip', required=True, help='xArm IP address')
parser.add_argument('--auto', action='store_true', help='Run no-plate
tests only (no human interaction)')
args = parser.parse_args()
arm = XArmAPI(args.ip)
arm.motion_enable(enable=True)
arm.set_mode(0)
arm.set_state(0)
print("=== Bio Gripper G2 Grasp Verification ===\n")
results = []
if args.auto:
results.append(("Wide, no plate", run_test(arm, "wide", has_plate=
False)))
results.append(("Narrow, no plate", run_test(arm, "narrow",
has_plate=False)))
else:
results.append(("Wide, no plate", run_test(arm, "wide", has_plate=
False)))
results.append(("Wide, with plate", run_test(arm, "wide", has_plate=
True)))
arm.open_bio_gripper(speed=0, wait=True)
input("\n Remove plate, press Enter to continue...")
results.append(("Narrow, no plate", run_test(arm, "narrow",
has_plate=False)))
results.append(("Narrow, with plate", run_test(arm, "narrow",
has_plate=True)))
arm.open_bio_gripper(speed=0, wait=True)
print("\n=== Results ===")
all_pass = True
for name, passed in results:
status = "PASS" if passed else "FAIL"
print(f" {name}: {status}")
if not passed:
all_pass = False
print(f"\n{'All tests passed!' if all_pass else 'Some tests FAILED'}")
arm.disconnect()
if __name__ == '__main__':
main()
…On Mon, May 18, 2026 at 6:29 AM Robert Keyser ***@***.***> wrote:
I'll have someone double check this to confirm. The SDK says yes:
def close_and_verify_grip(arm, speed=0):
"""
Closes the Bio Gripper G2 and verifies if the microtiter plate is
present.
Throws a ValueError if the grippers close but do not grip the object.
:param arm: An instantiated and connected XArmAPI object
:param speed: Gripper closing speed (0 uses default)
"""
# 1. Close the bio gripper
# wait=True blocks the script until the gripper stops moving
print("Closing Bio Gripper G2...")
code = arm.close_bio_gripper(speed=speed, wait=True, timeout=5)
if code != 0:
raise RuntimeError(f"Failed to execute close_bio_gripper. API
Return Code: {code}")
# 2. Check the gripper's internal status
code, status = arm.get_bio_gripper_status()
if code != 0:
raise RuntimeError(f"Failed to read bio gripper status. API Return
Code: {code}")
# UFACTORY Gripper Status mappings:
# 0: Stopped / Idle (Reached 71mm limit without catching anything)
# 1: Moving (Should not happen if wait=True)
# 2: Caught (Encountered object resistance at ~85mm or ~127mm)
# 3: Dropped / Slipped (Object was caught but then fell out)
if status == 0:
raise ValueError("Grip failed: The gripper closed fully to 71mm
without detecting a microtiter plate.")
elif status == 3:
raise ValueError("Grip failed: A microtiter plate was detected but
slipped out.")
elif status == 1:
raise TimeoutError("Grip timeout: The gripper is still moving.")
elif status == 2:
print("Success: Microtiter plate is securely gripped.")
else:
print(f"Warning: Gripper returned an undocumented status code:
{status}")
# 3. Double-check for hardware slip errors (Optional but recommended)
# Error code 12 specifically indicates: "The object slipped from the
BIO Gripper"
code, err = arm.get_bio_gripper_error()
if err == 12:
# Clear the error state so the gripper can be used again
arm.clean_bio_gripper_error()
raise ValueError("Hardware Error 12: The microtiter plate slipped
from the BIO Gripper.")
return True
On Sat, May 16, 2026 at 9:27 PM Rick Wierenga ***@***.***>
wrote:
> *rickwierenga* left a comment (PyLabRobot/pylabrobot#965)
> <#965 (comment)>
>
> does the arm have something like a for sensing mode / plate detection?
> where if you close the gripper it hits resistance and raises an error if no
> resistance is hit? similar to iswap
>
> —
> Reply to this email directly, view it on GitHub
> <#965 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AMMQJNDPCIWVRIP2PZARTOD43E5SFAVCNFSM6AAAAACW7TDCDWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DINRZGMZTMOBSGA>
> .
> Triage notifications on the go with GitHub Mobile for iOS
> <https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
> or Android
> <https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
>
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
--
Robert Keyser - ***@***.***
--
Robert Keyser - ***@***.***
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces a new abstraction for 6-axis robotic arms to the codebase, including a generic frontend and backend interface, as well as an initial implementation for the xArm 6 robot. It also provides comprehensive tests and an interactive example script for pick-and-place operations. These changes pave the way for supporting advanced robotic arms and make it easier to integrate different 6-axis robots in the future.
6-Axis Arm Abstraction and Implementation
SixAxisArmfrontend class insix_axis.pythat defines high-level operations (movement, gripper control, pick/place, freedrive mode) for 6-axis robots, delegating to a backend.SixAxisBackendinterface insix_axis_backend.pyspecifying all required backend methods for 6-axis robot support.__init__.py) for easy import. [1] [2]xArm 6-Specific Additions
XArm6Axis) and backend scaffolding for the xArm 6 robot, preparing for hardware-specific implementations. [1] [2]example_pick_place.py) that guides users through teaching positions, saving/loading them, and running a pick-and-place cycle with vertical access.taught_positions.jsonfile as an example of saved positions for the xArm 6.Testing
six_axis_tests.py) that mocks the backend and validates all major frontend behaviors, including freedrive mode handling and command delegation.