Servos
The six joints use Feetech STS bus servos with the standard STS register map. The full register dump is in the register map.
Identity
Section titled “Identity”| Joints | Model number (registers 3–4) | Firmware | Supply (measured) |
|---|---|---|---|
| J1–J3 | 0x0809 (2057) |
3.9 | ~7.6 V |
| J4 | 0x0709 (1801) |
3.9 | ~6.5 V |
| J5–J6 | 0x0209 (521) |
3.9 | ~6.4 V |
The common STS3215 reports 0x0309 and the STS3250 0x0B09. These three model
numbers are not in any public table found so far. They can be custom versions made
for Elephant Robotics.
Replacement servos
Section titled “Replacement servos”The servos are Feetech STS-series bus servos: they use Feetech’s protocol and register map, and register 3 is 9 as on other STS models. There are three types. Their settings are also different:
| Type | Joints | Model | Max voltage (14) | Min voltage (15) | Phase (18) | Protection current (28–29) |
|---|---|---|---|---|---|---|
| A | J1, J2, J3 | 0x0809 |
13.0 V (J1, J2), 24.0 V (J3) | 6.0 V | 4 | 300 |
| B | J4 | 0x0709 |
24.0 V | 4.0 V | 100 | 1000 |
| C | J5, J6 | 0x0209 |
24.0 V | 4.0 V | 100 | 1000 |
The Feetech product name of each type is not known yet. To find it:
- Read the label on the servo. This needs the joint covers off.
- Connect Feetech’s FD software (Windows) through the FT232R at 1 Mbaud. It reads the model number and shows the name.
- Ask Elephant Robotics for a replacement servo for that joint and give the model number. Elephant sells parts for the arm (not checked for these servos).
A replacement must report the same model number. Before you use it, set its ID,
the PID gains (21–23) and the other settings in the
register map for that joint, and
calibrate its zero (stock command SET_SERVO_CALIBRATION, 0x54, or the offset
in 31–32).
Units and encodings
Section titled “Units and encodings”- Multi-byte values are little-endian.
- Position: 12-bit, 4096 steps per turn (0.088° per step).
- Speed: steps/s, sign-magnitude with bit 15 as the sign. The present speed is quantised to 50 steps/s (4.4°/s).
- Load: 0.1 % units, bit 10 as the sign. It behaves like PWM duty, not torque: on J1 it is ~0.53 % per °/s plus ~3 % static friction.
- Voltage: 0.1 V. Temperature: °C.
- Calibration offset (registers 31–32): sign-magnitude, bit 11 as the sign.
Register 33 sets the mode.
| Mode | Command register | Status |
|---|---|---|
| 0 — position (default) | Goal position, 42–43 | Used for all the results. |
| 1 — velocity | Goal speed, 46–47 | Tested on J1: 102–103 steps/s for 100 commanded, with ~0.25 s of dead time at that speed. No joint limits. |
| 2 — PWM (open loop) | Goal time / PWM, 44–45 | Not tested. The closest mode to torque control. |
| 3 — step | relative position | Not used. |
Multi-turn (J6)
Section titled “Multi-turn (J6)”The STS servos can read and move past one turn (tested on J6 on 2026-10-06, through the ATOM’s REG_READ and REG_WRITE, with J6 turned by hand and then moved):
| Setting | Present position (56–57) | Goal position (42–43) |
|---|---|---|
| As shipped: phase 100, angle limits 0/4095 | One turn: wraps at 0/4095 (±180°) | Clamped to 0–4095: J6 stopped at −179.3° for a −190° goal |
| Phase bit 4 set (100 → 116), angle limits 0/4095 | Past one turn: read −231.8° (4685) | Clamped as above |
| Phase bit 4 set, angle limits 0/0 | Past one turn | Past one turn: J6 moved to −209.3° and back across ±180° the correct way |
- Values past one turn are sign-magnitude: bit 15 is the sign.
- The EEPROM lock (55) stays 1, so these settings last until the servo’s next power-off.
Our firmware (4.5+) writes them at each power-up for the joints marked
multi_turninservos.yaml(J6). - After a power-up the servo counts from its one-turn reading. The firmware finds the
turn from the J6 limits: they span one turn (−225° to +135°), so a reading above
+135° is one turn lower. It keeps a
turn_offsetfor the joint. Within 1.5° of ±135° the turn is not known: J6 goes limp and the ATOM stays in the error state until a HOLD finds the turn (the status log saysTURN UNKNOWN). - The positions in STATE, STREAM and plans go past 0–4095 on J6: −225° is step 4608.
- This also explains an older problem: after many turns by hand, J6 turned away from its goal when motion was enabled (see Known problems). The servo counts turns inside, even when it reports one turn only.
How goals and torque behave
Section titled “How goals and torque behave”These rules come from measurements. They are also in Known problems and rules.
- Goal position uses the same units as present position. To hold a joint, write goal = present.
- Goal speed 0 means “do not move” in position mode. A nonzero goal speed is a speed cap.
- Goal position and goal speed read 0 after power-up. Set every goal to the present position before you set a nonzero speed.
- Writing a goal position turns torque on. Writing the mode (register 33) turns it off.
- In velocity mode, the present position is raw (without the calibration offset). It looks like a jump of the offset size when the mode changes. The joint does not move.
- Register 55 (EEPROM lock) is 1. Changes to the EEPROM area (for example the mode) are probably lost at power-off. This is not verified.
Response in position mode
Section titled “Response in position mode”The servos follow a stream of goals with a repeatable lag and smoothing. Fitted on the sine recordings (see Servo response):
| Joint | Delay | Time constant | Pure-delay fit | PID (21/22/23) |
|---|---|---|---|---|
| J1 | 40 ms | 80 ms | 120 ms | 32 / 8 / 0 |
| J2 | 88 ms | 25 ms | 114 ms | 32 / 8 / 0 |
| J3 | 44 ms | 75 ms | 118 ms | 10 / 0 / 1 |
| J4 | 4 ms | 50 ms | 54 ms | 10 / 0 / 1 |
| J5 | 0 ms | 40 ms | 40 ms | 10 / 0 / 1 |
| J6 | 4 ms | 25 ms | 28 ms | 10 / 0 / 1 |
The joints also stick for 0.2–0.5 s after a reversal (static friction). The dead zone (registers 26/27) is 3 steps on every joint.
The PID column was read on 2026-10-03 with the stock ATOM firmware. See the next section for the gains in use now.
Position-loop gains
Section titled “Position-loop gains”Registers 21, 22, 23 are P, D, I of the servo’s position loop (Elephant’s documentation names 22 and 23 the other way round; see Stock protocol commands).
| Joint | Ours (MyCobot.GAINS, written at power-up by the controller firmware v3+) |
Stored in the servos | Stock ATOM firmware writes |
|---|---|---|---|
| J1 | 32 / 4 / 16 | 32 / 8 / 0 | 32 / 8 / 0 |
| J2 | 32 / 4 / 16 | 32 / 8 / 0 | 32 / 8 / 0 |
| J3 | 32 / 4 / 16 | 32 / 8 / 0 | 10 / 0 / 1 |
| J4 | 32 / 8 / 0 | 32 / 8 / 0 | 10 / 0 / 1 |
| J5 | 32 / 8 / 0 | 32 / 8 / 0 | 10 / 0 / 1 |
| J6 | 32 / 8 / 0 | 32 / 8 / 0 | 10 / 0 / 1 |
- Ours adds integral action on J1–J3. On the circle it halves the flange error (5.3 → 2.6 mm) with almost the same end-effector vibration. See Servo dynamics.
- Stored: what the servos use if nothing writes the gains (read on 2026-10-04 with the controller firmware v2, which did not write them).
- Stock: read on 2026-10-03 with Elephant’s ATOM firmware, which writes these at power-up.
- With the EEPROM lock (register 55) at 1, written gains last until the next power
cycle. The controller firmware writes ours again at each power-up (
gains_okin PING). - Change them for a test with
scripts/set_gains.jl ours|stored|stock --atom=IP.