First Reach: Cartesian Control
Tell the gripper where to go instead of what angle each joint should hold. This is the moment the robot stops being six servos and starts being an arm.
Outcome
Walk the gripper to a physical object on your desk using coordinates rather than joint angles, confirming that the whole command-to-servo pipeline works on your hardware.
What you will learn
- Joint space vs. Cartesian space
- What an inverse-kinematics solver is for
- How to tell a software error from a mechanical one
Prerequisites
- Software setup finished through step 9.
- One arm's motors have unique IDs and the arm is calibrated.
- Every motor answers
diagnostics/check_bus.py. - The workspace in front of the arm is clear.
What Inverse Kinematics Actually Does
Up to now you have moved the robot the way the motors think: shoulder to 30°, elbow to −45°. That is joint space. It works, but it is a terrible way to describe "touch that cup" — you would have to solve the geometry in your head.
Inverse kinematics flips the problem around. You name a point in space and the solver works backwards to the joint angles that put the gripper there. That is Cartesian space, and it is how every useful manipulation task is expressed.
This robot has two IK solvers, and they are for different jobs
| Solver | How it works | Used by |
|---|---|---|
| Closed-form 2-link this tutorial |
Law of cosines on the shoulder-lift and elbow pair. One equation, instant answer, no iteration. Only solves the arm's vertical plane. | 1_so100_keyboard_ee_control.py |
| Pink (Pinocchio) the autonomous demos |
Differential IK over the full arm model — nudges all joints toward the target a step at a time, handling orientation and singularities. | xlerobot_pro.IK_SO101, used across examples/vision/ |
You are starting with the simple one on purpose. It has almost nothing that can go wrong, so anything that does go wrong here is your hardware — which is exactly what this tutorial is for.
Instructions
1. Set the terminal up
Activate the environment and move to the repository root.
$ cd ~/xlerobot-pro
Permission denied: /dev/ttyACM0 means you are not in the dialout group — or you added yourself and never logged back out. Run sudo usermod -aG dialout $USER, then log out and in. sudo chmod 666 /dev/ttyACM* also works, but only until the next reboot.
2. Launch the script
It asks you two questions before anything moves.
| Prompt | What to answer |
|---|---|
| "Please enter the USB port…" | Full 2-bus robot: /dev/xle_armsBenchtop rig: /dev/ttyACM0, or just press Enter for that default |
| "Do you want to recalibrate the robot? (y/n)" | n — you already calibrated in software setup. Answering y throws that away and starts over. |
/dev/xle_arms moves the left arm and never the right — the right arm lives at IDs 7–12, which needs a different motor map, not just a different port. For two-armed work use the whole-robot teleop instead.
src/xlerobot_pro/firmware_limits.py, but a clamped arm still hits hard enough to hurt.
3. Learn the keys
Three of these pairs are Cartesian — they name a point and let IK find the angles. The rest are direct joint control, because a 2-link solver cannot express them.
| Keys | What moves | Kind |
|---|---|---|
| W / S | End-effector X — reach in and out | Cartesian (drives joints 2+3) |
| E / D | End-effector Y — height in the arm's vertical plane | Cartesian (drives joints 2+3) |
| Q / A | shoulder_pan — swings the whole arm left and right | Direct joint |
| R / F | Pitch adjustment — tilts wrist_flex | Direct joint |
| T / G | wrist_roll | Direct joint |
| Y / H | gripper — close and open | Direct joint |
| X | Exit — returns to the start position first | — |
| Esc | Exit immediately | — |
Update x coordinate: …. One tap tells you which way that key sends the gripper on your build, and each step is only 4 mm, so a wrong guess costs nothing. Holding a key queues a long move that is hard to stop cleanly.
4. Reach a physical target
Place a small object — a cup or a bottle — roughly 15–20 cm in front of the arm base. Then work in this order, which is easier than it sounds once you try it:
- Q / A to swing the arm until it is pointing at the object.
- E / D to set the height.
- W / S to reach out until the gripper is around it.
- Y / H to close the gripper.
Aiming with pan first and reaching last keeps the gripper away from the object while you are still finding the range.
Expected Output & Validation
The gripper tracks the coordinate you set, smoothly — the script runs a P-control loop toward the target rather than snapping. Accuracy within 1–2 cm is normal for a well-built arm.
Every keypress prints the updated coordinate or joint target. Those lines should appear on every press — even when the arm physically cannot get there.
Troubleshooting
Arm jerks or moves wildly. Almost always a motor ID mismatch or a servo horn installed a tooth off. Recheck Motor Configuration.
Coordinates update but nothing moves. The solver is running and the bus is not. Most often the motors have logic power but no motor power — a 5 V rail instead of 12 V. Confirm with python diagnostics/check_bus.py --motors arm.
Straight-line motion, but it consistently misses in one direction. That is calibration, not IK. Re-run calibration and check the left/right symmetry table in the Build Guide.
It will not reach a point you are sure is reachable. A joint's calibrated range is probably narrower than its real travel, because it was not swept all the way to both end stops during calibration.
The arm drops when you quit. Torque was cut rather than bled off. Quit with X so it returns to start first, and never cut torque on a loaded arm.
The bus dies partway through a long move. A cable is being tugged at a joint's travel extreme. Add a service loop at that joint — it will otherwise recur mid-grasp, under power.
Reaching by hand works. Let the camera pick the target next.
Autonomous Cube Grasp ↗