# Assemble SO101

TODO(pepijn): Create element interactive that shows steps + assembly video and people can click next step which loads next video. Progress is shown in top with progress bar?

## Source the parts

Follow this [README](https://github.com/TheRobotStudio/SO-ARM100). It contains the bill of materials, with a link to source the parts, as well as the instructions to 3D print the parts,
and advice if it's your first time printing or if you don't own a 3D printer.

Before assembling, you will first need to configure your motors. To this end, we provide a nice script, so let's first install LeRobot. After configuration, we will also guide you through assembly.

## Install LeRobot

Follow the [Installation Guide](./installation)

## Configure the motors

Designate one bus servo adapter and 6 motors for your leader arm, and similarly the other bus servo adapter and 6 motors for the follower arm. It's convenient to label them and write on each motor if it's for the follower `F` or for the leader `L` and it's ID from 1 to 6 (F1...F6 and L1...L6).

### Find the USB ports associated to each arm

To find the port for each bus servo adapter, run the utility script:
```bash
python lerobot/scripts/find_motors_bus_port.py
```
#### Example outputs

Example output when identifying the leader arm's port (e.g., `/dev/tty.usbmodem575E0031751` on Mac, or possibly `/dev/ttyACM0` on Linux):
```
Finding all available ports for the MotorBus.
['/dev/tty.usbmodem575E0032081', '/dev/tty.usbmodem575E0031751']
Remove the usb cable from your MotorsBus and press Enter when done.

[...Disconnect leader arm and press Enter...]

The port of this MotorsBus is /dev/tty.usbmodem575E0031751
Reconnect the usb cable.
```
Example output when identifying the follower arm's port (e.g., `/dev/tty.usbmodem575E0032081`, or possibly `/dev/ttyACM1` on Linux):
```
Finding all available ports for the MotorBus.
['/dev/tty.usbmodem575E0032081', '/dev/tty.usbmodem575E0031751']
Remove the usb cable from your MotorsBus and press Enter when done.

[...Disconnect follower arm and press Enter...]

The port of this MotorsBus is /dev/tty.usbmodem575E0032081
Reconnect the usb cable.
```

#### Troubleshooting
On Linux, you might need to give access to the USB ports by running:
```bash
sudo chmod 666 /dev/ttyACM0
sudo chmod 666 /dev/ttyACM1
```

#### Update config file

IMPORTANTLY: Now that you have your ports, update the **port** default values of [`SO100RobotConfig`](../lerobot/common/robot_devices/robots/configs.py). You will find something like:
```python
@RobotConfig.register_subclass("so100")
@dataclass
class So100RobotConfig(ManipulatorRobotConfig):
    calibration_dir: str = ".cache/calibration/so100"
    # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
    # Set this to a positive scalar to have the same value for all motors, or a list that is the same length as
    # the number of motors in your follower arms.
    max_relative_target: int | None = None

    leader_arms: dict[str, MotorsBusConfig] = field(
        default_factory=lambda: {
            "main": FeetechMotorsBusConfig(
                port="/dev/tty.usbmodem58760431091",  <-- UPDATE HERE
                motors={
                    # name: (index, model)
                    "shoulder_pan": [1, "sts3215"],
                    "shoulder_lift": [2, "sts3215"],
                    "elbow_flex": [3, "sts3215"],
                    "wrist_flex": [4, "sts3215"],
                    "wrist_roll": [5, "sts3215"],
                    "gripper": [6, "sts3215"],
                },
            ),
        }
    )

    follower_arms: dict[str, MotorsBusConfig] = field(
        default_factory=lambda: {
            "main": FeetechMotorsBusConfig(
                port="/dev/tty.usbmodem585A0076891",  <-- UPDATE HERE
                motors={
                    # name: (index, model)
                    "shoulder_pan": [1, "sts3215"],
                    "shoulder_lift": [2, "sts3215"],
                    "elbow_flex": [3, "sts3215"],
                    "wrist_flex": [4, "sts3215"],
                    "wrist_roll": [5, "sts3215"],
                    "gripper": [6, "sts3215"],
                },
            ),
        }
    )
```

### Set IDs for all 12 motors

TODO(pepijn): add video instruction for setting motor ID

Plug your first motor F1 and run this script to set its ID to 1. It will also set its present position to 2048, so expect your motor to rotate. Replace the text after --port to the corresponding follower control board port and run this command in cmd:
```bash
python lerobot/scripts/configure_motor.py \
  --port /dev/tty.usbmodem58760432961 \
  --brand feetech \
  --model sts3215 \
  --baudrate 1000000 \
  --ID 1
```

Then unplug your motor and plug the second motor and set its ID to 2.
```bash
python lerobot/scripts/configure_motor.py \
  --port /dev/tty.usbmodem58760432961 \
  --brand feetech \
  --model sts3215 \
  --baudrate 1000000 \
  --ID 2
```

Redo the process for all your motors until ID 6. Do the same for the 6 motors of the leader arm.

## Step-by-Step Assembly Instructions

### Clean Parts
Remove all support material from the 3D-printed parts.

### First Motor

**Step 2: Insert Wires**
- Insert two wires into the first motor.

  <img src="../media/tutorial/img1.jpg" style="height:300px;">

**Step 3: Install in Base**
- Place the first motor into the base.

  <img src="../media/tutorial/img2.jpg" style="height:300px;">

**Step 4: Secure Motor**
- Fasten the motor with 4 screws. Two from the bottom and two from top.

**Step 5: Attach Motor Holder**
- Slide over the first motor holder and fasten it using two screws (one on each side).

  <img src="../media/tutorial/img4.jpg" style="height:300px;">

**Step 6: Attach Motor Horns**
- Install both motor horns, securing the top horn with a screw. Try not to move the motor position when attaching the motor horn, especially for the leader arms, where we removed the gears.

  <img src="../media/tutorial/img5.jpg" style="height:300px;">
<details>
  <summary><strong>Video adding motor horn</strong></summary>
  <video src="https://github.com/user-attachments/assets/ef3391a4-ad05-4100-b2bd-1699bf86c969"></video>
</details>

**Step 7: Attach Shoulder Part**
- Route one wire to the back of the robot and the other to the left or in photo towards you (see photo).
- Attach the shoulder part.

  <img src="../media/tutorial/img6.jpg" style="height:300px;">

**Step 8: Secure Shoulder**
- Tighten the shoulder part with 4 screws on top and 4 on the bottom
*(access bottom holes by turning the shoulder).*

---

### Second Motor Assembly

**Step 9: Install Motor 2**
- Slide the second motor in from the top and link the wire from motor 1 to motor 2.

  <img src="../media/tutorial/img8.jpg" style="height:300px;">

**Step 10: Attach Shoulder Holder**
- Add the shoulder motor holder.
- Ensure the wire from motor 1 to motor 2 goes behind the holder while the other wire is routed upward (see photo).
- This part can be tight to assemble, you can use a workbench like the image or a similar setup to push the part around the motor.

  <div style="display: flex;">
    <img src="../media/tutorial/img9.jpg" style="height:250px;">
    <img src="../media/tutorial/img10.jpg" style="height:250px;">
    <img src="../media/tutorial/img12.jpg" style="height:250px;">
  </div>

**Step 11: Secure Motor 2**
- Fasten the second motor with 4 screws.

**Step 12: Attach Motor Horn**
- Attach both motor horns to motor 2, again use the horn screw.

**Step 13: Attach Base**
- Install the base attachment using 2 screws.

  <img src="../media/tutorial/img11.jpg" style="height:300px;">

**Step 14: Attach Upper Arm**
- Attach the upper arm with 4 screws on each side.

  <img src="../media/tutorial/img13.jpg" style="height:300px;">

---

### Third Motor Assembly

**Step 15: Install Motor 3**
- Route the motor cable from motor 2 through the cable holder to motor 3, then secure motor 3 with 4 screws.

**Step 16: Attach Motor Horn**
- Attach both motor horns to motor 3 and secure one again with a horn screw.

  <img src="../media/tutorial/img14.jpg" style="height:300px;">

**Step 17: Attach Forearm**
- Connect the forearm to motor 3 using 4 screws on each side.

  <img src="../media/tutorial/img15.jpg" style="height:300px;">

---

### Fourth Motor Assembly

**Step 18: Install Motor 4**
- Slide in motor 4, attach the cable from motor 3, and secure the cable in its holder with a screw.

  <div style="display: flex;">
    <img src="../media/tutorial/img16.jpg" style="height:300px;">
    <img src="../media/tutorial/img19.jpg" style="height:300px;">
  </div>

**Step 19: Attach Motor Holder 4**
- Install the fourth motor holder (a tight fit). Ensure one wire is routed upward and the wire from motor 3 is routed downward (see photo).

  <img src="../media/tutorial/img17.jpg" style="height:300px;">

**Step 20: Secure Motor 4 & Attach Horn**
- Fasten motor 4 with 4 screws and attach its motor horns, use for one a horn screw.

  <img src="../media/tutorial/img18.jpg" style="height:300px;">

---

### Wrist Assembly

**Step 21: Install Motor 5**
- Insert motor 5 into the wrist holder and secure it with 2 front screws.

  <img src="../media/tutorial/img20.jpg" style="height:300px;">

**Step 22: Attach Wrist**
- Connect the wire from motor 4 to motor 5. And already insert the other wire for the gripper.
- Secure the wrist to motor 4 using 4 screws on both sides.

  <img src="../media/tutorial/img22.jpg" style="height:300px;">

**Step 23: Attach Wrist Horn**
- Install only one motor horn on the wrist motor and secure it with a horn screw.

  <img src="../media/tutorial/img23.jpg" style="height:300px;">

---

### Follower Configuration

**Step 24: Attach Gripper**
- Attach the gripper to motor 5.

  <img src="../media/tutorial/img24.jpg" style="height:300px;">

**Step 25: Install Gripper Motor**
- Insert the gripper motor, connect the motor wire from motor 5 to motor 6, and secure it with 3 screws on each side.

  <img src="../media/tutorial/img25.jpg" style="height:300px;">

**Step 26: Attach Gripper Horn & Claw**
- Attach the motor horns and again use a horn screw.
- Install the gripper claw and secure it with 4 screws on both sides.

  <img src="../media/tutorial/img26.jpg" style="height:300px;">

**Step 27: Mount Controller**
- Attach the motor controller on the back.

  <div style="display: flex;">
    <img src="../media/tutorial/img27.jpg" style="height:300px;">
    <img src="../media/tutorial/img28.jpg" style="height:300px;">
  </div>

*Assembly complete – proceed to Leader arm assembly.*

---

### Leader Configuration

For the leader configuration, perform **Steps 1–23**. Make sure that you removed the motor gears from the motors.

**Step 24: Attach Leader Holder**
- Mount the leader holder onto the wrist and secure it with a screw.

  <img src="../media/tutorial/img29.jpg" style="height:300px;">

**Step 25: Attach Handle**
- Attach the handle to motor 5 using 4 screws.

  <img src="../media/tutorial/img30.jpg" style="height:300px;">

**Step 26: Install Gripper Motor**
- Insert the gripper motor, secure it with 3 screws on each side, attach a motor horn using a horn screw, and connect the motor wire.

  <img src="../media/tutorial/img31.jpg" style="height:300px;">

**Step 27: Attach Trigger**
- Attach the follower trigger with 4 screws.

  <img src="../media/tutorial/img32.jpg" style="height:300px;">

**Step 28: Mount Controller**
- Attach the motor controller on the back.

  <div style="display: flex;">
    <img src="../media/tutorial/img27.jpg" style="height:300px;">
    <img src="../media/tutorial/img28.jpg" style="height:300px;">
  </div>


## Calibrate

Next, you'll need to calibrate your SO-100 robot to ensure that the leader and follower arms have the same position values when they are in the same physical position. This calibration is essential because it allows a neural network trained on one SO-100 robot to work on another.

TODO(pepijn): Update with new calibratio method

#### a. Manual calibration of follower arm

You will need to move the follower arm to these positions sequentially:

| 1. Zero position                                                                                                                                             | 2. Rotated position                                                                                                                                                   | 3. Rest position                                                                                                                                             |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <img src="../media/so100/follower_zero.webp?raw=true" alt="SO-100 follower arm zero position" title="SO-100 follower arm zero position" style="width:100%;"> | <img src="../media/so100/follower_rotated.webp?raw=true" alt="SO-100 follower arm rotated position" title="SO-100 follower arm rotated position" style="width:100%;"> | <img src="../media/so100/follower_rest.webp?raw=true" alt="SO-100 follower arm rest position" title="SO-100 follower arm rest position" style="width:100%;"> |

Make sure both arms are connected and run this script to launch manual calibration:
```bash
python lerobot/scripts/control_robot.py \
  --robot.type=so100 \
  --robot.cameras='{}' \
  --control.type=calibrate \
  --control.arms='["main_follower"]'
```

#### b. Manual calibration of leader arm
Follow step 6 of the [assembly video](https://youtu.be/FioA2oeFZ5I?t=724) which illustrates the manual calibration. You will need to move the leader arm to these positions sequentially:

| 1. Zero position                                                                                                                                       | 2. Rotated position                                                                                                                                             | 3. Rest position                                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <img src="../media/so100/leader_zero.webp?raw=true" alt="SO-100 leader arm zero position" title="SO-100 leader arm zero position" style="width:100%;"> | <img src="../media/so100/leader_rotated.webp?raw=true" alt="SO-100 leader arm rotated position" title="SO-100 leader arm rotated position" style="width:100%;"> | <img src="../media/so100/leader_rest.webp?raw=true" alt="SO-100 leader arm rest position" title="SO-100 leader arm rest position" style="width:100%;"> |

Run this script to launch manual calibration:
```bash
python lerobot/scripts/control_robot.py \
  --robot.type=so100 \
  --robot.cameras='{}' \
  --control.type=calibrate \
  --control.arms='["main_leader"]'
```
