diff --git a/docs/source/koch.md b/docs/source/koch.md
deleted file mode 100644
index da79e5584..000000000
--- a/docs/source/koch.md
+++ /dev/null
@@ -1,250 +0,0 @@
-# Koch v1.1
-
-In the steps below, we explain how to assemble the Koch v1.1 robot.
-
-## Order and assemble the parts
-
-Follow the sourcing and assembling instructions provided in this [README](https://github.com/jess-moss/koch-v1-1). This will guide you through setting up both the follower and leader arms, as shown in the image below.
-
-For a visual walkthrough of the assembly process, you can refer to [this video tutorial](https://youtu.be/8nQIg9BwwTk).
-
-> [!WARNING]
-> Since the production of this video, we simplified the configuration phase. Because of this, two things differ from the instructions in that video:
-> - Don't plug in all the motor cables right away and wait to be instructed to do so in [Configure the motors](#configure-the-motors).
-> - Don't screw in the controller board (PCB) to the base right away and wait for being instructed to do so in [Configure the motors](#configure-the-motors).
-
-
-## Install LeRobot 🤗
-
-To install LeRobot follow, our [Installation Guide](./installation)
-
-In addition to these instructions, you need to install the Dynamixel SDK:
-```bash
-pip install -e ".[dynamixel]"
-```
-
-## Configure the motors
-
-### 1. Find the USB ports associated with each arm
-
-To find the port for each bus servo adapter, run this script:
-```bash
-python lerobot/find_port.py
-```
-
-
-
-
-Example output:
-
-```
-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 corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/tty.usbmodem575E0032081
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
-
-
-
-
-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
-```
-
-Example output:
-
-```
-Finding all available ports for the MotorBus.
-['/dev/ttyACM0', '/dev/ttyACM1']
-Remove the usb cable from your MotorsBus and press Enter when done.
-
-[...Disconnect corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/ttyACM1
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
-
-
-
-
-### 2. Set the motors ids and baudrates
-
-Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
-
-To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
-
-If you are repurposing motors from another robot, you will probably also need to perform this step, as the ids and baudrate likely won't match.
-
-#### Follower
-
-Connect the usb cable from your computer and the 5V power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
-
-For a visual reference on how to set the motor ids please refer to [this video](http://localhost:5173/so101#setup-motors-video) where we follow the process for the SO101 arm.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --robot.type=koch_follower \
- --robot.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.robots.koch_follower import KochFollower, KochFollowerConfig
-
-config = KochFollowerConfig(
- port="/dev/tty.usbmodem575E0031751",
- id="my_awesome_follower_arm",
-)
-follower = KochFollower(config)
-follower.setup_motors()
-```
-
-
-
-You should see the following instruction.
-```
-Connect the controller board to the 'gripper' motor only and press enter.
-```
-
-As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
-
-
-Troubleshooting
-
- If you get an error at that point, check your cables and make sure they are plugged in properly:
-
-
Power supply
-
USB cable between your computer and the controller board
-
The 3-pin cable from the controller board to the motor
-
-
- If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
-
-
-You should then see the following message:
-```
-'gripper' motor id set to 6
-```
-
-Followed by the next instruction:
-```
-Connect the controller board to the 'wrist_roll' motor only and press enter.
-```
-
-You can disconnect the 3-pin cable from the controller board but you can leave it connected to the gripper motor on the other end as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
-
-Repeat the operation for each motor as instructed.
-
-> [!TIP]
-> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
-
-When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
-
-#### Leader
-Do the same steps for the leader arm but modify the command or script accordingly.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --teleop.type=koch_leader \
- --teleop.port=/dev/tty.usbmodem575E0031751 \ # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.teleoperators.koch_leader import KochLeader, KochLeaderConfig
-
-config = KochLeaderConfig(
- port="/dev/tty.usbmodem575E0031751",
- id="my_awesome_leader_arm",
-)
-leader = KochLeader(config)
-leader.setup_motors()
-```
-
-
-
-## Calibrate
-
-Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
-The calibration process is very important because it allows a neural network trained on one robot to work on another.
-
-#### Follower
-
-Run the following command or API example to calibrate the follower arm:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --robot.type=koch_follower \
- --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.robots.koch_follower import KochFollowerConfig, KochFollower
-
-config = KochFollowerConfig(
- port="/dev/tty.usbmodem585A0076891",
- id="my_awesome_follower_arm",
-)
-
-follower = KochFollower(config)
-follower.connect(calibrate=False)
-follower.calibrate()
-follower.disconnect()
-```
-
-
-
-We unified the calibration method for most robots. Thus, the calibration steps for this Koch arm are the same as the steps for the SO100 and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
-
-#### Leader
-
-Do the same steps to calibrate the leader arm, run the following command or API example:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --teleop.type=koch_leader \
- --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.teleoperators.koch_leader import KochLeaderConfig, KochLeader
-
-config = KochLeaderConfig(
- port="/dev/tty.usbmodem575E0031751",
- id="my_awesome_leader_arm",
-)
-
-leader = KochLeader(config)
-leader.connect(calibrate=False)
-leader.calibrate()
-leader.disconnect()
-```
-
-
-
-Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
-
-> [!TIP]
-> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/docs/source/koch.md b/docs/source/koch.md
new file mode 120000
index 000000000..0ca082c00
--- /dev/null
+++ b/docs/source/koch.md
@@ -0,0 +1 @@
+../../lerobot/common/robots/koch_follower/koch.md
\ No newline at end of file
diff --git a/docs/source/lekiwi.md b/docs/source/lekiwi.md
deleted file mode 100644
index f476f55ca..000000000
--- a/docs/source/lekiwi.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# LeKiwi
-
-In the steps below, we explain how to assemble the LeKiwi mobile robot.
-
-## Source the parts
-
-Follow this [README](https://github.com/SIGRobotics-UIUC/LeKiwi). It contains the bill of materials, with a link to source the parts, as well as the instructions to 3D print the parts.
-And advise if it's your first time printing or if you don't own a 3D printer.
-
-### Wired version
-If you have the **wired** LeKiwi version, you can skip the installation of the Raspberry Pi and setting up SSH. You can also run all commands directly on your PC for both the LeKiwi scripts and the leader arm scripts for teleoperating.
-
-## Install software on Pi
-Now we have to set up the remote PC that will run on the LeKiwi Robot. This is normally a Raspberry Pi, but can be any PC that can run on 5V and has enough usb ports (2 or more) for the cameras and motor control board.
-
-### Install OS
-For setting up the Raspberry Pi and its SD-card see: [Setup PI](https://www.raspberrypi.com/documentation/computers/getting-started.html). Here is explained how to download the [Imager](https://www.raspberrypi.com/software/) to install Raspberry Pi OS or Ubuntu.
-
-### Setup SSH
-After setting up your Pi, you should enable and set up [SSH](https://www.raspberrypi.com/news/coding-on-raspberry-pi-remotely-with-visual-studio-code/) (Secure Shell Protocol) so you can log in to the Pi from your laptop without requiring a screen, keyboard, and mouse on the Pi. A great tutorial on how to do this can be found [here](https://www.raspberrypi.com/documentation/computers/remote-access.html#ssh). Logging into your Pi can be done in your Command Prompt (cmd) or, if you use VSCode you can use [this](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) extension.
-
-### Install LeRobot on Pi 🤗
-
-On your Raspberry Pi install LeRobot using our [Installation Guide](./installation)
-
-In addition to these instructions, you need to install the Feetech sdk on your Pi:
-```bash
-pip install -e ".[feetech]"
-```
-
-## Install LeRobot locally
-If you already have installed LeRobot on your laptop/pc you can skip this step; otherwise, please follow along as we do the same steps we did on the Pi.
-
-Follow our [Installation Guide](./installation)
-
-Great :hugs:! You are now done installing LeRobot, and we can begin assembling the SO100/SO101 arms and the mobile base :robot:.
-Every time you now want to use LeRobot, you can go to the `~/lerobot` folder where we installed LeRobot and run one of the commands.
-
-# Step-by-Step Assembly Instructions
-
-First, we will assemble the two SO100/SO101 arms. One to attach to the mobile base and one for teleoperation. Then we will assemble the mobile base. The instructions for assembling can be found on these two pages:
-
-- [Assemble SO101](./so101#step-by-step-assembly-instructions)
-- [Assemble LeKiwi](https://github.com/SIGRobotics-UIUC/LeKiwi/blob/main/Assembly.md)
-
-### Configure motors
-The instructions for configuring the motors can be found in the SO101 [docs](./so101#configure-the-motors). Besides the ids for the arm motors, we also need to set the motor ids for the mobile base. These need to be in a specific order to work. Below an image of the motor ids and motor mounting positions for the mobile base. Note that we only use one Motor Control board on LeKiwi. This means the motor ids for the wheels are 7, 8 and 9.
-
-
-
-### Troubleshoot communication
-
-If you are having trouble connecting to the Mobile SO100, follow these steps to diagnose and resolve the issue.
-
-#### 1. Verify IP Address Configuration
-Make sure that the correct IP for the Pi is used in the commands or in your code. To check the Raspberry Pi's IP address, run (on the Pi command line):
-```bash
-hostname -I
-```
-
-#### 2. Check if Pi is reachable from laptop/pc
-Try pinging the Raspberry Pi from your laptop:
-```bach
-ping
-```
-
-If the ping fails:
-- Ensure the Pi is powered on and connected to the same network.
-- Check if SSH is enabled on the Pi.
-
-#### 3. Try SSH connection
-If you can't SSH into the Pi, it might not be properly connected. Use:
-```bash
-ssh @
-```
-If you get a connection error:
-- Ensure SSH is enabled on the Pi by running:
- ```bash
- sudo raspi-config
- ```
- Then navigate to: **Interfacing Options -> SSH** and enable it.
-
-### Calibration
-
-Now we have to calibrate the leader arm and the follower arm. The wheel motors don't have to be calibrated.
-The calibration process is very important because it allows a neural network trained on one robot to work on another.
-
-### Calibrate follower arm (on mobile base)
-
-Make sure the arm is connected to the Raspberry Pi and run this script or API example (on the Raspberry Pi via SSH) to launch calibration of the follower arm:
-
-
-```bash
-python -m lerobot.calibrate \
- --robot.type=lekiwi \
- --robot.port=/dev/ttyACM0 \ # <- The port of your robot
- --robot.id=my_awesome_kiwi # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.robots.lekiwi import LeKiwiClient, LeKiwiClientConfig
-
-config = LeKiwiClientConfig(
- remote_ip="192.168.0.23",
- id="my_awesome_kiwi",
-)
-
-lekiwi = LeKiwiClient(config)
-lekiwi.connect(calibrate=False)
-lekiwi.calibrate()
-lekiwi.disconnect()
-```
-
-
-
-We unified the calibration method for most robots, thus, the calibration steps for this SO100 arm are the same as the steps for the Koch and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
-
-### Wired version
-If you have the **wired** LeKiwi version, please run all commands on your laptop.
-
-### Calibrate leader arm
-Then, to calibrate the leader arm (which is attached to the laptop/pc). Run the following command of API example on your laptop:
-
-
-```bash
-python -m lerobot.calibrate \
- --teleop.type=so100_leader \
- --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.teleoperators.so100_leader import SO100LeaderConfig, SO100Leader
-
-config = SO100LeaderConfig(
- port="/dev/tty.usbmodem58760431551",
- id="my_awesome_leader_arm",
-)
-
-leader = SO100Leader(config)
-leader.connect(calibrate=False)
-leader.calibrate()
-leader.disconnect()
-```
-
-
-
-Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
-
-> [!TIP]
-> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/docs/source/lekiwi.md b/docs/source/lekiwi.md
new file mode 120000
index 000000000..7293a1c60
--- /dev/null
+++ b/docs/source/lekiwi.md
@@ -0,0 +1 @@
+../../lerobot/common/robots/lekiwi/lekiwi.md
\ No newline at end of file
diff --git a/docs/source/so100.md b/docs/source/so100.md
deleted file mode 100644
index 20335faf8..000000000
--- a/docs/source/so100.md
+++ /dev/null
@@ -1,470 +0,0 @@
-# SO-100
-
-In the steps below, we explain how to assemble the SO-100 robot.
-
-## Source the parts
-
-Follow this [README](https://github.com/TheRobotStudio/SO-ARM100/blob/main/SO100.md). It contains the bill of materials, with a link to source the parts, as well as the instructions to 3D print the parts. And advise if it's your first time printing or if you don't own a 3D printer.
-
-## Install LeRobot 🤗
-
-To install LeRobot, follow our [Installation Guide](./installation)
-
-In addition to these instructions, you need to install the Feetech SDK:
-```bash
-pip install -e ".[feetech]"
-```
-
-## Step-by-Step Assembly Instructions
-
-## Remove the gears of the 6 leader motors
-
-
-Video removing gears
-
-
-
-
-
-Follow the video for removing gears. You need to remove the gear for the motors of the leader arm. As a result, you will only use the position encoding of the motor and reduce friction to more easily operate the leader arm.
-
-### Clean Parts
-Remove all support material from the 3D-printed parts. The easiest way to do this is using a small screwdriver to get underneath the support material.
-
-### Additional Guidance
-
-
-Video assembling arms
-
-
-
-
-
-**Note:**
-This video provides visual guidance for assembling the arms, but it doesn't specify when or how to do the wiring. Inserting the cables beforehand is much easier than doing it afterward. The first arm may take a bit more than 1 hour to assemble, but once you get used to it, you can assemble the second arm in under 1 hour.
-
----
-
-### First Motor
-
-**Step 2: Insert Wires**
-- Insert two wires into the first motor.
-
-
-
-**Step 3: Install in Base**
-- Place the first motor into the base.
-
-
-
-**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).
-
-
-
-**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.
-
-
-
- Video adding motor horn
-
-
-
-**Step 7: Attach Shoulder Part**
-- Route one wire to the back of the robot and the other to the left or towards you (see photo).
-- Attach the shoulder part.
-
-
-
-**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.
-
-
-
-**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.
-
-
-
-
-
-
-
-**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.
-
-
-
-**Step 14: Attach Upper Arm**
-- Attach the upper arm with 4 screws on each side.
-
-
-
----
-
-### 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.
-
-
-
-**Step 17: Attach Forearm**
-- Connect the forearm to motor 3 using 4 screws on each side.
-
-
-
----
-
-### 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.
-
-
-
-
-
-
-**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).
-
-
-
-**Step 20: Secure Motor 4 & Attach Horn**
-- Fasten motor 4 with 4 screws and attach its motor horns, use for one a horn screw.
-
-
-
----
-
-### Wrist Assembly
-
-**Step 21: Install Motor 5**
-- Insert motor 5 into the wrist holder and secure it with 2 front screws.
-
-
-
-**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.
-
-
-
-**Step 23: Attach Wrist Horn**
-- Install only one motor horn on the wrist motor and secure it with a horn screw.
-
-
-
----
-
-### Follower Configuration
-
-**Step 24: Attach Gripper**
-- Attach the gripper to motor 5.
-
-
-
-**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.
-
-
-
-**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.
-
-
-
-**Step 27: Mount Controller**
-- Attach the motor controller to the back of the robot.
-
-
-
-
-
-
-*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.
-
-
-
-**Step 25: Attach Handle**
-- Attach the handle to motor 5 using 4 screws.
-
-
-
-**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.
-
-
-
-**Step 27: Attach Trigger**
-- Attach the follower trigger with 4 screws.
-
-
-
-**Step 28: Mount Controller**
-- Attach the motor controller to the back of the robot.
-
-
-
-
-
-
-## Configure the motors
-
-### 1. Find the USB ports associated with each arm
-
-To find the port for each bus servo adapter, run this script:
-```bash
-python lerobot/find_port.py
-```
-
-
-
-
-Example output:
-
-```
-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 corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/tty.usbmodem575E0032081
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
-
-
-
-
-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
-```
-
-Example output:
-
-```
-Finding all available ports for the MotorBus.
-['/dev/ttyACM0', '/dev/ttyACM1']
-Remove the usb cable from your MotorsBus and press Enter when done.
-
-[...Disconnect corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/ttyACM1
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
-
-
-
-
-### 2. Set the motors ids and baudrates
-
-Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
-
-To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
-
-If you are repurposing motors from another robot, you will probably also need to perform this step as the ids and baudrate likely won't match.
-
-#### Follower
-
-Connect the usb cable from your computer and the power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
-
-For a visual reference on how to set the motor ids please refer to [this video](http://localhost:5173/so101#setup-motors-video) where we follow the process for the SO101 arm.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --robot.type=so100_follower \
- --robot.port=/dev/tty.usbmodem585A0076841 # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.robots.so100_follower import SO100Follower, SO100FollowerConfig
-
-config = SO100FollowerConfig(
- port="/dev/tty.usbmodem585A0076841",
- id="my_awesome_follower_arm",
-)
-follower = SO100Follower(config)
-follower.setup_motors()
-```
-
-
-
-You should see the following instruction
-```
-Connect the controller board to the 'gripper' motor only and press enter.
-```
-
-As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
-
-
-Troubleshooting
-
- If you get an error at that point, check your cables and make sure they are plugged in properly:
-
-
Power supply
-
USB cable between your computer and the controller board
-
The 3-pin cable from the controller board to the motor
-
-
-If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
-
-
-You should then see the following message:
-```
-'gripper' motor id set to 6
-```
-
-Followed by the next instruction:
-```
-Connect the controller board to the 'wrist_roll' motor only and press enter.
-```
-
-You can disconnect the 3-pin cable from the controller board, but you can leave it connected to the gripper motor on the other end, as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
-
-Repeat the operation for each motor as instructed.
-
-> [!TIP]
-> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
-
-When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
-
-#### Leader
-Do the same steps for the leader arm.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --teleop.type=so100_leader \
- --teleop.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.teleoperators.so100_leader import SO100Leader, SO100LeaderConfig
-
-config = SO100LeaderConfig(
- port="/dev/tty.usbmodem585A0076841",
- id="my_awesome_leader_arm",
-)
-leader = SO100Leader(config)
-leader.setup_motors()
-```
-
-
-
-## Calibrate
-
-Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
-The calibration process is very important because it allows a neural network trained on one robot to work on another.
-
-#### Follower
-
-Run the following command or API example to calibrate the follower arm:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --robot.type=so100_follower \
- --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.robots.so100_follower import SO100FollowerConfig, SO100Follower
-
-config = SO100FollowerConfig(
- port="/dev/tty.usbmodem585A0076891",
- id="my_awesome_follower_arm",
-)
-
-follower = SO100Follower(config)
-follower.connect(calibrate=False)
-follower.calibrate()
-follower.disconnect()
-```
-
-
-
-We unified the calibration method for most robots. Thus, the calibration steps for this SO100 arm are the same as the steps for the Koch and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
-
-#### Leader
-
-Do the same steps to calibrate the leader arm, run the following command or API example:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --teleop.type=so100_leader \
- --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.teleoperators.so100_leader import SO100LeaderConfig, SO100Leader
-
-config = SO100LeaderConfig(
- port="/dev/tty.usbmodem58760431551",
- id="my_awesome_leader_arm",
-)
-
-leader = SO100Leader(config)
-leader.connect(calibrate=False)
-leader.calibrate()
-leader.disconnect()
-```
-
-
-
-Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
-
-> [!TIP]
-> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/docs/source/so100.md b/docs/source/so100.md
new file mode 120000
index 000000000..3a4625f2b
--- /dev/null
+++ b/docs/source/so100.md
@@ -0,0 +1 @@
+../../lerobot/common/robots/so100_follower/so100.md
\ No newline at end of file
diff --git a/docs/source/so101.md b/docs/source/so101.md
deleted file mode 100644
index 4105bd34d..000000000
--- a/docs/source/so101.md
+++ /dev/null
@@ -1,373 +0,0 @@
-# SO-101
-
-In the steps below, we explain how to assemble our flagship robot, the SO-101.
-
-## 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 advise if it's your first time printing or if you don't own a 3D printer.
-
-## Install LeRobot 🤗
-
-To install LeRobot, follow our [Installation Guide](./installation)
-
-In addition to these instructions, you need to install the Feetech SDK:
-```bash
-pip install -e ".[feetech]"
-```
-
-## Step-by-Step Assembly Instructions
-
-The follower arm uses 6x STS3215 motors with 1/345 gearing. The leader, however, uses three differently geared motors to make sure it can both sustain its own weight and it can be moved without requiring much force. Which motor is needed for which joint is shown in the table below.
-
-| Leader-Arm Axis | Motor | Gear Ratio |
-|-----------------|:-------:|:----------:|
-| Base / Shoulder Yaw | 1 | 1 / 191 |
-| Shoulder Pitch | 2 | 1 / 345 |
-| Elbow | 3 | 1 / 191 |
-| Wrist Roll | 4 | 1 / 147 |
-| Wrist Pitch | 5 | 1 / 147 |
-| Gripper | 6 | 1 / 147 |
-
-### Clean Parts
-Remove all support material from the 3D-printed parts. The easiest way to do this is using a small screwdriver to get underneath the support material.
-
-### Joint 1
-
-- Place the first motor into the base.
-- Fasten the motor with 4 M2x6mm screws (smallest screws). Two from the top and two from the bottom.
-- Slide over the first motor holder and fasten it using two M2x6mm screws (one on each side).
-- Install both motor horns, securing the top horn with a M3x6mm screw.
-- Attach the shoulder part.
-- Tighten the shoulder part with 4 M3x6mm screws on top and 4 M3x6mm screws on the bottom
-- Add the shoulder motor holder.
-
-
-
-
-
-### Joint 2
-
-- Slide the second motor in from the top.
-- Fasten the second motor with 4 M2x6mm screws.
-- Attach both motor horns to motor 2, again use the M3x6mm horn screw.
-- Attach the upper arm with 4 M3x6mm screws on each side.
-
-
-
-
-
-### Joint 3
-
-- Insert motor 3 and fasten using 4 M2x6mm screws
-- Attach both motor horns to motor 3 and secure one again with a M3x6mm horn screw.
-- Connect the forearm to motor 3 using 4 M3x6mm screws on each side.
-
-
-
-
-
-### Joint 4
-
-- Slide over motor holder 4.
-- Slide in motor 4.
-- Fasten motor 4 with 4 M2x6mm screws and attach its motor horns, use a M3x6mm horn screw.
-
-
-
-
-
-### Joint 5
-
-- Insert motor 5 into the wrist holder and secure it with 2 M2x6mm front screws.
-- Install only one motor horn on the wrist motor and secure it with a M3x6mm horn screw.
-- Secure the wrist to motor 4 using 4 M3x6mm screws on both sides.
-
-
-
-
-
-### Gripper / Handle
-
-
-
-
-- Attach the gripper to motor 5, attach it to the motor horn on the wrist using 4 M3x6mm screws.
-- Insert the gripper motor and secure it with 2 M2x6mm screws on each side.
-- Attach the motor horns and again use a M3x6mm horn screw.
-- Install the gripper claw and secure it with 4 M3x6mm screws on both sides.
-
-
-
-
-
-
-
-
-- Mount the leader holder onto the wrist and secure it with 4 M3x6mm screws.
-- Attach the handle to motor 5 using 1 M2x6mm screw.
-- Insert the gripper motor, secure it with 2 M2x6mm screws on each side, attach a motor horn using a M3x6mm horn screw.
-- Attach the follower trigger with 4 M3x6mm screws.
-
-
-
-
-
-
-
-
-## Configure the motors
-
-### 1. Find the USB ports associated with each arm
-
-To find the port for each bus servo adapter, run this script:
-```bash
-python lerobot/find_port.py
-```
-
-
-
-
-Example output:
-
-```
-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 corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/tty.usbmodem575E0032081
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
-
-
-
-
-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
-```
-
-Example output:
-
-```
-Finding all available ports for the MotorBus.
-['/dev/ttyACM0', '/dev/ttyACM1']
-Remove the usb cable from your MotorsBus and press Enter when done.
-
-[...Disconnect corresponding leader or follower arm and press Enter...]
-
-The port of this MotorsBus is /dev/ttyACM1
-Reconnect the USB cable.
-```
-
-Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
-
-
-
-
-### 2. Set the motors ids and baudrates
-
-Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
-
-To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
-
-If you are repurposing motors from another robot, you will probably also need to perform this step as the ids and baudrate likely won't match.
-
-The video below shows the sequence of steps for setting the motor ids.
-
-##### Setup motors video
-
-
-
-
-
-#### Follower
-
-Connect the usb cable from your computer and the power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --robot.type=so101_follower \
- --robot.port=/dev/tty.usbmodem585A0076841 # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.robots.so101_follower import SO101Follower, SO101FollowerConfig
-
-config = SO101FollowerConfig(
- port="/dev/tty.usbmodem585A0076841",
- id="my_awesome_follower_arm",
-)
-follower = SO101Follower(config)
-follower.setup_motors()
-```
-
-
-
-You should see the following instruction
-```bash
-Connect the controller board to the 'gripper' motor only and press enter.
-```
-
-As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
-
-
-Troubleshooting
-
- If you get an error at that point, check your cables and make sure they are plugged in properly:
-
-
Power supply
-
USB cable between your computer and the controller board
-
The 3-pin cable from the controller board to the motor
-
-
- If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
-
-
-You should then see the following message:
-```bash
-'gripper' motor id set to 6
-```
-
-Followed by the next instruction:
-```bash
-Connect the controller board to the 'wrist_roll' motor only and press enter.
-```
-
-You can disconnect the 3-pin cable from the controller board, but you can leave it connected to the gripper motor on the other end, as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
-
-Repeat the operation for each motor as instructed.
-
-> [!TIP]
-> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
-
-When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
-
-#### Leader
-Do the same steps for the leader arm.
-
-
-
-```bash
-python -m lerobot.setup_motors \
- --teleop.type=so101_leader \
- --teleop.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
-```
-
-
-```python
-from lerobot.common.teleoperators.so101_leader import SO101Leader, SO101LeaderConfig
-
-config = SO101LeaderConfig(
- port="/dev/tty.usbmodem585A0076841",
- id="my_awesome_leader_arm",
-)
-leader = SO101Leader(config)
-leader.setup_motors()
-```
-
-
-
-## Calibrate
-
-Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
-The calibration process is very important because it allows a neural network trained on one robot to work on another.
-
-#### Follower
-
-Run the following command or API example to calibrate the follower arm:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --robot.type=so101_follower \
- --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.robots.so101_follower import SO101FollowerConfig, SO101Follower
-
-config = SO101FollowerConfig(
- port="/dev/tty.usbmodem585A0076891",
- id="my_awesome_follower_arm",
-)
-
-follower = SO101Follower(config)
-follower.connect(calibrate=False)
-follower.calibrate()
-follower.disconnect()
-```
-
-
-
-The video below shows how to perform the calibration. First you need to move the robot to the position where all joints are in the middle of their ranges. Then after pressing enter you have to move each joint through its full range of motion.
-
-##### Calibration video
-
-
-
-
-
-#### Leader
-
-Do the same steps to calibrate the leader arm, run the following command or API example:
-
-
-
-```bash
-python -m lerobot.calibrate \
- --teleop.type=so101_leader \
- --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
- --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
-```
-
-
-```python
-from lerobot.common.teleoperators.so101_leader import SO101LeaderConfig, SO101Leader
-
-config = SO101LeaderConfig(
- port="/dev/tty.usbmodem58760431551",
- id="my_awesome_leader_arm",
-)
-
-leader = SO101Leader(config)
-leader.connect(calibrate=False)
-leader.calibrate()
-leader.disconnect()
-```
-
-
-
-Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
-
-> [!TIP]
-> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/docs/source/so101.md b/docs/source/so101.md
new file mode 120000
index 000000000..a67046099
--- /dev/null
+++ b/docs/source/so101.md
@@ -0,0 +1 @@
+../../lerobot/common/robots/so101_follower/so101.md
\ No newline at end of file
diff --git a/lerobot/common/robots/koch_follower/README.md b/lerobot/common/robots/koch_follower/README.md
deleted file mode 100644
index 5878fdaff..000000000
--- a/lerobot/common/robots/koch_follower/README.md
+++ /dev/null
@@ -1,328 +0,0 @@
-# Using the [Koch v1.1](https://github.com/jess-moss/koch-v1-1) with LeRobot
-
-## Table of Contents
-
- - [A. Order and Assemble the parts](#a-order-and-assemble-the-parts)
- - [B. Install LeRobot](#b-install-lerobot)
- - [C. Configure the Motors](#c-configure-the-motors)
- - [D. Calibrate](#d-calibrate)
- - [E. Teleoperate](#e-teleoperate)
- - [F. Record a dataset](#f-record-a-dataset)
- - [G. Visualize a dataset](#g-visualize-a-dataset)
- - [H. Replay an episode](#h-replay-an-episode)
- - [I. Train a policy](#i-train-a-policy)
- - [J. Evaluate your policy](#j-evaluate-your-policy)
- - [K. More Information](#k-more-information)
-
-## A. Order and Assemble the parts
-
-Follow the sourcing and assembling instructions provided on the [Koch v1.1 Github page](https://github.com/jess-moss/koch-v1-1). This will guide you through setting up both the follower and leader arms, as shown in the image below.
-
-
-
-
-
-For a visual walkthrough of the assembly process, you can refer to [this video tutorial](https://youtu.be/8nQIg9BwwTk).
-
-> [!IMPORTANT]
-> Since the production of this video, we simplified the configuration phase (detailed in [section C](#c-configure-the-motors)) of the motors.
-> Because of this, two things differ from the instructions in that video:
-> - Don't plug all the motors cables right away and wait for being instructed to do so in [section C](#c-configure-the-motors).
-> - Don't screw in the controller board (PCB) to the base right away and wait for being instructed to do so in [section C](#c-configure-the-motors).
-
-
-## B. Install LeRobot
-
-> [!TIP]
-> We use the Command Prompt (cmd) quite a lot. If you are not comfortable using the cmd or want to brush up using the command line you can have a look here: [Command line crash course](https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line)
-
-Follow instructions on our [README](https://github.com/huggingface/lerobot) to install LeRobot.
-
-In addition to these instructions, you need to install the dynamixel sdk:
-```bash
-pip install -e ".[dynamixel]"
-```
-
-## C. Configure the motors
-
-### 1. Find the USB ports associated to each arm
-
-For each controller board (Waveshare Serial Bus Servo Driver Board, one for the leader arm and one for the follower), connect it first to your computer through usb. To then find the internal port its connected to -which we will need later on- run the utility script:
-```bash
-python -m lerobot.find_port
-```
-
-> [!NOTE]
-> Note: 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
-> ```
-
-This will first display all currently available ports on your computer. As prompted by the script, unplug the controller board usb cable from your computer. The script will then detect which port has been disconnected and will display it.
-
-
-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.
-```
-
-You can now reconnect the usb cable to your computer.
-
-### 2. Set the motors ids and baudrate
-
-Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
-
-To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
-
-> [!NOTE]
-> Note: If you are repurposing motors from another robot, you will probably also need to perform this step as the ids and baudrate likely won't match.
-
-Connect the usb cable from your computer and the 5V power supply to the leader arm's controller board. Then, run the following command with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
-
-```bash
-python -m lerobot.setup_motors \
- --device.type=so100_leader \
- --device.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
-```
-
-Note that the command above is equivalent to running the following script:
-
-Setup script
-
- ```python
- from lerobot.common.teleoperators.koch import KochLeader, KochLeaderConfig
-
- config = KochLeaderConfig(
- port="/dev/tty.usbmodem575E0031751",
- )
- leader = KochLeader(config)
- leader.setup_motors()
- ```
-
-
-
-You should see the following instruction
-```
-Connect the controller board to the 'gripper' motor only and press enter.
-```
-
-As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
-
-
-
-Troubleshooting
-
- If you get an error at that point, check your cables and make sure they are plugged-in properly:
- - Power supply
- - USB cable between from your computer to the controller board
- - The 3-pin cable from the controller board to the motor.
-
- If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
-
-
-You should then see the following message:
-```
-'gripper' motor id set to 6
-```
-
-Followed by the next instruction:
-```
-Connect the controller board to the 'wrist_roll' motor only and press enter.
-```
-
-You can disconnect the 3-pin cable from the controller board but you can leave it connected to the gripper motor on the other end as it will already be in the right place. Now, plug-in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
-
-Repeat the operation for each motor as instructed.
-
-> [!TIP]
-> Check your cabling at each step before pressing Enter. For instance, the power supply cable is not solidly anchored to the board and might disconnect easily as you manipulate the board.
-
-When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
-
-## D. 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.
-
-#### a. Manual calibration of follower arm
-
-> [!IMPORTANT]
-> Contrarily to step 6 of the [assembly video](https://youtu.be/FioA2oeFZ5I?t=724) which illustrates the auto calibration, we will actually do manual calibration of follower for now.
-
-You will need to move the follower arm to these positions sequentially:
-
-| 1. Zero position | 2. Rotated position | 3. Rest position |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| | | |
-
-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 |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| | | |
-
-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"]'
-```
-
-## E. Teleoperate
-
-**Simple teleop**
-Then you are ready to teleoperate your robot! Run this simple script (it won't connect and display the cameras):
-```bash
-python lerobot/scripts/control_robot.py \
- --robot.type=so100 \
- --robot.cameras='{}' \
- --control.type=teleoperate
-```
-
-
-#### a. Teleop with displaying cameras
-Follow [this guide to setup your cameras](https://github.com/huggingface/lerobot/blob/main/examples/7_get_started_with_real_robot.md#c-add-your-cameras-with-opencvcamera). Then you will be able to display the cameras on your computer while you are teleoperating by running the following code. This is useful to prepare your setup before recording your first dataset.
-
-> **NOTE:** To visualize the data, enable `--control.display_data=true`. This streams the data using `rerun`.
-
-```bash
-python lerobot/scripts/control_robot.py \
- --robot.type=so100 \
- --control.type=teleoperate
-```
-
-## F. Record a dataset
-
-Once you're familiar with teleoperation, you can record your first dataset with SO-100.
-
-If you want to use the Hugging Face hub features for uploading your dataset and you haven't previously done it, make sure you've logged in using a write-access token, which can be generated from the [Hugging Face settings](https://huggingface.co/settings/tokens):
-```bash
-huggingface-cli login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
-```
-
-Store your Hugging Face repository name in a variable to run these commands:
-```bash
-HF_USER=$(huggingface-cli whoami | head -n 1)
-echo $HF_USER
-```
-
-Record 2 episodes and upload your dataset to the hub:
-```bash
-python lerobot/scripts/control_robot.py \
- --robot.type=so100 \
- --control.type=record \
- --control.fps=30 \
- --control.single_task="Grasp a lego block and put it in the bin." \
- --control.repo_id=${HF_USER}/so100_test \
- --control.tags='["so100","tutorial"]' \
- --control.warmup_time_s=5 \
- --control.episode_time_s=30 \
- --control.reset_time_s=30 \
- --control.num_episodes=2 \
- --control.push_to_hub=true
-```
-
-Note: You can resume recording by adding `--control.resume=true`.
-
-## G. Visualize a dataset
-
-If you uploaded your dataset to the hub with `--control.push_to_hub=true`, you can [visualize your dataset online](https://huggingface.co/spaces/lerobot/visualize_dataset) by copy pasting your repo id given by:
-```bash
-echo ${HF_USER}/so100_test
-```
-
-If you didn't upload with `--control.push_to_hub=false`, you can also visualize it locally with (a window can be opened in the browser `http://127.0.0.1:9090` with the visualization tool):
-```bash
-python lerobot/scripts/visualize_dataset_html.py \
- --repo-id ${HF_USER}/so100_test \
- --local-files-only 1
-```
-
-## H. Replay an episode
-
-Now try to replay the first episode on your robot:
-```bash
-python lerobot/scripts/control_robot.py \
- --robot.type=so100 \
- --control.type=replay \
- --control.fps=30 \
- --control.repo_id=${HF_USER}/so100_test \
- --control.episode=0
-```
-
-## I. Train a policy
-
-To train a policy to control your robot, use the [`python lerobot/scripts/train.py`](../lerobot/scripts/train.py) script. A few arguments are required. Here is an example command:
-```bash
-python lerobot/scripts/train.py \
- --dataset.repo_id=${HF_USER}/so100_test \
- --policy.type=act \
- --output_dir=outputs/train/act_so100_test \
- --job_name=act_so100_test \
- --policy.device=cuda \
- --wandb.enable=true
-```
-
-Let's explain it:
-1. We provided the dataset as argument with `--dataset.repo_id=${HF_USER}/so100_test`.
-2. We provided the policy with `policy.type=act`. This loads configurations from [`configuration_act.py`](../lerobot/common/policies/act/configuration_act.py). Importantly, this policy will automatically adapt to the number of motor sates, motor actions and cameras of your robot (e.g. `laptop` and `phone`) which have been saved in your dataset.
-4. We provided `policy.device=cuda` since we are training on a Nvidia GPU, but you could use `policy.device=mps` to train on Apple silicon.
-5. We provided `wandb.enable=true` to use [Weights and Biases](https://docs.wandb.ai/quickstart) for visualizing training plots. This is optional but if you use it, make sure you are logged in by running `wandb login`.
-
-Training should take several hours. You will find checkpoints in `outputs/train/act_so100_test/checkpoints`.
-
-To resume training from a checkpoint, below is an example command to resume from `last` checkpoint of the `act_so100_test` policy:
-```bash
-python lerobot/scripts/train.py \
- --config_path=outputs/train/act_so100_test/checkpoints/last/pretrained_model/train_config.json \
- --resume=true
-```
-
-## J. Evaluate your policy
-
-You can use the `record` function from [`lerobot/scripts/control_robot.py`](../lerobot/scripts/control_robot.py) but with a policy checkpoint as input. For instance, run this command to record 10 evaluation episodes:
-```bash
-python lerobot/scripts/control_robot.py \
- --robot.type=so100 \
- --control.type=record \
- --control.fps=30 \
- --control.single_task="Grasp a lego block and put it in the bin." \
- --control.repo_id=${HF_USER}/eval_act_so100_test \
- --control.tags='["tutorial"]' \
- --control.warmup_time_s=5 \
- --control.episode_time_s=30 \
- --control.reset_time_s=30 \
- --control.num_episodes=10 \
- --control.push_to_hub=true \
- --control.policy.path=outputs/train/act_so100_test/checkpoints/last/pretrained_model
-```
-
-As you can see, it's almost the same command as previously used to record your training dataset. Two things changed:
-1. There is an additional `--control.policy.path` argument which indicates the path to your policy checkpoint with (e.g. `outputs/train/eval_act_so100_test/checkpoints/last/pretrained_model`). You can also use the model repository if you uploaded a model checkpoint to the hub (e.g. `${HF_USER}/act_so100_test`).
-2. The name of dataset begins by `eval` to reflect that you are running inference (e.g. `${HF_USER}/eval_act_so100_test`).
-
-## K. More Information
-
-Follow this [previous tutorial](https://github.com/huggingface/lerobot/blob/main/examples/7_get_started_with_real_robot.md#4-train-a-policy-on-your-data) for a more in-depth tutorial on controlling real robots with LeRobot.
-
-> [!TIP]
-> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb) in the channel [`#so100-arm`](https://discord.com/channels/1216765309076115607/1237741463832363039).
diff --git a/lerobot/common/robots/koch_follower/koch.md b/lerobot/common/robots/koch_follower/koch.md
new file mode 100644
index 000000000..da79e5584
--- /dev/null
+++ b/lerobot/common/robots/koch_follower/koch.md
@@ -0,0 +1,250 @@
+# Koch v1.1
+
+In the steps below, we explain how to assemble the Koch v1.1 robot.
+
+## Order and assemble the parts
+
+Follow the sourcing and assembling instructions provided in this [README](https://github.com/jess-moss/koch-v1-1). This will guide you through setting up both the follower and leader arms, as shown in the image below.
+
+For a visual walkthrough of the assembly process, you can refer to [this video tutorial](https://youtu.be/8nQIg9BwwTk).
+
+> [!WARNING]
+> Since the production of this video, we simplified the configuration phase. Because of this, two things differ from the instructions in that video:
+> - Don't plug in all the motor cables right away and wait to be instructed to do so in [Configure the motors](#configure-the-motors).
+> - Don't screw in the controller board (PCB) to the base right away and wait for being instructed to do so in [Configure the motors](#configure-the-motors).
+
+
+## Install LeRobot 🤗
+
+To install LeRobot follow, our [Installation Guide](./installation)
+
+In addition to these instructions, you need to install the Dynamixel SDK:
+```bash
+pip install -e ".[dynamixel]"
+```
+
+## Configure the motors
+
+### 1. Find the USB ports associated with each arm
+
+To find the port for each bus servo adapter, run this script:
+```bash
+python lerobot/find_port.py
+```
+
+
+
+
+Example output:
+
+```
+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 corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/tty.usbmodem575E0032081
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
+
+
+
+
+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
+```
+
+Example output:
+
+```
+Finding all available ports for the MotorBus.
+['/dev/ttyACM0', '/dev/ttyACM1']
+Remove the usb cable from your MotorsBus and press Enter when done.
+
+[...Disconnect corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/ttyACM1
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
+
+
+
+
+### 2. Set the motors ids and baudrates
+
+Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
+
+To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
+
+If you are repurposing motors from another robot, you will probably also need to perform this step, as the ids and baudrate likely won't match.
+
+#### Follower
+
+Connect the usb cable from your computer and the 5V power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
+
+For a visual reference on how to set the motor ids please refer to [this video](http://localhost:5173/so101#setup-motors-video) where we follow the process for the SO101 arm.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --robot.type=koch_follower \
+ --robot.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.robots.koch_follower import KochFollower, KochFollowerConfig
+
+config = KochFollowerConfig(
+ port="/dev/tty.usbmodem575E0031751",
+ id="my_awesome_follower_arm",
+)
+follower = KochFollower(config)
+follower.setup_motors()
+```
+
+
+
+You should see the following instruction.
+```
+Connect the controller board to the 'gripper' motor only and press enter.
+```
+
+As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
+
+
+Troubleshooting
+
+ If you get an error at that point, check your cables and make sure they are plugged in properly:
+
+
Power supply
+
USB cable between your computer and the controller board
+
The 3-pin cable from the controller board to the motor
+
+
+ If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
+
+
+You should then see the following message:
+```
+'gripper' motor id set to 6
+```
+
+Followed by the next instruction:
+```
+Connect the controller board to the 'wrist_roll' motor only and press enter.
+```
+
+You can disconnect the 3-pin cable from the controller board but you can leave it connected to the gripper motor on the other end as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
+
+Repeat the operation for each motor as instructed.
+
+> [!TIP]
+> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
+
+When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
+
+#### Leader
+Do the same steps for the leader arm but modify the command or script accordingly.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --teleop.type=koch_leader \
+ --teleop.port=/dev/tty.usbmodem575E0031751 \ # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.teleoperators.koch_leader import KochLeader, KochLeaderConfig
+
+config = KochLeaderConfig(
+ port="/dev/tty.usbmodem575E0031751",
+ id="my_awesome_leader_arm",
+)
+leader = KochLeader(config)
+leader.setup_motors()
+```
+
+
+
+## Calibrate
+
+Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
+The calibration process is very important because it allows a neural network trained on one robot to work on another.
+
+#### Follower
+
+Run the following command or API example to calibrate the follower arm:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --robot.type=koch_follower \
+ --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.robots.koch_follower import KochFollowerConfig, KochFollower
+
+config = KochFollowerConfig(
+ port="/dev/tty.usbmodem585A0076891",
+ id="my_awesome_follower_arm",
+)
+
+follower = KochFollower(config)
+follower.connect(calibrate=False)
+follower.calibrate()
+follower.disconnect()
+```
+
+
+
+We unified the calibration method for most robots. Thus, the calibration steps for this Koch arm are the same as the steps for the SO100 and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
+
+#### Leader
+
+Do the same steps to calibrate the leader arm, run the following command or API example:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --teleop.type=koch_leader \
+ --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.teleoperators.koch_leader import KochLeaderConfig, KochLeader
+
+config = KochLeaderConfig(
+ port="/dev/tty.usbmodem575E0031751",
+ id="my_awesome_leader_arm",
+)
+
+leader = KochLeader(config)
+leader.connect(calibrate=False)
+leader.calibrate()
+leader.disconnect()
+```
+
+
+
+Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
+
+> [!TIP]
+> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/lerobot/common/robots/lekiwi/lekiwi.md b/lerobot/common/robots/lekiwi/lekiwi.md
new file mode 100644
index 000000000..f476f55ca
--- /dev/null
+++ b/lerobot/common/robots/lekiwi/lekiwi.md
@@ -0,0 +1,153 @@
+# LeKiwi
+
+In the steps below, we explain how to assemble the LeKiwi mobile robot.
+
+## Source the parts
+
+Follow this [README](https://github.com/SIGRobotics-UIUC/LeKiwi). It contains the bill of materials, with a link to source the parts, as well as the instructions to 3D print the parts.
+And advise if it's your first time printing or if you don't own a 3D printer.
+
+### Wired version
+If you have the **wired** LeKiwi version, you can skip the installation of the Raspberry Pi and setting up SSH. You can also run all commands directly on your PC for both the LeKiwi scripts and the leader arm scripts for teleoperating.
+
+## Install software on Pi
+Now we have to set up the remote PC that will run on the LeKiwi Robot. This is normally a Raspberry Pi, but can be any PC that can run on 5V and has enough usb ports (2 or more) for the cameras and motor control board.
+
+### Install OS
+For setting up the Raspberry Pi and its SD-card see: [Setup PI](https://www.raspberrypi.com/documentation/computers/getting-started.html). Here is explained how to download the [Imager](https://www.raspberrypi.com/software/) to install Raspberry Pi OS or Ubuntu.
+
+### Setup SSH
+After setting up your Pi, you should enable and set up [SSH](https://www.raspberrypi.com/news/coding-on-raspberry-pi-remotely-with-visual-studio-code/) (Secure Shell Protocol) so you can log in to the Pi from your laptop without requiring a screen, keyboard, and mouse on the Pi. A great tutorial on how to do this can be found [here](https://www.raspberrypi.com/documentation/computers/remote-access.html#ssh). Logging into your Pi can be done in your Command Prompt (cmd) or, if you use VSCode you can use [this](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) extension.
+
+### Install LeRobot on Pi 🤗
+
+On your Raspberry Pi install LeRobot using our [Installation Guide](./installation)
+
+In addition to these instructions, you need to install the Feetech sdk on your Pi:
+```bash
+pip install -e ".[feetech]"
+```
+
+## Install LeRobot locally
+If you already have installed LeRobot on your laptop/pc you can skip this step; otherwise, please follow along as we do the same steps we did on the Pi.
+
+Follow our [Installation Guide](./installation)
+
+Great :hugs:! You are now done installing LeRobot, and we can begin assembling the SO100/SO101 arms and the mobile base :robot:.
+Every time you now want to use LeRobot, you can go to the `~/lerobot` folder where we installed LeRobot and run one of the commands.
+
+# Step-by-Step Assembly Instructions
+
+First, we will assemble the two SO100/SO101 arms. One to attach to the mobile base and one for teleoperation. Then we will assemble the mobile base. The instructions for assembling can be found on these two pages:
+
+- [Assemble SO101](./so101#step-by-step-assembly-instructions)
+- [Assemble LeKiwi](https://github.com/SIGRobotics-UIUC/LeKiwi/blob/main/Assembly.md)
+
+### Configure motors
+The instructions for configuring the motors can be found in the SO101 [docs](./so101#configure-the-motors). Besides the ids for the arm motors, we also need to set the motor ids for the mobile base. These need to be in a specific order to work. Below an image of the motor ids and motor mounting positions for the mobile base. Note that we only use one Motor Control board on LeKiwi. This means the motor ids for the wheels are 7, 8 and 9.
+
+
+
+### Troubleshoot communication
+
+If you are having trouble connecting to the Mobile SO100, follow these steps to diagnose and resolve the issue.
+
+#### 1. Verify IP Address Configuration
+Make sure that the correct IP for the Pi is used in the commands or in your code. To check the Raspberry Pi's IP address, run (on the Pi command line):
+```bash
+hostname -I
+```
+
+#### 2. Check if Pi is reachable from laptop/pc
+Try pinging the Raspberry Pi from your laptop:
+```bach
+ping
+```
+
+If the ping fails:
+- Ensure the Pi is powered on and connected to the same network.
+- Check if SSH is enabled on the Pi.
+
+#### 3. Try SSH connection
+If you can't SSH into the Pi, it might not be properly connected. Use:
+```bash
+ssh @
+```
+If you get a connection error:
+- Ensure SSH is enabled on the Pi by running:
+ ```bash
+ sudo raspi-config
+ ```
+ Then navigate to: **Interfacing Options -> SSH** and enable it.
+
+### Calibration
+
+Now we have to calibrate the leader arm and the follower arm. The wheel motors don't have to be calibrated.
+The calibration process is very important because it allows a neural network trained on one robot to work on another.
+
+### Calibrate follower arm (on mobile base)
+
+Make sure the arm is connected to the Raspberry Pi and run this script or API example (on the Raspberry Pi via SSH) to launch calibration of the follower arm:
+
+
+```bash
+python -m lerobot.calibrate \
+ --robot.type=lekiwi \
+ --robot.port=/dev/ttyACM0 \ # <- The port of your robot
+ --robot.id=my_awesome_kiwi # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.robots.lekiwi import LeKiwiClient, LeKiwiClientConfig
+
+config = LeKiwiClientConfig(
+ remote_ip="192.168.0.23",
+ id="my_awesome_kiwi",
+)
+
+lekiwi = LeKiwiClient(config)
+lekiwi.connect(calibrate=False)
+lekiwi.calibrate()
+lekiwi.disconnect()
+```
+
+
+
+We unified the calibration method for most robots, thus, the calibration steps for this SO100 arm are the same as the steps for the Koch and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
+
+### Wired version
+If you have the **wired** LeKiwi version, please run all commands on your laptop.
+
+### Calibrate leader arm
+Then, to calibrate the leader arm (which is attached to the laptop/pc). Run the following command of API example on your laptop:
+
+
+```bash
+python -m lerobot.calibrate \
+ --teleop.type=so100_leader \
+ --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.teleoperators.so100_leader import SO100LeaderConfig, SO100Leader
+
+config = SO100LeaderConfig(
+ port="/dev/tty.usbmodem58760431551",
+ id="my_awesome_leader_arm",
+)
+
+leader = SO100Leader(config)
+leader.connect(calibrate=False)
+leader.calibrate()
+leader.disconnect()
+```
+
+
+
+Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
+
+> [!TIP]
+> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/lerobot/common/robots/so100_follower/so100.md b/lerobot/common/robots/so100_follower/so100.md
new file mode 100644
index 000000000..20335faf8
--- /dev/null
+++ b/lerobot/common/robots/so100_follower/so100.md
@@ -0,0 +1,470 @@
+# SO-100
+
+In the steps below, we explain how to assemble the SO-100 robot.
+
+## Source the parts
+
+Follow this [README](https://github.com/TheRobotStudio/SO-ARM100/blob/main/SO100.md). It contains the bill of materials, with a link to source the parts, as well as the instructions to 3D print the parts. And advise if it's your first time printing or if you don't own a 3D printer.
+
+## Install LeRobot 🤗
+
+To install LeRobot, follow our [Installation Guide](./installation)
+
+In addition to these instructions, you need to install the Feetech SDK:
+```bash
+pip install -e ".[feetech]"
+```
+
+## Step-by-Step Assembly Instructions
+
+## Remove the gears of the 6 leader motors
+
+
+Video removing gears
+
+
+
+
+
+Follow the video for removing gears. You need to remove the gear for the motors of the leader arm. As a result, you will only use the position encoding of the motor and reduce friction to more easily operate the leader arm.
+
+### Clean Parts
+Remove all support material from the 3D-printed parts. The easiest way to do this is using a small screwdriver to get underneath the support material.
+
+### Additional Guidance
+
+
+Video assembling arms
+
+
+
+
+
+**Note:**
+This video provides visual guidance for assembling the arms, but it doesn't specify when or how to do the wiring. Inserting the cables beforehand is much easier than doing it afterward. The first arm may take a bit more than 1 hour to assemble, but once you get used to it, you can assemble the second arm in under 1 hour.
+
+---
+
+### First Motor
+
+**Step 2: Insert Wires**
+- Insert two wires into the first motor.
+
+
+
+**Step 3: Install in Base**
+- Place the first motor into the base.
+
+
+
+**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).
+
+
+
+**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.
+
+
+
+ Video adding motor horn
+
+
+
+**Step 7: Attach Shoulder Part**
+- Route one wire to the back of the robot and the other to the left or towards you (see photo).
+- Attach the shoulder part.
+
+
+
+**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.
+
+
+
+**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.
+
+
+
+
+
+
+
+**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.
+
+
+
+**Step 14: Attach Upper Arm**
+- Attach the upper arm with 4 screws on each side.
+
+
+
+---
+
+### 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.
+
+
+
+**Step 17: Attach Forearm**
+- Connect the forearm to motor 3 using 4 screws on each side.
+
+
+
+---
+
+### 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.
+
+
+
+
+
+
+**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).
+
+
+
+**Step 20: Secure Motor 4 & Attach Horn**
+- Fasten motor 4 with 4 screws and attach its motor horns, use for one a horn screw.
+
+
+
+---
+
+### Wrist Assembly
+
+**Step 21: Install Motor 5**
+- Insert motor 5 into the wrist holder and secure it with 2 front screws.
+
+
+
+**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.
+
+
+
+**Step 23: Attach Wrist Horn**
+- Install only one motor horn on the wrist motor and secure it with a horn screw.
+
+
+
+---
+
+### Follower Configuration
+
+**Step 24: Attach Gripper**
+- Attach the gripper to motor 5.
+
+
+
+**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.
+
+
+
+**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.
+
+
+
+**Step 27: Mount Controller**
+- Attach the motor controller to the back of the robot.
+
+
+
+
+
+
+*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.
+
+
+
+**Step 25: Attach Handle**
+- Attach the handle to motor 5 using 4 screws.
+
+
+
+**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.
+
+
+
+**Step 27: Attach Trigger**
+- Attach the follower trigger with 4 screws.
+
+
+
+**Step 28: Mount Controller**
+- Attach the motor controller to the back of the robot.
+
+
+
+
+
+
+## Configure the motors
+
+### 1. Find the USB ports associated with each arm
+
+To find the port for each bus servo adapter, run this script:
+```bash
+python lerobot/find_port.py
+```
+
+
+
+
+Example output:
+
+```
+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 corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/tty.usbmodem575E0032081
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
+
+
+
+
+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
+```
+
+Example output:
+
+```
+Finding all available ports for the MotorBus.
+['/dev/ttyACM0', '/dev/ttyACM1']
+Remove the usb cable from your MotorsBus and press Enter when done.
+
+[...Disconnect corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/ttyACM1
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
+
+
+
+
+### 2. Set the motors ids and baudrates
+
+Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
+
+To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
+
+If you are repurposing motors from another robot, you will probably also need to perform this step as the ids and baudrate likely won't match.
+
+#### Follower
+
+Connect the usb cable from your computer and the power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
+
+For a visual reference on how to set the motor ids please refer to [this video](http://localhost:5173/so101#setup-motors-video) where we follow the process for the SO101 arm.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --robot.type=so100_follower \
+ --robot.port=/dev/tty.usbmodem585A0076841 # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.robots.so100_follower import SO100Follower, SO100FollowerConfig
+
+config = SO100FollowerConfig(
+ port="/dev/tty.usbmodem585A0076841",
+ id="my_awesome_follower_arm",
+)
+follower = SO100Follower(config)
+follower.setup_motors()
+```
+
+
+
+You should see the following instruction
+```
+Connect the controller board to the 'gripper' motor only and press enter.
+```
+
+As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
+
+
+Troubleshooting
+
+ If you get an error at that point, check your cables and make sure they are plugged in properly:
+
+
Power supply
+
USB cable between your computer and the controller board
+
The 3-pin cable from the controller board to the motor
+
+
+If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
+
+
+You should then see the following message:
+```
+'gripper' motor id set to 6
+```
+
+Followed by the next instruction:
+```
+Connect the controller board to the 'wrist_roll' motor only and press enter.
+```
+
+You can disconnect the 3-pin cable from the controller board, but you can leave it connected to the gripper motor on the other end, as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
+
+Repeat the operation for each motor as instructed.
+
+> [!TIP]
+> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
+
+When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
+
+#### Leader
+Do the same steps for the leader arm.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --teleop.type=so100_leader \
+ --teleop.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.teleoperators.so100_leader import SO100Leader, SO100LeaderConfig
+
+config = SO100LeaderConfig(
+ port="/dev/tty.usbmodem585A0076841",
+ id="my_awesome_leader_arm",
+)
+leader = SO100Leader(config)
+leader.setup_motors()
+```
+
+
+
+## Calibrate
+
+Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
+The calibration process is very important because it allows a neural network trained on one robot to work on another.
+
+#### Follower
+
+Run the following command or API example to calibrate the follower arm:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --robot.type=so100_follower \
+ --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.robots.so100_follower import SO100FollowerConfig, SO100Follower
+
+config = SO100FollowerConfig(
+ port="/dev/tty.usbmodem585A0076891",
+ id="my_awesome_follower_arm",
+)
+
+follower = SO100Follower(config)
+follower.connect(calibrate=False)
+follower.calibrate()
+follower.disconnect()
+```
+
+
+
+We unified the calibration method for most robots. Thus, the calibration steps for this SO100 arm are the same as the steps for the Koch and SO101. First, we have to move the robot to the position where each joint is in the middle of its range, then we press `Enter`. Secondly, we move all joints through their full range of motion. A video of this same process for the SO101 as reference can be found [here](http://localhost:5173/so101#calibration-video)
+
+#### Leader
+
+Do the same steps to calibrate the leader arm, run the following command or API example:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --teleop.type=so100_leader \
+ --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.teleoperators.so100_leader import SO100LeaderConfig, SO100Leader
+
+config = SO100LeaderConfig(
+ port="/dev/tty.usbmodem58760431551",
+ id="my_awesome_leader_arm",
+)
+
+leader = SO100Leader(config)
+leader.connect(calibrate=False)
+leader.calibrate()
+leader.disconnect()
+```
+
+
+
+Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
+
+> [!TIP]
+> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).
diff --git a/lerobot/common/robots/so101_follower/so101.md b/lerobot/common/robots/so101_follower/so101.md
new file mode 100644
index 000000000..4105bd34d
--- /dev/null
+++ b/lerobot/common/robots/so101_follower/so101.md
@@ -0,0 +1,373 @@
+# SO-101
+
+In the steps below, we explain how to assemble our flagship robot, the SO-101.
+
+## 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 advise if it's your first time printing or if you don't own a 3D printer.
+
+## Install LeRobot 🤗
+
+To install LeRobot, follow our [Installation Guide](./installation)
+
+In addition to these instructions, you need to install the Feetech SDK:
+```bash
+pip install -e ".[feetech]"
+```
+
+## Step-by-Step Assembly Instructions
+
+The follower arm uses 6x STS3215 motors with 1/345 gearing. The leader, however, uses three differently geared motors to make sure it can both sustain its own weight and it can be moved without requiring much force. Which motor is needed for which joint is shown in the table below.
+
+| Leader-Arm Axis | Motor | Gear Ratio |
+|-----------------|:-------:|:----------:|
+| Base / Shoulder Yaw | 1 | 1 / 191 |
+| Shoulder Pitch | 2 | 1 / 345 |
+| Elbow | 3 | 1 / 191 |
+| Wrist Roll | 4 | 1 / 147 |
+| Wrist Pitch | 5 | 1 / 147 |
+| Gripper | 6 | 1 / 147 |
+
+### Clean Parts
+Remove all support material from the 3D-printed parts. The easiest way to do this is using a small screwdriver to get underneath the support material.
+
+### Joint 1
+
+- Place the first motor into the base.
+- Fasten the motor with 4 M2x6mm screws (smallest screws). Two from the top and two from the bottom.
+- Slide over the first motor holder and fasten it using two M2x6mm screws (one on each side).
+- Install both motor horns, securing the top horn with a M3x6mm screw.
+- Attach the shoulder part.
+- Tighten the shoulder part with 4 M3x6mm screws on top and 4 M3x6mm screws on the bottom
+- Add the shoulder motor holder.
+
+
+
+
+
+### Joint 2
+
+- Slide the second motor in from the top.
+- Fasten the second motor with 4 M2x6mm screws.
+- Attach both motor horns to motor 2, again use the M3x6mm horn screw.
+- Attach the upper arm with 4 M3x6mm screws on each side.
+
+
+
+
+
+### Joint 3
+
+- Insert motor 3 and fasten using 4 M2x6mm screws
+- Attach both motor horns to motor 3 and secure one again with a M3x6mm horn screw.
+- Connect the forearm to motor 3 using 4 M3x6mm screws on each side.
+
+
+
+
+
+### Joint 4
+
+- Slide over motor holder 4.
+- Slide in motor 4.
+- Fasten motor 4 with 4 M2x6mm screws and attach its motor horns, use a M3x6mm horn screw.
+
+
+
+
+
+### Joint 5
+
+- Insert motor 5 into the wrist holder and secure it with 2 M2x6mm front screws.
+- Install only one motor horn on the wrist motor and secure it with a M3x6mm horn screw.
+- Secure the wrist to motor 4 using 4 M3x6mm screws on both sides.
+
+
+
+
+
+### Gripper / Handle
+
+
+
+
+- Attach the gripper to motor 5, attach it to the motor horn on the wrist using 4 M3x6mm screws.
+- Insert the gripper motor and secure it with 2 M2x6mm screws on each side.
+- Attach the motor horns and again use a M3x6mm horn screw.
+- Install the gripper claw and secure it with 4 M3x6mm screws on both sides.
+
+
+
+
+
+
+
+
+- Mount the leader holder onto the wrist and secure it with 4 M3x6mm screws.
+- Attach the handle to motor 5 using 1 M2x6mm screw.
+- Insert the gripper motor, secure it with 2 M2x6mm screws on each side, attach a motor horn using a M3x6mm horn screw.
+- Attach the follower trigger with 4 M3x6mm screws.
+
+
+
+
+
+
+
+
+## Configure the motors
+
+### 1. Find the USB ports associated with each arm
+
+To find the port for each bus servo adapter, run this script:
+```bash
+python lerobot/find_port.py
+```
+
+
+
+
+Example output:
+
+```
+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 corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/tty.usbmodem575E0032081
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/tty.usbmodem575E0032081` corresponding to your leader or follower arm.
+
+
+
+
+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
+```
+
+Example output:
+
+```
+Finding all available ports for the MotorBus.
+['/dev/ttyACM0', '/dev/ttyACM1']
+Remove the usb cable from your MotorsBus and press Enter when done.
+
+[...Disconnect corresponding leader or follower arm and press Enter...]
+
+The port of this MotorsBus is /dev/ttyACM1
+Reconnect the USB cable.
+```
+
+Where the found port is: `/dev/ttyACM1` corresponding to your leader or follower arm.
+
+
+
+
+### 2. Set the motors ids and baudrates
+
+Each motor is identified by a unique id on the bus. When brand new, motors usually come with a default id of `1`. For the communication to work properly between the motors and the controller, we first need to set a unique, different id to each motor. Additionally, the speed at which data is transmitted on the bus is determined by the baudrate. In order to talk to each other, the controller and all the motors need to be configured with the same baudrate.
+
+To that end, we first need to connect to each motor individually with the controller in order to set these. Since we will write these parameters in the non-volatile section of the motors' internal memory (EEPROM), we'll only need to do this once.
+
+If you are repurposing motors from another robot, you will probably also need to perform this step as the ids and baudrate likely won't match.
+
+The video below shows the sequence of steps for setting the motor ids.
+
+##### Setup motors video
+
+
+
+
+
+#### Follower
+
+Connect the usb cable from your computer and the power supply to the follower arm's controller board. Then, run the following command or run the API example with the port you got from the previous step. You'll also need to give your leader arm a name with the `id` parameter.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --robot.type=so101_follower \
+ --robot.port=/dev/tty.usbmodem585A0076841 # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.robots.so101_follower import SO101Follower, SO101FollowerConfig
+
+config = SO101FollowerConfig(
+ port="/dev/tty.usbmodem585A0076841",
+ id="my_awesome_follower_arm",
+)
+follower = SO101Follower(config)
+follower.setup_motors()
+```
+
+
+
+You should see the following instruction
+```bash
+Connect the controller board to the 'gripper' motor only and press enter.
+```
+
+As instructed, plug the gripper's motor. Make sure it's the only motor connected to the board, and that the motor itself is not yet daisy-chained to any other motor. As you press `[Enter]`, the script will automatically set the id and baudrate for that motor.
+
+
+Troubleshooting
+
+ If you get an error at that point, check your cables and make sure they are plugged in properly:
+
+
Power supply
+
USB cable between your computer and the controller board
+
The 3-pin cable from the controller board to the motor
+
+
+ If you are using a Waveshare controller board, make sure that the two jumpers are set on the `B` channel (USB).
+
+
+You should then see the following message:
+```bash
+'gripper' motor id set to 6
+```
+
+Followed by the next instruction:
+```bash
+Connect the controller board to the 'wrist_roll' motor only and press enter.
+```
+
+You can disconnect the 3-pin cable from the controller board, but you can leave it connected to the gripper motor on the other end, as it will already be in the right place. Now, plug in another 3-pin cable to the wrist roll motor and connect it to the controller board. As with the previous motor, make sure it is the only motor connected to the board and that the motor itself isn't connected to any other one.
+
+Repeat the operation for each motor as instructed.
+
+> [!TIP]
+> Check your cabling at each step before pressing Enter. For instance, the power supply cable might disconnect as you manipulate the board.
+
+When you are done, the script will simply finish, at which point the motors are ready to be used. You can now plug the 3-pin cable from each motor to the next one, and the cable from the first motor (the 'shoulder pan' with id=1) to the controller board, which can now be attached to the base of the arm.
+
+#### Leader
+Do the same steps for the leader arm.
+
+
+
+```bash
+python -m lerobot.setup_motors \
+ --teleop.type=so101_leader \
+ --teleop.port=/dev/tty.usbmodem575E0031751 # <- paste here the port found at previous step
+```
+
+
+```python
+from lerobot.common.teleoperators.so101_leader import SO101Leader, SO101LeaderConfig
+
+config = SO101LeaderConfig(
+ port="/dev/tty.usbmodem585A0076841",
+ id="my_awesome_leader_arm",
+)
+leader = SO101Leader(config)
+leader.setup_motors()
+```
+
+
+
+## Calibrate
+
+Next, you'll need to calibrate your robot to ensure that the leader and follower arms have the same position values when they are in the same physical position.
+The calibration process is very important because it allows a neural network trained on one robot to work on another.
+
+#### Follower
+
+Run the following command or API example to calibrate the follower arm:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --robot.type=so101_follower \
+ --robot.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --robot.id=my_awesome_follower_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.robots.so101_follower import SO101FollowerConfig, SO101Follower
+
+config = SO101FollowerConfig(
+ port="/dev/tty.usbmodem585A0076891",
+ id="my_awesome_follower_arm",
+)
+
+follower = SO101Follower(config)
+follower.connect(calibrate=False)
+follower.calibrate()
+follower.disconnect()
+```
+
+
+
+The video below shows how to perform the calibration. First you need to move the robot to the position where all joints are in the middle of their ranges. Then after pressing enter you have to move each joint through its full range of motion.
+
+##### Calibration video
+
+
+
+
+
+#### Leader
+
+Do the same steps to calibrate the leader arm, run the following command or API example:
+
+
+
+```bash
+python -m lerobot.calibrate \
+ --teleop.type=so101_leader \
+ --teleop.port=/dev/tty.usbmodem58760431551 \ # <- The port of your robot
+ --teleop.id=my_awesome_leader_arm # <- Give the robot a unique name
+```
+
+
+```python
+from lerobot.common.teleoperators.so101_leader import SO101LeaderConfig, SO101Leader
+
+config = SO101LeaderConfig(
+ port="/dev/tty.usbmodem58760431551",
+ id="my_awesome_leader_arm",
+)
+
+leader = SO101Leader(config)
+leader.connect(calibrate=False)
+leader.calibrate()
+leader.disconnect()
+```
+
+
+
+Congrats 🎉, your robot is all set to learn a task on its own. Start training it by following this tutorial: [Getting started with real-world robots](./getting_started_real_world_robot)
+
+> [!TIP]
+> If you have any questions or need help, please reach out on [Discord](https://discord.com/invite/s3KuuzsPFb).