equipment
UFactory xArm 6 Robot Arm
Public Made by Adomby adom
UFACTORY's 6-axis, 5 kg, 700 mm collaborative arm: specs, base bolt pattern, manuals, a per-link GLB digital twin with kinematics, and AI skills for the Python SDK, web UI and ROS 2.
← Commit history
UFactory xArm 6: specs, mounting, docs, digital twin, skills
19 files changed
+808−5
README.md+219−1docs/xarm6-base-bolt-pattern.pnginstall.sh+8models/LICENSE-xarm_ros2.txt+27models/kinematics.json+227models/link1.glbmodels/link2.glbmodels/link3.glbmodels/link4.glbmodels/link5.glbmodels/link6.glbmodels/link_base.glbmodels/xarm6_home.glbpackage.json+25page.json+27−4skills/xarm-control/SKILL.md+150skills/xarm-twin/SKILL.md+87skills/xarm-twin/fk.py+34uninstall.sh+4README.md+219−1@@ -1,3 +1,221 @@ # UFactory xArm 6 Robot Arm -UFACTORY's 6-axis, 5 kg, 700 mm collaborative arm: specs, base bolt pattern, manuals, a per-link GLB digital twin with kinematics, and AI skills for the Python SDK, web UI and ROS 2.+A 6-axis collaborative arm from UFACTORY with a 5 kg payload and 700 mm reach. This page collects the specs, the official manuals, a ready-to-use digital twin (per-link GLBs plus a kinematics file), and AI skills for driving the arm from Python, the browser and ROS 2.++## Key specs++All values are from the UFACTORY xArm Hardware Manual (1305 model, V2.6.0, chapter 8) and the UFACTORY product page, unless noted.++| Spec | xArm 6 |+|---|---|+| Model code | XI1305 |+| Degrees of freedom | 6 (all revolute) |+| Payload | 5 kg |+| Reach | 700 mm |+| Repeatability | ±0.1 mm |+| Max joint speed | 180 °/s |+| Max TCP speed | 1 m/s |+| Arm weight (arm only) | 12.5 kg |+| Base footprint | Ø126 mm |+| Mounting | any direction (floor, wall, ceiling); set the mount angle in UFACTORY Studio |+| End flange | DIN ISO 9409-1-A50/63, M5 × 6 |+| Materials | aluminium, carbon fibre |+| Ambient temperature | 0 to 50 °C |+| Cleanroom | ISO Class 5 |+| IP rating | not stated in the manual or product page |+| Arm supply | 24 V DC, 20.8 A (typical 200 W, 500 W supply recommended) |+| Control box | AC: 100 to 240 V AC, 50/60 Hz, 3.9 kg, 285 × 135 × 101 mm. DC: 24 to 72 V DC, 2.6 kg, 262 × 160 × 76 mm |+| Control box I/O | 8 CI + 8 DI, 8 CO + 8 DO, 2 AI, 2 AO, 1 × RS-485 master |+| Robot link | Ethernet (UFACTORY private TCP, Modbus TCP) |+| Programming | UFACTORY Studio (browser), Python SDK, C++ SDK, ROS / ROS 2 |++### Joint ranges++| Joint | Hardware range (manual) | Range in the ROS 2 model (`xarm_ros2`) |+|---|---|---|+| J1 | ±360° | ±178.2° |+| J2 | −117° to +116° | −117.97° to +120.0° |+| J3 | −219° to +10° | −178.2° to +11.0° |+| J4 | ±360° | ±178.2° |+| J5 | −97° to +180° | −97.0° to +178.2° |+| J6 | ±360° | ±178.2° |++The ROS 2 description clamps the continuous joints to ±0.99π, which is what motion planners and the twin below use. Plan with the tighter set unless you know you need the full hardware range.++## Base mounting++++The base is a Ø126 mm round flange held down by **five M5 socket-head cap screws on a Ø110 mm bolt circle**. Each hole is Ø5.5 mm through a 10 mm flange with a Ø9 × 5.5 mm counterbore, so the screw heads sit below the flange top; the screw needs 4.5 mm of flange grip plus the thread engagement in your plate. The holes sit at 0°, ±60° and ±120° from the base +X axis, and the sixth position (180°) is left empty on the cable-connector side. Two Ø5 mm locating holes (+0.006/+0.024, 8 mm deep, one round and one slotted) sit at X = −10 mm, Y = ±55 mm for dowel pins, so the arm goes back on in exactly the same place.++Hole centres in the base frame (mm): (55.0, 0), (27.5, ±47.63), (−27.5, ±47.63). The drawing above is redrawn from the manual's section 2.2.3.1 "Robot Base Mounting"; which side carries the round pin and which the slot follows the manual's view direction, so check it against the UFACTORY STEP model before you machine a plate.++UFACTORY's installation rules: the mounting surface must be rigid and vibration-free, and must withstand at least 10 times the base joint's full torque and 5 times the arm's weight. A safety assessment is required every time the arm is installed. The arm ships with a clamp-style mounting tool for table edges.++## Documents and downloads++UFACTORY's documents are not licensed for redistribution, so this page links to them on ufactory.cc.++| Document | Link |+|---|---|+| Product page and spec table | [ufactory.cc/xarm-collaborative-robot](https://www.ufactory.cc/xarm-collaborative-robot/) |+| xArm Hardware Manual V2.6.0 (1305 model): safety, installation, mounting drawing, electrical I/O, specs | [PDF](https://www.ufactory.cc/wp-content/uploads/2026/06/xArm_Hardware_Manual1305_V2.6.0.pdf) · [online](https://docs.xarm.ufactory.cc/1.safety.html) |+| xArm Developer Manual V2.0.1: Modbus TCP protocol, report ports, error codes | [PDF](https://www.ufactory.cc/wp-content/uploads/2026/04/xArm-Developer-Manual-V2.0.1.pdf) |+| UFACTORY Studio User Manual V2.8.2: web UI, settings, safety features | [PDF](https://www.ufactory.cc/wp-content/uploads/2026/10/UFACTORY_Studio_User_Manual_V2.8.2.pdf) |+| xArm 6 (XI1305) 3D model (STEP) | [zip](https://www.ufactory.cc/wp-content/uploads/2025/06/xArm6XF1305.zip) |+| xArm 6 movable SolidWorks model | [zip](https://www.ufactory.cc/wp-content/uploads/2024/07/xArm-6-XI1305-solidworks2016.zip) |+| AC control box 3D model | [zip](https://www.ufactory.cc/wp-content/uploads/2023/04/AC-Control-BoxAC1300.zip) |+| Mounting tool 3D model | [zip](https://www.ufactory.cc/wp-content/uploads/2024/02/xArm-Mounting-Tool.zip) |+| All downloads | [ufactory.cc/download](https://www.ufactory.cc/download/) |+| Firmware release notes | [docs.releasenote.ufactory.cc](https://docs.releasenote.ufactory.cc/release_note/version_list.html) |+| Browser simulator | [xarm6.sim.ufactory.cc](https://xarm6.sim.ufactory.cc/reservation?lang=en&channel=prod) |++## Digital twin: per-link GLBs and kinematics++The [`models/`](models/) folder holds a ready-to-animate twin of the xArm 6, converted from UFACTORY's open-source ROS 2 description.++| File | What it is |+|---|---|+| `link_base.glb` … `link6.glb` | one mesh per URDF link, each in its own link frame (white shell and grey joint covers as separate materials) |+| `xarm6_home.glb` | the whole arm baked at the zero pose, for a static preview |+| `kinematics.json` | the joint chain: parent link, origin xyz (mm), origin rpy (rad), axis, limits (deg), home pose and TCP |+| `LICENSE-xarm_ros2.txt` | the BSD-3-Clause licence the meshes and kinematics come under |++Conventions:+- **Units are millimetres, Z is up.** The vertex data is the URDF data × 1000; no extra transform is needed.+- **Each child link's pose** is `parent × Translate(origin_xyz_mm) × RPY(origin_rpy) × RotZ(q)`, where RPY is URDF fixed-axis (R = Rz(yaw) · Ry(pitch) · Rx(roll)). Every joint axis is +Z of its own frame.+- **Zero pose** (all joints 0°): upper arm up, forearm forward, flange pointing down. The flange centre is at (207, 0, 112) mm in the base frame, which matches what the SDK's `get_position()` reports at zero.+- **Base frame:** the origin is the centre of the base's bottom face; +X is the direction the arm reaches at zero; the cable connectors face −X. J2's axis is 267 mm above the mounting face.+- **TCP:** the `link6` origin is the flange face, +Z out of the flange. Add your tool offset there. No gripper is included.+- **glTF loaders** that convert +Y-up to their own space (Babylon.js, three.js) add a root rotation; parent the links under a Z-up root and undo that root transform so each link stays in its authored frame.++Forward kinematics in a few lines of Python (the full script is [`skills/xarm-twin/fk.py`](skills/xarm-twin/fk.py)):++```python+from fk import fk+import json+kin = json.load(open("models/kinematics.json"))+pose = fk(kin, [0, 0, 0, 0, 0, 0])["link6"] # 4x4, mm+print(pose[:3, 3]) # [207. 0. 112.]+```++Source: [`xArm-Developer/xarm_ros2`](https://github.com/xArm-Developer/xarm_ros2) at commit `62936f7`, `xarm_description` (the `xarm6_1305` visual meshes and `xarm6_default_kinematics.yaml`). Copyright (c) 2018 UFACTORY Inc., BSD-3-Clause; the licence travels with the files in `models/`.++## Controlling the arm++### 1. UFACTORY Studio in the browser++The control box serves its own web UI, UFACTORY Studio, on port 18333. Put your PC on the same subnet as the control box and open:++```+http://<arm-ip>:18333+```++Studio covers jogging (Live Control), Blockly programs, trajectory recording, the end-effector, TCP and payload settings, and the safety settings below. The factory IP is on a label on the control box (192.168.1.xxx). If the IP is lost, short RI0 to GND on the control box with the power off, power up and wait for the beep: the IP resets to `192.168.1.111` (Hardware Manual 3.4.5).++### 2. Python SDK quick start++```bash+pip install xarm-python-sdk+```++```python+from xarm.wrapper import XArmAPI++arm = XArmAPI("192.168.1.xxx") # connects to the control box+arm.clean_warn(); arm.clean_error()+arm.motion_enable(enable=True) # energise the servos+arm.set_mode(0) # 0 = position control+arm.set_state(state=0) # 0 = ready to move++arm.set_servo_angle(angle=[0, -30, -60, 0, 90, 0], speed=30, wait=True) # joints, deg, deg/s+code, pose = arm.get_position() # [x, y, z, roll, pitch, yaw], mm and deg+arm.set_position(x=300, y=0, z=250, roll=180, pitch=0, yaw=0, speed=100, wait=True) # mm/s+code, joints = arm.get_servo_angle()++arm.disconnect()+```++The calls you will use most:++| Call | What it does |+|---|---|+| `XArmAPI(ip, is_radian=False)` | connect; angles default to degrees, lengths are always mm |+| `motion_enable(True)` | enable all servos (also needed after an e-stop) |+| `set_mode(m)` | 0 position, 1 servo (streaming `set_servo_angle_j`), 2 joint teach (hand-guiding), 4 joint velocity, 5 Cartesian velocity, 6/7 online trajectory |+| `set_state(s)` | 0 move, 3 pause, 4 stop |+| `set_servo_angle(angle=[...], speed, mvacc, wait)` | joint move |+| `set_position(x, y, z, roll, pitch, yaw, speed, wait, relative)` | linear Cartesian move |+| `get_position()` / `get_servo_angle()` | read the TCP pose / joint angles; both return `(code, values)` |+| `emergency_stop()` | software stop: `set_state(4)`, then re-enable; does not clear errors and is not a substitute for the hardware e-stop |+| `clean_error()` / `clean_warn()` | clear error and warning codes; afterwards call `motion_enable(True)` and `set_state(0)` yourself |+| `set_collision_sensitivity(0..5)` | 0 disables collision detection; higher is more sensitive |+| `set_reduced_mode(True)` | turn on the speed and range limits configured for reduced mode |+| `set_gripper_mode(0)`, `set_gripper_enable(True)`, `set_gripper_position(pos, wait=True)` | UFACTORY xArm Gripper in position mode; `pos` runs from about 0 (closed) to 850 (open) |++Every call returns a code; 0 means success. The [API reference](https://github.com/xArm-Developer/xArm-Python-SDK/blob/master/doc/api/xarm_api.md) and [error codes](https://github.com/xArm-Developer/xArm-Python-SDK/blob/master/doc/api/xarm_api_code.md) are in the SDK repo, along with [numbered examples](https://github.com/xArm-Developer/xArm-Python-SDK/tree/master/example/wrapper/common).++### 3. C++ SDK++[`xArm-Developer/xArm-CPLUS-SDK`](https://github.com/xArm-Developer/xArm-CPLUS-SDK) (BSD-3-Clause) mirrors the Python API (`XArmAPI`, `motion_enable`, `set_mode`, `set_state`, `set_servo_angle`, `set_position`) for xArm 5/6/7, Lite 6 and the 850.++### 4. ROS 2 and MoveIt 2++[`xArm-Developer/xarm_ros2`](https://github.com/xArm-Developer/xarm_ros2) has one branch per ROS 2 distribution (`humble`, `jazzy`, `rolling`, plus older `foxy` and `galactic`). Clone the branch that matches your ROS install.++```bash+ros2 launch xarm_description xarm6_rviz_display.launch.py # view the model+ros2 launch xarm_moveit_config xarm6_moveit_fake.launch.py # MoveIt, simulated arm+ros2 launch xarm_moveit_config xarm6_moveit_realmove.launch.py robot_ip:=<arm-ip> # MoveIt, real arm+ros2 launch xarm_api xarm6_driver.launch.py robot_ip:=<arm-ip> # driver services only+```++Add `add_gripper:=true` to include the xArm Gripper model. ROS 1 users have [`xarm_ros`](https://github.com/xArm-Developer/xarm_ros).++### Network ports on the control box++| Port | Use | Source |+|---|---|---|+| 18333 | UFACTORY Studio web UI (HTTP) | Studio User Manual 3.2 |+| 502 | SDK and command channel: UFACTORY private TCP and standard Modbus TCP | Developer Manual 2.1.2, Studio Manual 7.2.6 |+| 504 | G-code over TCP (firmware 2.2.0 and later) | Studio User Manual 6.4 |+| 30001 | automatic state report, "normal", 5 Hz | Developer Manual 2.1.6 |+| 30002 | automatic state report, "rich" (adds arm type, joint count and more), 5 Hz | Developer Manual 2.1.6 |+| 30003 | automatic state report, "develop" (joints, pose, joint torques, F/T), 100 Hz | Developer Manual 2.1.6 |++UFACTORY recommends a wired link. A Wi-Fi hop between the PC and the router works but adds delay and packet loss.++## Safety++- **Hardware e-stop.** Pressing the red button on the control box decelerates the arm, clears queued commands and cuts arm power within 300 ms. The arm sags slightly as the brakes catch. To recover: twist the button to release it, then re-enable from Studio or `motion_enable(True)`. UFACTORY notes the e-stop is not a risk-reduction measure in itself.+- **Collision detection.** Set the sensitivity from 0 (off) to 5. It relies on the payload, TCP offset and mount angle being set correctly; wrong values cause false trips or missed collisions. Collision rebound (back off after contact) and self-collision prevention are separate switches in Studio.+- **Safety boundary.** A Cartesian box the TCP must stay inside; the arm stops if the TCP leaves it.+- **Reduced mode.** Lower limits on TCP speed, joint speed and joint range, switchable from Studio, the SDK, or a control box input.+- **Manual (teach) mode** gravity-compensates the arm for hand-guiding; teach sensitivity is 1 to 5.+- **Before any motion:** keep people out of the 700 mm reach plus the tool, set payload and TCP, start at low speed with `wait=True`, and keep someone within reach of the e-stop.+- **Releasing a joint brake** lets that joint fall; support the arm first.++## Talk to the AI++Install the skills below, then try:++- "**xarm** connect to the arm at 192.168.1.xxx and read the joint angles"+- "**move the xArm** to joints 0, -30, -60, 0, 90, 0 at 20 deg/s and wait"+- "**xarm** jog the TCP 50 mm up in Z, then return"+- "**xarm clear error** and re-enable the arm"+- "**xarm twin** load the xArm 6 GLBs in a Z-up scene and pose it from these joint angles"+- "**xarm forward kinematics** where is the flange for joints 30, 10, -40, 0, 60, 0?"+- "**xarm mounting plate** draw the bolt pattern for an adapter plate"++## Skills++The [`skills/`](skills/) folder carries AI skills for this arm.++| Skill | What it's for |+|---|---|+| [`xarm-control`](skills/xarm-control/SKILL.md) | connect, enable, move, read state, gripper, error recovery and safe-motion rules with the Python SDK |+| [`xarm-twin`](skills/xarm-twin/SKILL.md) | load the per-link GLBs, compose the joint chain, forward kinematics, mount the twin on a plate |++## Licence++The meshes and kinematics in `models/` are derived from UFACTORY's `xarm_ros2` and stay under its BSD-3-Clause licence (Copyright (c) 2018 UFACTORY Inc.). The bolt-pattern diagram, the text and the skills on this page are Adom's, MIT. UFACTORY's PDFs and CAD files are linked, not copied. xArm and UFACTORY are trademarks of UFACTORY.
docs/xarm6-base-bolt-pattern.pngadded⋯ 1 unchanged line ⋯
install.shadded+8@@ -0,0 +1,8 @@+#!/usr/bin/env bash+set -e+DEST="$HOME/.claude/skills"+install_skill() { mkdir -p "$DEST/$1"; cp "$2" "$DEST/$1/SKILL.md"; }+install_skill xarm-control skills/xarm-control/SKILL.md+install_skill xarm-twin skills/xarm-twin/SKILL.md+cp skills/xarm-twin/fk.py "$DEST/xarm-twin/fk.py"+echo "OK: Installed xarm-control and xarm-twin to $DEST/."
models/LICENSE-xarm_ros2.txtadded+27@@ -0,0 +1,27 @@+Copyright (c) 2018, UFACTORY Inc.++All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.+ * Neither the name of the copyright holder nor the names of its contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.+ +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
models/kinematics.jsonadded+227@@ -0,0 +1,227 @@+{+ "units": "mm",+ "up": "+Z",+ "angle_units": {+ "origin_rpy": "rad (URDF fixed-axis roll-pitch-yaw, R=Rz(yaw)Ry(pitch)Rx(roll))",+ "limit_deg": "deg"+ },+ "robot": "UFactory xArm 6",+ "links": [+ {+ "name": "link_base",+ "glb": "link_base.glb",+ "parent": null,+ "joint": null+ },+ {+ "name": "link1",+ "glb": "link1.glb",+ "parent": "link_base",+ "joint": {+ "name": "joint1",+ "type": "revolute",+ "origin_xyz_mm": [+ 0.0,+ 0.0,+ 267.0+ ],+ "origin_rpy": [+ 0.0,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -178.2,+ 178.2+ ]+ }+ },+ {+ "name": "link2",+ "glb": "link2.glb",+ "parent": "link1",+ "joint": {+ "name": "joint2",+ "type": "revolute",+ "origin_xyz_mm": [+ 0.0,+ 0.0,+ 0.0+ ],+ "origin_rpy": [+ -1.5708,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -117.972,+ 120.0+ ]+ }+ },+ {+ "name": "link3",+ "glb": "link3.glb",+ "parent": "link2",+ "joint": {+ "name": "joint3",+ "type": "revolute",+ "origin_xyz_mm": [+ 53.5,+ -284.5,+ 0.0+ ],+ "origin_rpy": [+ 0.0,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -178.2,+ 11.0+ ]+ }+ },+ {+ "name": "link4",+ "glb": "link4.glb",+ "parent": "link3",+ "joint": {+ "name": "joint4",+ "type": "revolute",+ "origin_xyz_mm": [+ 77.5,+ 342.5,+ 0.0+ ],+ "origin_rpy": [+ -1.5708,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -178.2,+ 178.2+ ]+ }+ },+ {+ "name": "link5",+ "glb": "link5.glb",+ "parent": "link4",+ "joint": {+ "name": "joint5",+ "type": "revolute",+ "origin_xyz_mm": [+ 0.0,+ 0.0,+ 0.0+ ],+ "origin_rpy": [+ 1.5708,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -97.0,+ 178.2+ ]+ }+ },+ {+ "name": "link6",+ "glb": "link6.glb",+ "parent": "link5",+ "joint": {+ "name": "joint6",+ "type": "revolute",+ "origin_xyz_mm": [+ 76.0,+ 97.0,+ 0.0+ ],+ "origin_rpy": [+ -1.5708,+ 0.0,+ 0.0+ ],+ "axis": [+ 0,+ 0,+ 1+ ],+ "limit_deg": [+ -178.2,+ 178.2+ ]+ }+ }+ ],+ "home_deg": [+ 0,+ 0,+ 0,+ 0,+ 0,+ 0+ ],+ "tcp": {+ "parent": "link6",+ "origin_xyz_mm": [+ 0,+ 0,+ 0+ ],+ "note": "flange face = link6 origin; +Z of link6 points out of the flange. Add tool offset here."+ },+ "notes": [+ "Each GLB is in its own URDF link frame, metres*1000 = mm, Z-up, no extra transform needed.",+ "Child link world = parent world * Translate(origin_xyz_mm) * RPY(origin_rpy) * Rot(axis, q).",+ "GLB loaders (Babylon) convert glTF +Y-up to scene space; when parenting under a Z-up root, undo the loader __root__ handedness/rotation so the link frame stays as authored (glTF stores the raw Z-up vertex coords).",+ "Meshes: xarm6_1305 visual DAE set (newer xArm 6 shell, separate white shell + grey joint covers). Kinematics identical to the legacy xarm6 STL set.",+ "home_deg all zeros = UFactory zero pose (upper arm up, forearm forward, flange pointing down), TCP ~ (207, 0, 112) mm.",+ "No gripper included (link6 = flange/end cap)."+ ],+ "source": {+ "repo": "https://github.com/xArm-Developer/xarm_ros2",+ "commit": "62936f7ea1846a85f7350de2c4c18f39e6d19715",+ "files": [+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/config/kinematics/default/xarm6_default_kinematics.yaml",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/urdf/xarm6/xarm6.urdf.xacro",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/urdf/xarm6/xarm6_robot_macro.xacro",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link_base.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link1.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link2.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link3.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link4.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link5.dae",+ "https://raw.githubusercontent.com/xArm-Developer/xarm_ros2/62936f7ea1846a85f7350de2c4c18f39e6d19715/xarm_description/meshes/xarm6_1305/visual/link6.dae"+ ],+ "license": "BSD-3-Clause, repo LICENSE, Copyright (c) 2018 UFACTORY Inc."+ }+}
models/link1.glbadded⋯ 1 unchanged line ⋯
models/link2.glbadded⋯ 1 unchanged line ⋯
models/link3.glbadded⋯ 1 unchanged line ⋯
models/link4.glbadded⋯ 1 unchanged line ⋯
models/link5.glbadded⋯ 1 unchanged line ⋯
models/link6.glbadded⋯ 1 unchanged line ⋯
models/link_base.glbadded⋯ 1 unchanged line ⋯
models/xarm6_home.glbadded⋯ 1 unchanged line ⋯
package.jsonadded+25@@ -0,0 +1,25 @@+{+ "name": "ufactory-xarm-6",+ "title": "UFactory xArm 6 Robot Arm",+ "version": "0.1.0",+ "description": "UFACTORY's 6-axis, 5 kg, 700 mm collaborative arm: specs, base bolt pattern, manuals, a per-link GLB digital twin with kinematics, and AI skills for the Python SDK, web UI and ROS 2.",+ "license": "MIT",+ "files": [+ "README.md",+ "page.json",+ "install.sh",+ "uninstall.sh",+ "skills/xarm-control/SKILL.md",+ "skills/xarm-twin/SKILL.md",+ "skills/xarm-twin/fk.py",+ "models/*",+ "docs/*"+ ],+ "scripts": {+ "install": "bash install.sh",+ "uninstall": "bash uninstall.sh"+ },+ "slug": "ufactory-xarm-6",+ "type": "component",+ "dependencies": {}+}
page.json+27−4@@ -16,13 +16,36 @@ "name": "John Lauer", "email": "[email protected]" },- "tags": [],- "license": "proprietary",+ "tags": [+ "robot-arm",+ "cobot",+ "ufactory",+ "xarm",+ "xarm-6",+ "digital-twin",+ "python-sdk",+ "ros2",+ "moveit"+ ],+ "license": "MIT", "visibility": { "public": true },- "sample_prompts": [],- "discovery_triggers": [],+ "sample_prompts": [+ "xarm connect to the arm at 192.168.1.xxx and read the joint angles",+ "xarm twin load the xArm 6 GLBs in a Z-up scene and pose it from these joint angles",+ "xarm mounting plate draw the bolt pattern for an adapter plate"+ ],+ "discovery_triggers": [+ "xarm",+ "xarm 6",+ "ufactory xarm",+ "ufactory robot arm",+ "xarm python sdk",+ "xarm digital twin",+ "xarm bolt pattern",+ "xarm mounting"+ ], "discovery_pitch": null, "metadata": {}, "created_at": "2026-10-16T12:12:54.005Z",
skills/xarm-control/SKILL.mdadded+150@@ -0,0 +1,150 @@+---+name: xarm-control+description: >-+ Drive a UFACTORY xArm 6 (also xArm 5/7, Lite 6, 850) from Python with the official+ xArm-Python-SDK: connect by IP, clear errors, enable motion, set mode and state, joint+ and Cartesian moves, read joints and TCP pose, gripper, collision sensitivity, reduced+ mode, emergency stop and recovery, plus the safe-motion rules to follow before moving a+ real arm. Also covers the UFACTORY Studio web UI (port 18333), the control box ports+ (502, 504, 30001-30003) and the ROS 2 launch files. Trigger words: xarm, xArm 6,+ ufactory, move the xarm, xarm python sdk, XArmAPI, set_servo_angle, set_position,+ get_position, get_servo_angle, motion_enable, set_mode, set_state, clean_error, xarm+ error, xarm gripper, ufactory studio, xarm ip, xarm emergency stop, xarm ros2, xarm moveit.+---++Parent page: **UFactory xArm 6 Robot Arm**++# Controlling the xArm with the Python SDK++## Before you move a real arm (always)++1. Confirm with the human that the cell is clear and someone can reach the e-stop, unless the+ operator has said this arm is cleared for unattended motion.+2. Read the current state first (`get_servo_angle`, `get_position`, `arm.error_code`,+ `arm.warn_code`, `arm.state`, `arm.mode`). Never assume where the arm is.+3. First move of a session: low speed (joints ≤ 20 °/s, TCP ≤ 50 mm/s), `wait=True`, one move at a time.+4. Check the target against the joint limits (below) and the workspace before sending it.+5. Check the payload and TCP offset match the tool that is actually on the flange+ (`arm.tcp_load`, `arm.tcp_offset`); wrong values break collision detection.+6. After any error, stop and report the code and its meaning before clearing it. Do not loop+ `clean_error` + retry.++## Install and connect++```bash+pip install xarm-python-sdk+```++```python+from xarm.wrapper import XArmAPI+arm = XArmAPI("192.168.1.xxx") # is_radian=False by default: angles in deg, lengths in mm+print(arm.version, arm.state, arm.mode, arm.error_code, arm.warn_code)+```++The PC must be on the same subnet as the control box. A reachable box answers on TCP 502.++## Enable and get ready++```python+arm.clean_warn()+arm.clean_error()+arm.motion_enable(enable=True)+arm.set_mode(0) # position control+arm.set_state(state=0) # ready+```++Modes: 0 position, 1 servo (streaming `set_servo_angle_j` / `set_servo_cartesian`), 2 joint+teach (hand-guiding), 4 joint velocity, 5 Cartesian velocity, 6 joint online planning,+7 Cartesian online planning. Re-send `set_state(0)` after every `set_mode`.++States (`arm.state`): 1 in motion, 2 sleeping, 3 suspended (paused), 4 stopping. `set_state(0)` starts motion, 3 pauses, 4 stops.++## Move++```python+# joints, degrees; speed deg/s, mvacc deg/s^2+code = arm.set_servo_angle(angle=[0, -30, -60, 0, 90, 0], speed=20, mvacc=200, wait=True)++# one joint+code = arm.set_servo_angle(servo_id=1, angle=45, speed=20, wait=True)++# Cartesian line, mm and deg; speed mm/s+code = arm.set_position(x=300, y=0, z=250, roll=180, pitch=0, yaw=0, speed=50, wait=True)++# relative move: 50 mm up+code = arm.set_position(z=50, relative=True, speed=30, wait=True)+```++Every call returns a code; 0 is success. Anything else: read `arm.error_code` and look it up+in the SDK's `doc/api/xarm_api_code.md`.++## Read state++```python+code, joints = arm.get_servo_angle() # [j1..j6, (j7)], deg+code, pose = arm.get_position() # [x, y, z, roll, pitch, yaw], mm, deg+code, (pos, vel, eff) = arm.get_joint_states()+```++At the zero pose (all joints 0) the flange reads about (207, 0, 112, 180, 0, 0).++For a live stream without polling, connect to the report ports: 30003 (100 Hz: joints, pose,+joint torques, F/T), 30001 (5 Hz normal), 30002 (5 Hz rich). Formats: Developer Manual 2.1.6.++## Gripper (UFACTORY xArm Gripper)++```python+arm.set_gripper_mode(0)+arm.set_gripper_enable(True)+arm.set_gripper_speed(3000)+arm.set_gripper_position(850, wait=True) # open+arm.set_gripper_position(0, wait=True) # close+code, pos = arm.get_gripper_position()+```++BIO Gripper, vacuum gripper and Robotiq have their own calls (`set_bio_gripper_*`,+`set_vacuum_gripper`, `robotiq_*`).++## Stop, errors, recovery++- `arm.set_state(4)`: stop now (clears the queue).+- `arm.emergency_stop()`: the SDK's software stop (`set_state(4)` then re-enable). It does not cut+ power and does not clear errors. The red button on the control box is the real e-stop.+- After the hardware e-stop: the human releases the button, then `motion_enable(True)`,+ `set_mode(0)`, `set_state(0)`.+- After an error (collision, joint limit, overcurrent): report it, let the human decide,+ then `clean_error()`, `clean_warn()`, `motion_enable(True)`, `set_state(0)`.++## Safety settings++```python+arm.set_collision_sensitivity(3) # 0 off .. 5 most sensitive+arm.set_reduced_mode(True) # apply the reduced-mode speed/range limits+arm.set_tcp_load(weight_kg, [cx, cy, cz])+arm.set_tcp_offset([x, y, z, roll, pitch, yaw])+arm.save_conf() # persist; otherwise lost on reboot+```++Do not lower collision sensitivity or turn off the safety boundary to get past an error+without the operator's OK.++## Joint limits (ROS 2 model, use these for planning)++| J1 | J2 | J3 | J4 | J5 | J6 |+|---|---|---|---|---|---|+| ±178.2° | −117.97° to 120° | −178.2° to 11° | ±178.2° | −97° to 178.2° | ±178.2° |++Hardware ranges are wider on J1, J3, J4 and J6 (see the page's spec table).++## Web UI and ROS 2++- UFACTORY Studio: `http://<arm-ip>:18333` (jog, Blockly, settings, safety).+- ROS 2 (`xarm_ros2`, branch per distro):+ `ros2 launch xarm_moveit_config xarm6_moveit_realmove.launch.py robot_ip:=<arm-ip>`;+ driver only: `ros2 launch xarm_api xarm6_driver.launch.py robot_ip:=<arm-ip>`.++## Disconnect++```python+arm.disconnect()+```
skills/xarm-twin/SKILL.mdadded+87@@ -0,0 +1,87 @@+---+name: xarm-twin+description: >-+ Use the UFACTORY xArm 6 digital twin from the xArm 6 wiki page: per-link GLBs+ (link_base..link6) plus kinematics.json in millimetres, Z-up, composed into a joint+ chain you can pose from live joint angles; forward kinematics in Python; mounting the+ twin on a plate or table in a larger scene; matching the twin to the real arm's SDK+ readings. Trigger words: xarm twin, xarm 6 glb, xarm digital twin, xarm 3d model, pose+ the xarm model, xarm forward kinematics, xarm fk, xarm kinematics.json, xarm urdf to glb,+ xarm in babylon, xarm in three.js, mount the xarm model, xarm base frame.+---++Parent page: **UFactory xArm 6 Robot Arm**++# The xArm 6 digital twin++## Files (page folder `models/`)++| File | Use |+|---|---|+| `link_base.glb`, `link1.glb` … `link6.glb` | one mesh per URDF link, in that link's own frame |+| `xarm6_home.glb` | whole arm at the zero pose, static preview only |+| `kinematics.json` | chain, joint origins, axes, limits, home pose, TCP |+| `LICENSE-xarm_ros2.txt` | BSD-3-Clause, keep it next to the meshes when you redistribute |++## Conventions++- Millimetres, +Z up. Vertex coordinates are the URDF metres × 1000.+- `links[]` is ordered base to tip. Each non-base link has a `joint` with `origin_xyz_mm`,+ `origin_rpy` (rad, URDF fixed-axis: R = Rz(yaw)·Ry(pitch)·Rx(roll)), `axis` (always+ [0, 0, 1]) and `limit_deg`.+- Child world pose = parent world × T(origin_xyz_mm) × R(origin_rpy) × Rz(q).+- Base frame origin: centre of the bottom face of the base. +X is where the arm reaches at+ zero; the cable connectors face −X. J2's axis is 267 mm up.+- TCP = `link6` origin = flange face, +Z out of the flange. Add the tool offset there.+- Zero pose: flange at (207, 0, 112) mm, pointing down. This equals the SDK's+ `get_position()` at joints all 0, so SDK joint angles drive the twin directly (deg → rad).++## Forward kinematics (Python)++`fk.py` in this folder:++```python+import json+from fk import fk+kin = json.load(open("models/kinematics.json"))+poses = fk(kin, [30, 10, -40, 0, 60, 0]) # degrees in, {link: 4x4 mm} out+print(poses["link6"][:3, 3])+```++Self-check: `fk(kin, [0]*6)["link6"]` translation must be (207, 0, 112). If it is not, the+RPY order or the axis is wrong.++## Building the chain in a 3D scene++For each link in order:++1. `origin` node, child of the parent link node: position = `origin_xyz_mm`, rotation =+ Rz(yaw)·Ry(pitch)·Rx(roll) as a quaternion.+2. `joint` node, child of `origin`: rotation about local Z = q.+3. Load the link GLB under the `joint` node with no extra transform.++glTF loaders (Babylon.js, three.js) wrap a file in a root node that converts glTF +Y-up into+their scene space. In a Z-up scene, cancel that root transform (or re-parent the mesh+children and drop the root), otherwise every link is rotated 90° about X.++To animate from the real arm: poll `get_servo_angle()` (or read the 30003 report at 100 Hz)+and set each `joint` node's Z rotation.++## Mounting the twin++- The base's bottom face is z = 0 in the arm frame. On a plate of thickness t, place the arm+ root at (plate centre x, plate centre y, plate top z) = (…, …, t + plate base z).+- Yaw the arm root so its +X points where the real arm reaches at zero. The cable side+ (−X) usually faces the back of the cell.+- Bolt pattern for the plate: 5 × M5 on a Ø110 circle at 0°, ±60°, ±120° from +X, none at 180°,+ plus Ø5 locating holes at (−10, ±55). See the page's "Base mounting" section.++## Limits for animation and planning++Use `limit_deg` from `kinematics.json` (the ROS 2 model ranges). Clamp or reject targets+outside them; the real controller will refuse them anyway.++## Attribution++Meshes and kinematics: UFACTORY `xarm_ros2`, `xarm_description` (`xarm6_1305` visual meshes,+`xarm6_default_kinematics.yaml`), commit 62936f7, BSD-3-Clause, Copyright (c) 2018 UFACTORY Inc.
skills/xarm-twin/fk.pyadded+34@@ -0,0 +1,34 @@+"""Forward kinematics for the UFACTORY xArm 6 twin (models/kinematics.json, mm, Z-up).++Usage: python3 fk.py models/kinematics.json [j1 j2 j3 j4 j5 j6] (degrees)+Kinematics data: UFACTORY xarm_ros2, BSD-3-Clause. This script: MIT, Adom.+"""+import json, math, sys+import numpy as np++def rpy(r, p, y):+ cr, sr, cp, sp, cy, sy = math.cos(r), math.sin(r), math.cos(p), math.sin(p), math.cos(y), math.sin(y)+ Rz = np.array([[cy, -sy, 0], [sy, cy, 0], [0, 0, 1]])+ Ry = np.array([[cp, 0, sp], [0, 1, 0], [-sp, 0, cp]])+ Rx = np.array([[1, 0, 0], [0, cr, -sr], [0, sr, cr]])+ return Rz @ Ry @ Rx++def fk(kin, q_deg):+ """Return {link_name: 4x4 world pose (mm)} for joint angles in degrees."""+ poses, qi = {}, 0+ for link in kin["links"]:+ if link["parent"] is None:+ poses[link["name"]] = np.eye(4); continue+ j = link["joint"]+ T = np.eye(4)+ T[:3, :3] = rpy(*j["origin_rpy"]); T[:3, 3] = j["origin_xyz_mm"]+ q = math.radians(q_deg[qi]); qi += 1+ Rq = np.eye(4); Rq[:3, :3] = rpy(0, 0, q) # every axis is +Z+ poses[link["name"]] = poses[link["parent"]] @ T @ Rq+ return poses++if __name__ == "__main__":+ kin = json.load(open(sys.argv[1]))+ q = [float(a) for a in sys.argv[2:8]] if len(sys.argv) >= 8 else kin["home_deg"]+ np.set_printoptions(precision=2, suppress=True)+ print(fk(kin, q)["link6"])
uninstall.shadded+4@@ -0,0 +1,4 @@+#!/usr/bin/env bash+set -e+rm -rf "$HOME/.claude/skills/xarm-control" "$HOME/.claude/skills/xarm-twin"+echo "OK: Removed xarm-control and xarm-twin."