SCS0009 Web Control
This page is the SCS0009 serial bus version-specific guide for web control. For the general interface, startup, preset poses and camera teleoperation, see the web control overview.
Servo mapping
Data source: DEFAULT_HAND_SERVOS in AmazingHandControl/hand_logic.py and docs/zh-hans/CONFIG_FORMAT.md.
| Finger | Right hand ID | Left hand ID |
|---|---|---|
| Ring | 5, 6 | 11, 12 |
| Middle | 3, 4 | 13, 14 |
| Index | 1, 2 | 15, 16 |
| Thumb | 7, 8 | 17, 18 |
Both servos drive one finger differentially ⚠️
Each finger's two servos are mounted side by side with their shafts on the same line. It is not "one servo for flexion and one for splay"; instead, the two servos drive the same finger together:
- The two servos rotate in the same direction → the finger splays left/right (abduction/adduction)
- The two servos rotate in opposite directions → the finger extends / bends (flexion/extension)
- The two servos carrying both same-direction and opposite-direction components → flexion and splay superimpose
Splay (same direction) Flexion (opposite direction)
Axis 1 ↻ Axis 2 ↻ Axis 1 ↻ Axis 2 ↺The interface shows two control axes, not two servos:
Flexion = (servo1 + servo2) / 2
Splay = (servo1 − servo2) / 2Conversely, the program converts the control axes into target angles for the two servos:
servo1 = flexion + splay
servo2 = flexion − splayHere
servo1/servo2are software target angles (even IDs have already been negated for reverse mounting); they are not the physical directions of the servo output shafts. To judge whether the two servos rotate in the same or opposite directions in the actual mechanism, look at the output shafts — do not compare these two values directly.Example: when only splay
+20is given, the software values areservo1 = +20,servo2 = −20; after negating the even ID, the two servos actually rotate in the same direction.
"The fingers won't spread / won't close" means the splay control value is zero, or the two servos are not forming a differential — a scissors-style V, for example, relies on index splay +17 and middle splay −30 (one positive, one negative).
Range (derived from the 19 official poses + middle):
| Axis | Range | Description |
|---|---|---|
| Flexion | −50 ~ +90 | open (negative) ↔ closed (positive) |
| Splay | −45 ~ +45 | together (negative) ↔ apart (positive) |
Two ID rules (getting them wrong burns servos or reverses motion):
- Odd IDs are each finger's first servo, even IDs its second. True on both hands (left 11/12, 13/14… also alternate odd/even). The second servo is mounted in reverse, so negate it in the conversion.
- The left hand's IDs are not 9–16 but 11–18, numbered "ring finger first". Wiring them as 1-8 in sequence is always wrong.
Physical direction and middle position: counting from right to left, the right servo is odd and the left servo even. Right-hand middle-position order (raw 0–1023) is 451,571,451,571,451,571,451,571; left hand 571,451,571,451,571,451,571,451.

Conversion formulas
Identical to apply_pose() in AmazingHandControl (verified pose by pose):
1) when mirror_pose is true, first swap the two servo values of each finger
2) servo_deg = (ID odd ? +1 : -1) × (pose value − angle_offset_deg)| Hand | angle_offset_deg | mirror_pose |
|---|---|---|
| Right | 0 | yes ⚠️ |
| Left | -35.16 | yes |
-35.16° is the physical half-slot offset of the left hand's servo horns relative to the right, measured from the official configuration.
About
mirror_pose— this build differs from the official oneIn the official reference only the left hand is
true. This build's right-hand module also has its servo pairs mounted reversed, so the right hand is set totrueas well.Mathematically, the swap only flips splay and never touches flexion:
base = (a+b)/2is unchanged by a swap, whileside = (a−b)/2changes sign. So when "one hand's splay is entirely reversed", this switch is the first thing to check.
The pose array is always 8 values in the order [ring, middle, index, thumb], two values per finger — byte-identical to the official data/hand_config.yaml, for easy comparison.
Worked conversion examples
| Pose | Right hand | Left hand |
|---|---|---|
open [-35]*8 | ID5 −35 / ID6 +35 | ID11 +0.16 / ID12 −0.16 |
close [75]*8 | ID5 +75 / ID6 −75 | ID11 +110.16 / ID12 −110.16 |
middle [-17.578]*8 | ID5 −17.58 / ID6 +17.58 | ID11 +17.58 / ID12 −17.58 |
Note that the left hand's
closeneeds ±110.16°, noticeably more than the right hand. That is why the limit inconfig.jsonis set to ±120° (the SCS0009 full range is about ±150°). Setting the limit to ±90 would clip the left hand's fist.
SCS0009 FAQ
Motion direction reversed → check that the wiring follows "odd ID first"; for the left hand, also confirm angle_offset_deg = -35.16.
Left hand's fist is not tight enough / servos buzz → the limit may still be ±90; check whether config.json has been raised to ±120.
Fingers do not move / "expected N motors, found M" → run tools/scan_servos.py; the left hand's IDs are the easiest to wire incorrectly.
Middle position incorrect → the middle value -17.578125 comes from converting the official raw 451/571; to calibrate for your own mechanical assembly, edit middle.positions in config.json.

