Controlling the Nero Robotic Arm with OpenClaw
As a popular open-source project, OpenClaw has become a highlight in the robotic arm control field with its intuitive operation and strong adaptability. It enables full end-to-end linkage between AI commands and device execution, greatly lowering the barrier for robotic arm control. This article focuses on practical implementation. Combined with the pyAgxArm SDK, we will guide you through the download, installation, and configuration of OpenClaw to achieve efficient control of the NERO 7-axis robotic arm.
Download and Install OpenClaw
- Open the OpenClaw official website: https://openclaw.ai/
- Locate the
Quick Starttab and execute the one-click installation script
Start Configuring OpenClaw
- It is recommended to select the QWEN model
- Select all hooks
- Open the web interface
Teach OpenClaw the Skill and Rules for Controlling the Robotic Arm
-
Create an
agx_arm_codegendirectory in the skill folder, then create the following skill files: -
SKILLS.md
---
name: agx-arm-codegen
description: Guide OpenClaw to generate pyAgxArm-based robotic arm control code from user natural language. When users describe robotic arm movements with prompts and existing scripts cannot directly meet the requirements, automatically organize and generate executable Python scripts based on the APIs and examples provided by this skill.
metadata:
{
"openclaw":
{
"emoji": "烙",
"requires": { "bins": ["python3", "pip3"] },
},
}
---
## Function Overview
- This skill is used to **guide OpenClaw to generate** executable pyAgxArm control code (Python scripts) based on user natural language descriptions, rather than just calling existing CLIs.
- Reference SDK: pyAgxArm ([GitHub](https://github.com/agilexrobotics/pyAgxArm)); Reference example: `pyAgxArm/demos/nero/test1.py`.
## When to Use This Skill
- Users say "Write code to control the robotic arm", "Generate a control script based on my description", "Make the robotic arm perform multiple actions in sequence", etc.
- Users explicitly request to "generate Python code" or "provide a runnable script" to control AgileX robotic arms such as Nero/Piper.
## Generate Code Using This Skill
- Based on user prompts, combine the APIs and templates in `references/pyagxarm-api.md` of this skill to generate a complete, runnable Python script.
- After generation, explain: the script needs to run in an environment with pyAgxArm and python-can installed, and CAN must be activated and the robotic arm powered on; remind users to pay attention to safety (no one in the workspace, small-scale testing first is recommended).
## Rules for Generating Code
1. **Connection and Configuration**
- Use `create_agx_arm_config(robot="nero", comm="can", channel="can0", interface="socketcan")` to create a configuration (Nero example; Piper can use `robot="piper"`).
- Use `AgxArmFactory.create_arm(robot_cfg)` to create a robotic arm instance, then `robot.connect()` to establish a connection.
2. **Enabling and Pre-Motion**
- CRITICAL: The robot MUST BE ENABLED before switching modes. If the robot is in a disabled state, you cannot switch modes.
- Switch to normal mode before movement, then enable: `robot.set_normal_mode()`, then poll `robot.enable()` until successful; you can set `robot.set_speed_percent(100)`.
- Motion modes: Whenever using move_* or needing to switch to * mode, explicitly set `robot.set_motion_mode(robot.MOTION_MODE.J)` (Joint), `P` (Point-to-Point), `L` (Linear), `C` (Circular), `JS` (Joint Quick Response, use with caution).
3. **Motion Interfaces and Units**
- Joint motion: `robot.move_j([j1, j2, ..., j7])`, unit is **radians**, Nero has 7 joints.
- Cartesian: `robot.move_p(pose)` / `robot.move_l(pose)`, pose is `[x, y, z, roll, pitch, yaw]`, position unit is **meters**, attitude is **radians**.
- Circular: `robot.move_c(start_pose, mid_pose, end_pose)`, each pose is 6 floating-point numbers.
- CRITICAL: All movement commands (move_j, move_js, move_mit, move_c, move_l, move_p) must be used in normal mode
- After motion completion, poll `robot.get_arm_status().msg.motion_status == 0` or encapsulate `wait_motion_done(robot, timeout=...)` before executing the next step.
4. **Mode Switching**
- Switching modes (master, slave, normal) requires 1s delay before and after the mode switch
- Use `robot.set_normal_mode()` to set normal mode
- Use `robot.set_master_mode()` to set master mode
- Use `robot.set_slave_mode()` to set slave mode
- CRITICAL: Enable the robot FIRST with `robot.enable()` BEFORE switching modes
5. **Safety and Conclusion**
- In the generated script, note: confirm workspace safety before execution; small-scale movement is recommended for the first time; use physical emergency stop or `robot.electronic_emergency_stop()` / `robot.disable()` in case of emergency.
- If the user requests "disable after completion", call `robot.disable()` at the end of the script.
6. **Implementation Details**
- When waiting for motion to complete, use shorter timeout (2-3 seconds)
- After each mechanical arm operation, add a small sleep (0.01 seconds)
- Motion completion detection: `robot.get_arm_status().msg.motion_status == 0` (not == 1)
## Reference Files
- **API and Minimal Runnable Template**: `references/pyagxarm-api.md`
When generating code, refer to the interfaces and code snippets in this file to ensure consistency with pyAgxArm and test1.py usage.
## Safety Notes
- The generated code will drive a physical robotic arm. Users must be reminded: confirm no personnel or obstacles in the workspace before execution; it is recommended to test with small movements and low speeds first.
- High-risk modes (such as `move_js`, `move_mit`) should be marked with risks in code comments or user explanations, and it is recommended to use them only after understanding the consequences.
- This skill is only responsible for "guiding code generation" and does not directly execute movements; users need to prepare the actual running environment, CAN activation, and pyAgxArm installation by themselves (refer to environment preparation in the agx-arm skill).
- pyagxarm-api.md
# pyAgxArm API Quick Reference & Minimal Runnable Template
For reference when OpenClaw generates robotic arm control code from user natural language. SDK source: pyAgxArm ([GitHub](https://github.com/agilexrobotics/pyAgxArm)); Example reference: `pyAgxArm/demos/nero/test1.py`.
## 1. Connection and Configuration
```python
from pyAgxArm import create_agx_arm_config, AgxArmFactory
# Configuration: robot options - nero / piper / piper_h / piper_l / piper_x; channel e.g. can0
robot_cfg = create_agx_arm_config(
robot="nero",
comm="can",
channel="can0",
interface="socketcan",
)
robot = AgxArmFactory.create_arm(robot_cfg)
robot.connect()
create_agx_arm_config(robot, comm="can", channel="can0", interface="socketcan", **kwargs): Create configuration dictionary; CAN-related parameters are passed via kwargs (e.g. channel, interface).AgxArmFactory.create_arm(config): Return robotic arm driver instance.robot.connect(): Establish CAN connection and start reading thread.
2. Enabling and Modes
robot.set_normal_mode() # Normal mode (single arm control)
# Enable: poll until successful
while not robot.enable():
time.sleep(0.01)
robot.set_speed_percent(100) # Motion speed percentage 0–100
# Disable
while not robot.disable():
time.sleep(0.01)
- Master/Slave modes (Nero/Piper, etc.):
robot.set_master_mode()(zero-force teaching),robot.set_slave_mode()(follow master arm).
3. Motion Modes and Interfaces
| Mode | Constant | Interface | Description |
|---|---|---|---|
| Joint Position Speed | robot.MOTION_MODE.J |
robot.move_j([j1..j7]) |
7 joint angles (radians), with smoothing |
| Joint Quick Response | robot.MOTION_MODE.JS |
robot.move_js([j1..j7]) |
No smoothing, use with caution |
| Point-to-Point | robot.MOTION_MODE.P |
robot.move_p([x,y,z,roll,pitch,yaw]) |
Cartesian pose, meters/radians |
| Linear | robot.MOTION_MODE.L |
robot.move_l([x,y,z,roll,pitch,yaw]) |
Linear trajectory |
| Circular | robot.MOTION_MODE.C |
robot.move_c(start_pose, mid_pose, end_pose) |
Each pose is 6 floating-point numbers |
- Units: Joint angles are in radians; Cartesian pose is in meters (x,y,z) and radians (roll, pitch, yaw).
- Nero has 7 joints; Piper has 6 joints, the number of parameters for
move_j/move_jsmust match the model.
Example (Joint Motion + Wait for Completion):
import time
def wait_motion_done(robot, timeout: float = 3.0, poll_interval: float = 0.1) -> bool: # Shorter timeout (2-3s)
time.sleep(0.5)
start_t = time.monotonic()
while True:
status = robot.get_arm_status()
if status is not None and getattr(status.msg, "motion_status", None) == 0:
return True
if time.monotonic() - start_t > timeout:
return False
time.sleep(poll_interval)
robot.set_motion_mode(robot.MOTION_MODE.J)
robot.move_j([0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
wait_motion_done(robot, timeout=3.0) # Shorter timeout
4. Read Status
robot.get_joint_angles(): Current joint angles (return value is array when with.msgattribute).robot.get_flange_pose(): Current flange pose[x, y, z, roll, pitch, yaw].robot.get_arm_status(): Motion status, etc.;status.msg.motion_status == 0indicates motion completion.- Note: Detect motion completion with
robot.get_arm_status().msg.motion_status == 0(not == 1)
5. Others
- Homing:
robot.move_j([0] * 7)(7 joints for Nero). - Emergency Stop:
robot.electronic_emergency_stop(); recovery requiresrobot.reset(). - MIT Impedance/Torque Control (Advanced):
robot.set_motion_mode(robot.MOTION_MODE.MIT),robot.move_mit(joint_index, p_des, v_des, kp, kd, t_ff), parameter ranges refer to SDK, use with caution.
6. Minimal Runnable Template (Extend based on this when generating code)
#!/usr/bin/env python3
import time
from pyAgxArm import create_agx_arm_config, AgxArmFactory
def wait_motion_done(robot, timeout: float = 3.0, poll_interval: float = 0.1) -> bool: # Shorter timeout (2-3s)
time.sleep(0.5)
start_t = time.monotonic()
while True:
status = robot.get_arm_status()
if status is not None and getattr(status.msg, "motion_status", None) == 0:
return True
if time.monotonic() - start_t > timeout:
return False
time.sleep(poll_interval)
def main():
robot_cfg = create_agx_arm_config(
robot="nero",
comm="can",
channel="can0",
interface="socketcan",
)
robot = AgxArmFactory.create_arm(robot_cfg)
robot.connect()
# Mode switching requires 1s delay before and after
time.sleep(1) # 1s delay before mode switch
robot.set_normal_mode()
time.sleep(1) # 1s delay after mode switch
# CRITICAL: The robot MUST BE ENABLED before switching modes
while not robot.enable():
time.sleep(0.01)
robot.set_speed_percent(80)
# After each mechanical arm operation, add a small sleep (0.01 seconds)
# CRITICAL: All movement commands must be used in normal mode
robot.set_motion_mode(robot.MOTION_MODE.J)
robot.move_j([0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
time.sleep(0.01) # Small delay after move command
wait_motion_done(robot, timeout=3.0) # Shorter timeout
# Optional: Disable before exit
# while not robot.disable():
# time.sleep(0.01)
if __name__ == "__main__":
main()
When generating code, replace or add motion steps (move_j / move_p / move_l / move_c, etc.) according to user descriptions, and keep consistency in connection, enabling, wait_motion_done and units (radians/meters).
- Next, teach OpenClaw this skill








