
LeKiwi puts an SO-100 class arm on a three-wheel holonomic base. What the extra base dimensions do to the action vector, and what really changes when you record and train.
LeKiwi is an SO-100 class arm bolted onto a three-wheel omnidirectional base with a Raspberry Pi 5 in between. The arm half you know: six Feetech STS3215 servos driving five joints and a gripper, the same degrees of freedom and the same calibration dance as any SO-100. The other half changes the shape of the problem. The robot stops being six servo positions and becomes nine dimensions, three of them velocities in different units, measured rather than commanded, with no position feedback anywhere in the loop.
This is about that vector: where it comes from, what the kiwi drive matrix computes, which numbers are hard limits, and what changes when you record episodes and fine-tune a policy. Everything below was read out of lerobot main in August 2026 (pyproject on main reads version 0.6.2; the newest tagged release is v0.6.1, published 3 August 2026) and the LeKiwi build repository. Where docs and code disagree, and twice they do, the code wins.
What you need to know
- •The action vector has nine entries: six arm joint positions plus x.vel and y.vel in m/s and theta.vel in deg/s. The observation state has the same nine.
- •The base entries are velocities, not positions. No odometry, no pose. A policy can only know where it is from the camera images.
- •The wheels sit at 150, -90 and 30 degrees. One 3x3 matrix maps body velocity to wheel speed; its inverse decodes the feedback back.
- •A 3000-tick wheel cap limits the base to about 0.27 m/s forward and 0.23 m/s sideways. The published table promises 0.4 m/s and the code does not deliver it.
- •Two defaults eat your first evening: the host exits after 30 seconds, and the base halts if no command arrives for 500 ms.
- •SmolVLA and Pi0.5 pad state and action to 32 dims, so nine costs nothing. ACT reads the dataset dimension. Only GR00T needs a modality config with a base slice.
What LeKiwi actually is
LeKiwi v1 is maintained by SIG Robotics at the University of Illinois with the Hugging Face LeRobot team. It is a build repository, not a product: bill of materials, STLs, assembly guide, Fusion 360 model, exported URDF. The arm on top is an SO-ARM101, sharing servo family and code paths with the SO-100. Here it is a compatible arm rather than the reference one; the side-by-side is at SO-100 vs LeKiwi.
| Subsystem | LeKiwi v1 | Detail that matters later |
|---|---|---|
| Arm | SO-ARM101, six STS3215 | ids 1 to 6, position mode |
| Base | three 4 inch omni wheels, 120 degrees apart | kiwi drive, holonomic |
| Base motors | three STS3215 on the same bus | ids 7, 8, 9, velocity mode |
| Compute | Raspberry Pi 5, 4 GB in the BOM | runs the host loop |
| Cameras | one workspace, one wrist, USB | defaults /dev/video0 and /dev/video2 |
| Power | 12 V 5 A Li-ion, or a USB-C laptop power bank | a third build is wired |
| Link | ZeroMQ, 5555 out and 5556 back | commands out, JPEG frames back |
The upstream bill of materials quotes three builds. Full totals include two arms, because leader-follower teleoperation needs a leader as well as the follower on the base.
| Build | US | EU | Contents |
|---|---|---|---|
| 12 V, complete | 482 USD | 545.80 EUR | base plus two arms, 12 V servos |
| 5 V, complete | 499 USD | 526.00 EUR | base plus two arms, 7.4 V servos |
| Base only, 12 V | 251.50 USD | 307.80 EUR | if you own two arms |
| Base only, 5 V | 248 USD | 295.00 EUR | easiest first build |
| Base only, wired | 184 USD | 235.00 EUR | cheapest, tethered |
Those exclude 3D printing. The parts cost listed here for a LeKiwi is about 400 to 500 EUR, the same territory. Either way it lands at roughly three times the parts cost of an SO-101 (about 130 to 170 EUR), and the difference is base, battery and Pi.
The STS3215 ships in a 7.4 V and a 12 V variant that look identical. The 7.4 V part is rated 16.5 kg.cm stall torque at 6 V, the 12 V part 30 kg.cm. Feeding 12 V into a 7.4 V servo destroys it. Upstream keeps each build on one voltage: the 5 V build uses 7.4 V servos throughout, the 12 V build uses 12 V servos throughout and steps the battery down to 5 V only for the Pi. The arm catalog here lists LeKiwi as a 7.4 V arm on a 12 V base, so check which servo variant is actually in your kit before you wire anything. If a joint has already gone quiet, start at servo not responding.
The kiwi drive is one 3x3 matrix
A kiwi drive is three omni wheels 120 degrees apart. Free rollers across each axis let a wheel be dragged sideways while it drives, so the three constraints together leave the chassis all three planar freedoms at once: forward, sideways, yaw. A differential-drive robot has to turn before it moves sideways. LeKiwi does not. The forward model is four lines.
# src/lerobot/robots/lekiwi/lekiwi.py, _body_to_wheel_raw defaults: wheel_radius=0.05, base_radius=0.125
angles = np.radians(np.array([240, 0, 120]) - 90) # -> 150, -90, 30 degrees
m = np.array([[np.cos(a), np.sin(a), base_radius] for a in angles])
wheel_linear_speeds = m.dot(np.array([x, y, theta_rad])) # m/s at the rim
wheel_angular_speeds = wheel_linear_speeds / wheel_radius # rad/s| Wheel | Motor id | Mount angle | Row of M: x, y, theta |
|---|---|---|---|
| base_left_wheel | 7 | 150 deg | -0.866, 0.500, 0.125 |
| base_back_wheel | 8 | -90 deg | 0.000, -1.000, 0.125 |
| base_right_wheel | 9 | 30 deg | 0.866, 0.500, 0.125 |
The determinant is 0.325, so the matrix inverts cleanly and runs backwards to turn measured wheel speeds into a body velocity. That inverse is how x.vel, y.vel and theta.vel reach the observation. It also says forward speed comes only from the left and right wheels, and yaw is a third of the wheel sum over the base radius.
Then the part nobody documents. Wheel commands become raw ticks at 4096 per revolution, and if any wheel exceeds max_raw = 3000 all three scale down proportionally. 3000 ticks is 263.7 deg/s, or 0.230 m/s at a 0.05 m rim. Push that back through the matrix and you get the real envelope.
| Command | Peak wheel speed | Scale | What the base does |
|---|---|---|---|
| 0.10 m/s forward | 0.087 m/s | 1.00 | 0.10 m/s |
| 0.20 m/s forward | 0.173 m/s | 1.00 | 0.20 m/s |
| 0.30 m/s forward | 0.260 m/s | 0.886 | 0.27 m/s |
| 0.30 m/s sideways | 0.300 m/s | 0.767 | 0.23 m/s |
| 90 deg/s spin | 0.196 m/s | 1.00 | 90 deg/s |
| 0.30 m/s plus 90 deg/s | 0.456 m/s | 0.504 | about half of both |
Scaling all three wheels by one factor keeps the direction of travel through clipping and only drops the magnitude, so a diagonal command never bends into a curve. The cost: the base silently goes slower than asked, worst when driving and spinning at once, where a full command comes out at roughly half strength.
That matters when reading a recorded LeRobot dataset. The stored action is the requested velocity before scaling; the state is decoded from what the wheels reported. Wherever the request exceeded the cap, action and state disagree by construction, and no tuning closes that gap.
Six positions, three velocities, one vector
Both observation_features and action_features on the LeKiwi class return the same dictionary. That one decision drives most of what follows.
# both the observation state and the action are this, in this order
(
"arm_shoulder_pan.pos",
"arm_shoulder_lift.pos",
"arm_elbow_flex.pos",
"arm_wrist_flex.pos",
"arm_wrist_roll.pos",
"arm_gripper.pos",
"x.vel",
"y.vel",
"theta.vel",
)| Index | Key | Unit | Kind | Origin |
|---|---|---|---|---|
| 0-4 | arm_shoulder_pan.pos ... arm_wrist_roll.pos | degrees | position | Present_Position, ids 1 to 5 |
| 5 | arm_gripper.pos | 0 to 100 | position | Present_Position, id 6, normalised 0 to 100 |
| 6 | x.vel | m/s | velocity | M inverse of Present_Velocity, ids 7 to 9 |
| 7 | y.vel | m/s | velocity | same three readings |
| 8 | theta.vel | deg/s | velocity | same three readings |
Degrees for the arm is a default aligned with the SO-100 in May 2026 (use_degrees = True). Older LeKiwi datasets used a normalised -100 to 100 range, so check units before trusting a recording from the public directory.
Three ways the base dimensions are not joints
- They are velocities, so there is nothing to integrate. No pose, no odometry, no map is stored. Integrating x.vel drifts, because omni rollers slip and nothing corrects it. The policy locates itself from pixels alone.
- State and action are the same quantity. On the arm, state is a measured position and action a target, separated by a real tracking error. On the base they are the decoded and the commanded body velocity, one step apart. A policy that copies state into action scores well on those columns while learning nothing, and the loss curve looks healthy. See loss falls, policy does nothing.
- They are zero most of the time. The base drives for a second, then holds still while the arm works. Every normaliser has to cope with a spike at zero and thin tails, the shape min-max and mean-std both handle badly.
Load the parquet, take the three base columns, print the fraction of frames where all three are exactly zero. Above roughly 90 percent and you have an arm dataset with three noisy columns attached. Fix it in the teleoperation protocol, not in loss weights: record episodes where driving is part of the task, not ones where you drove into position and then started the clock.

What changes when you record
Recording is a two-machine affair. A host on the Pi owns the serial bus and the cameras; a client on your laptop owns the leader arm, the keyboard and the dataset writer. ZeroMQ carries commands on 5555 with CONFLATE set, so only the newest survives a backlog, and multipart observations on 5556 with a two-deep queue that sheds stale frames. The host loop is capped at 30 Hz.
- 1Install the lekiwi extra on both machines
It pulls in the Feetech SDK and pyzmq. Needed on the Pi and the laptop.
bashpip install -e ".[lekiwi]" - 2Set all nine motor ids
One control board serves the whole robot, so ids must be unique across arm and base. The helper walks the arm down from 6 to 1, then the wheels from 9 to 7.
bashlerobot-find-port lerobot-setup-motors \ --robot.type=lekiwi \ --robot.port=/dev/ttyACM0 - 3Calibrate the arm. The wheels do not need it
Wheels and wrist roll count as full-turn motors: offset 0, range 0 to 4095, no interaction. Run it over SSH on the Pi.
bashlerobot-calibrate \ --robot.type=lekiwi \ --robot.id=my_awesome_kiwi - 4Start the host, and give it more than 30 seconds
connection_time_s defaults to 30 and the loop exits when it elapses.
bashpython -m lerobot.robots.lekiwi.lekiwi_host \ --robot.id=my_awesome_kiwi \ --host.connection_time_s=3600 - 5Teleoperate from the laptop
Edit remote_ip first. The leader arm drives the six joints; w, a, s, d drive the base, z and x spin it, r and f step the speed preset.
bashpython examples/lekiwi/teleoperate.py - 6Record episodes
Set remote_ip, HF_REPO_ID, TASK_DESCRIPTION, NUM_EPISODES (default 2), EPISODE_TIME_SEC (30) and RESET_TIME_SEC (10) at the top. FPS is 30.
bashpython examples/lekiwi/record.py
1. The host quits after 30 seconds. The loop is while duration < connection_time_s, default 30. Pass --host.connection_time_s. 2. lerobot-record cannot record LeKiwi. The CLI takes one --teleop.type, but LeKiwi needs an arm teleoperator and a keyboard; record_loop takes a list and refuses anything else with "Currently only supported for LeKiwi robot". Use examples/lekiwi/record.py. 3. Base keyboard teleop needs a global key backend. X11, Windows, or macOS with Input Monitoring. Not Wayland, not headless SSH, and it fails quietly: the arm follows, the base never moves.
1. Speed. The LeRobot LeKiwi page lists modes at 0.4, 0.25 and 0.1 m/s. LeKiwiClient.speed_levels in the same repository holds 0.1, 0.2 and 0.3 m/s at 30, 60 and 90 deg/s, and the comment beside it still says "e.g. 0.1, 0.25, or 0.4". Clipping puts the fast preset near 0.27 m/s anyway. 2. Recording length. The same page tells you to raise NB_CYCLES_CLIENT_CONNECTION to record for longer. No such constant exists in examples/lekiwi/record.py; the knobs that are actually there are NUM_EPISODES and EPISODE_TIME_SEC. Annotate datasets from the config, not from the page.
One detail that surprises people later: the default cameras have different shapes. Front is 640 by 480 rotated 180; wrist is declared 480 wide by 640 tall and rotated 90, so LeRobot captures landscape and hands you a portrait frame. In the dataset that is (480, 640, 3) for front and (640, 480, 3) for wrist. Policies resize, but a portrait wrist view crops differently than you expect, and depth cameras are rejected outright: the transport carries colour only. See camera not detected when one never appears.
What changes when you train
Less than you would think for four of the five policies. Nine dimensions are small enough that the modern stacks absorb them silently, and only GR00T N1.7 asks you to describe the vector.
| Policy | How it handles 9 dims | What you change |
|---|---|---|
| ACT | heads sized from the dataset, no padding | nothing |
| SmolVLA | pads state and action to max_state_dim = 32 | nothing |
| Pi0.5 | pads to 32 too, normalises with quantiles | nothing |
| GR00T N1.7 / N1.5 | reads named slices from meta/modality.json | add a base slice and one ActionConfig |
For ACT and SmolVLA you point lerobot-train at the dataset and the shapes follow. A nine-dimensional LeKiwi recording trains with the same command line as a six-dimensional SO-100 one.
lerobot-train \
--dataset.repo_id=${HF_USER}/lekiwi_pick_and_carry \
--policy.type=act \
--output_dir=outputs/train/act_lekiwi \
--job_name=act_lekiwi \
--policy.device=cuda \
--policy.repo_id=${HF_USER}/act_lekiwiGR00T is the exception, and in two ways. It loads LeRobot v2.0 or v2.1 only, so a v3.0 recording has to be converted down before the loader will touch it (dataset rejected as v3). And it looks up named slices in meta/modality.json and transforms each separately, so a custom robot gets registered under the NEW_EMBODIMENT tag with its config path passed to the fine-tuning entry point.
{
"state": {
"single_arm": {"start": 0, "end": 5},
"gripper": {"start": 5, "end": 6},
"base": {"start": 6, "end": 9}
},
"action": {
"single_arm": {"start": 0, "end": 5},
"gripper": {"start": 5, "end": 6},
"base": {"start": 6, "end": 9}
},
"video": {
"front": {"original_key": "observation.images.front"},
"wrist": {"original_key": "observation.images.wrist"}
},
"annotation": {
"human.task_description": {"original_key": "task_index"}
}
}"action": ModalityConfig(
delta_indices=list(range(0, 16)),
modality_keys=["single_arm", "gripper", "base"],
action_configs=[
ActionConfig(rep=ActionRepresentation.RELATIVE, type=ActionType.NON_EEF,
format=ActionFormat.DEFAULT), # arm: delta from current state
ActionConfig(rep=ActionRepresentation.ABSOLUTE, type=ActionType.NON_EEF,
format=ActionFormat.DEFAULT), # gripper: target position
ActionConfig(rep=ActionRepresentation.ABSOLUTE, type=ActionType.NON_EEF,
format=ActionFormat.DEFAULT), # base: already a velocity
],
)In GR00T, RELATIVE stores the action as a delta from the matching state. Sensible for joint angles, and what the SO-100 example uses. For the base it is nonsense: the state is already a velocity, so a relative base action is an acceleration, which the robot does not consume. At deployment the processor adds the measured velocity back on, injecting wheel-estimate error straight into the command. Keep the base ABSOLUTE, and remember that changing delta_indices means re-running gr00t/data/stats.py or training dies with an IndexError.
Normalisation is where mixed units bite
ACT and SmolVLA normalise state and action with mean and standard deviation, Pi0.5 uses quantiles, GR00T defaults to min-max per slice. All of them rescale each dimension independently, which is why nobody has to think about degrees sitting beside metres per second. It works, with one side effect worth naming.
After z-scoring, all nine dimensions contribute roughly equally to the loss. The base dimensions claim a third of the gradient budget even when the base moves in a minority of frames, and their small standard deviation amplifies wheel-feedback noise into large normalised targets. Not a reason to drop the base, but a reason to record episodes where it earns its place and to check generalisation across setups before trusting the result.

Two routes from a LeKiwi to a trained policy
You own the chain: Pi image, ZeroMQ link, dataset, GPU. The right choice if you are changing the robot itself, because every layer is a file you can edit.
- 1Build and wire
Print, assemble, set nine motor ids, calibrate both arms. Budget a weekend, most of it printing.
bashlerobot-setup-motors --robot.type=lekiwi --robot.port=/dev/ttyACM0 lerobot-calibrate --robot.type=lekiwi --robot.id=my_awesome_kiwi - 2Record 50 or more episodes
Host on the Pi, client on the laptop. Edit the example script; the CLI will not do it.
bash# Pi python -m lerobot.robots.lekiwi.lekiwi_host --robot.id=my_awesome_kiwi --host.connection_time_s=3600 # laptop python examples/lekiwi/record.py - 3Train on your own card or a rented one
ACT and SmolVLA fit on 24 GB. GR00T and Pi0.5 need 80 GB.
bashlerobot-train \ --dataset.repo_id=${HF_USER}/lekiwi_pick_and_carry \ --policy.type=smolvla \ --output_dir=outputs/train/smolvla_lekiwi \ --job_name=smolvla_lekiwi \ --policy.device=cuda - 4Serve next to the robot
Same network as the Pi. The base watchdog does not care why a command was late.
bashpython examples/lekiwi/evaluate.py
The cost: a GPU you provision yourself, an afternoon on driver versions, and the GR00T modality config, the one piece with no mobile-base example to copy.
The platform does not replace the LeKiwi host loop, and pretending otherwise would be dishonest: base teleoperation and the ZeroMQ link stay upstream. It replaces everything after the dataset exists. A LeRobot dataset can come from a Hugging Face repo id or from your own machine.
- Pick model, dataset and hyperparameters in a form. The backend rents a GPU on a spot market by required VRAM, runs the trainer and writes checkpoints to object storage. Start at the training matrix.
- Inference pods are auto-provisioned by
/api/inference/podand carry an idle watchdog that destroys the pod, so a forgotten 80 GB card does not bill silently. - Base checkpoints are the vendors' own: nvidia/GR00T-N1.7-3B, nvidia/GR00T-N1.5-3B, lerobot/pi05_base. ACT has no base model; it exists only after training on your task.
- The same operations run from the CLI and from AI agents via the MCP server.
- No robot yet? /live streams a physical arm with no signup, queue-based.
| Tier | Models | Run time | Cost per run |
|---|---|---|---|
| A100 80 GB or H100 80 GB | GR00T N1.7, GR00T N1.5, Pi0.5 | 3 to 6 hours | about 4 to 12 USD |
| RTX 4090 or any 24 GB card | SmolVLA, ACT | 2 to 5 hours | about 1 to 3 USD |
Minimum episodes differ: 30 for SmolVLA, 50 for the rest. Parameter counts and latency are on the policies page, costs at pricing.
Is the base worth it?
- The workspace stops being a table. The arm reaches a shelf and a bin without re-clamping either.
- Approach direction becomes free. The policy can strafe into alignment instead of a three-point turn.
- Three extra dimensions are nearly free: SmolVLA and Pi0.5 pad to 32 anyway, ACT sizes itself from the data.
- Same servo family, same bus, same calibration flow, same code paths as the arm you already run.
- Kits exist, and the repository ships CAD, STLs and a URDF for simulation.
- Nine dimensions but still no pose. Navigation has to be solved from images, which needs far more data than manipulation.
- Velocity actions do not replay. Re-running an episode restores the joint configuration, not the position on the floor.
- The base moves the cameras, so every frame carries motion blur and viewpoint change a static rig never sees.
- A 30 Hz host loop and a 500 ms watchdog put a hard floor under your latency budget.
- Three more servos, a battery, a Pi, a charging routine, and a network link that fails on its own.
Where the base does not help
The base makes the data problem harder faster than it makes the robot more capable. Manipulation from 50 episodes works because the task is short, the camera fixed and the start state nearly identical each time. Add a base and you have added navigation, with a much larger state space, no pose observation and the same 50 episodes. Expect a policy that manipulates well once it is in the right place and cannot get itself there.
Then latency, where the arithmetic is concrete. The base stops if no command arrives for 500 ms. One policy step costs 20 ms for ACT, 152 ms for GR00T N1.7, 165 ms for GR00T N1.5, 245 ms for SmolVLA and 485 ms for Pi0.5 (inference latency). Send one command per inference and Pi0.5 lands within 15 ms of the watchdog before the network does anything.
The watchdog calls stop_base(), which zeroes the wheel velocities and leaves the arm goals alone, so a latency problem does not look like one. It looks like a base that stutters while the arm carries on. The fix is not a faster model but action chunking: these policies predict 16 steps (GR00T), 50 (SmolVLA, Pi0.5) or 100 (ACT) at a time. One 485 ms Pi0.5 call covers 1.67 seconds of commands at 30 fps. Stream the chunk at the control rate instead of sending one and waiting.
The platform limit applies with extra force here. Remote inference over the public internet is viable for slow pick-and-place and not for fast reactive motion. On a mobile base the round trip that makes a grasp hesitant also makes the base overshoot, because a late velocity command keeps executing until the next replaces it. For anything past a demo, put inference on the same LAN as the Pi.
A scope note to close. The desktop client and browser teleoperation here are built around the arm; the LeKiwi base loop, keyboard mapping and ZeroMQ host stay upstream code you run yourself. This side gives you the part after the dataset: a guide per model and arm, rented GPUs by VRAM, and a served checkpoint. If somebody else drives while you build the dataset, the operator side is its own job. Background: the SO-100 hub, the SO-100 complete guide and collecting high-quality VLA data.

Train a policy on a LeKiwi
Every model and arm pair has its own guide: the GPU tier it needs, the dataset format it accepts, and the defaults the trainer really sends. GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT, each on a LeKiwi, each run start to finish.
Open the training matrixHow many dimensions does a LeKiwi action have?▾
Nine. Six arm joint positions (five joints in degrees plus a gripper on a 0 to 100 scale) and three base velocities: x.vel and y.vel in m/s, theta.vel in deg/s. The observation state carries the same nine in the same order, so observation.state and action both have shape (9,).
Can I use lerobot-record to record a LeKiwi dataset?▾
No. The CLI accepts a single --teleop.type, but LeKiwi needs an arm teleoperator and a keyboard at once. record_loop accepts a list and raises a ValueError for anything that is not exactly one KeyboardTeleop plus one arm leader on a lekiwi_client robot. Use examples/lekiwi/record.py.
Why does my LeKiwi stop after 30 seconds?▾
Not a crash. LeKiwiHostConfig.connection_time_s defaults to 30, the host loop runs while elapsed time is below it, then prints 'Cycle time reached' and disconnects. Start the host with --host.connection_time_s set higher, for example 3600.
Do I need to change anything to train SmolVLA or ACT on nine dimensions?▾
No. ACT sizes its heads from the dataset features, and SmolVLA and Pi0.5 pad state and action to 32 dimensions regardless. Only GR00T needs work: add a base slice covering indices 6 to 9 in meta/modality.json and give it an ActionConfig with ABSOLUTE representation.
How fast can the LeKiwi base actually drive?▾
Roughly 0.27 m/s forward and 0.23 m/s sideways. Wheel commands cap at 3000 raw ticks, which is 263.7 deg/s or 0.230 m/s at the 0.05 m rim, and anything above scales all three wheels down proportionally. The documented 0.4 m/s fast preset is unreachable, and the client config asks for 0.3 m/s.
Can I run inference in the cloud for a LeKiwi?▾
For slow tasks yes, for anything reactive no. The base halts if no command arrives within 500 ms and a Pi0.5 step alone is 485 ms. Predict a chunk, stream it back at the 30 Hz control rate, and keep the serving machine on the same LAN as the Pi.
Sources
- SIGRobotics-UIUC/LeKiwi: Low-Cost Mobile Manipulator, Version 1
- LeKiwi Bill of Materials: 12 V, 5 V and wired build totals
- LeKiwi Assembly: wheel modules, base plates, motor mounting
- LeRobot documentation: LeKiwi setup, calibration, teleoperation, recording
- lerobot config_lekiwi.py: LeKiwiConfig, LeKiwiHostConfig, LeKiwiClientConfig
- lerobot lekiwi.py: motor table, _body_to_wheel_raw, _wheel_raw_to_body
- lerobot examples/lekiwi/record.py: the LeKiwi recording script
- lerobot_record.py: record_loop and the multi-teleop restriction
- LeRobot: An Open-Source Library for End-to-End Robot Learning, Cadene et al., 26 Feb 2026
- Isaac-GR00T: how to prepare your modality configuration
- Isaac-GR00T: fine-tune on custom embodiments (NEW_EMBODIMENT)
- lerobot configuration_smolvla.py: max_state_dim and max_action_dim of 32
- lerobot configuration_pi05.py: quantile normalisation and 32-dim padding
- TheRobotStudio/SO-ARM100: SO-101 and SO-100 arms, STS3215 torque ratings
- Modern Robotics: Mechanics, Planning and Control, chapter 13 Wheeled Mobile Robots (Lynch and Park, 2017)
Sources
- SIGRobotics-UIUC/LeKiwi: Low-Cost Mobile Manipulator, Version 1
- LeKiwi Bill of Materials: 12 V, 5 V and wired build totals
- LeKiwi Assembly: wheel modules, base plates, motor mounting
- LeRobot documentation: LeKiwi setup, calibration, teleoperation, recording
- lerobot config_lekiwi.py: LeKiwiConfig, LeKiwiHostConfig, LeKiwiClientConfig
- lerobot lekiwi.py: motor table, _body_to_wheel_raw, _wheel_raw_to_body
- lerobot examples/lekiwi/record.py: the LeKiwi recording script
- lerobot_record.py: record_loop and the multi-teleop restriction
- LeRobot: An Open-Source Library for End-to-End Robot Learning, Cadene et al., 26 Feb 2026
- Isaac-GR00T: how to prepare your modality configuration
- Isaac-GR00T: fine-tune on custom embodiments (NEW_EMBODIMENT)
- lerobot configuration_smolvla.py: max_state_dim and max_action_dim of 32
- lerobot configuration_pi05.py: quantile normalisation and 32-dim padding
- TheRobotStudio/SO-ARM100: SO-101 and SO-100 arms, STS3215 torque ratings
- Modern Robotics: Mechanics, Planning and Control, chapter 13 Wheeled Mobile Robots (Lynch and Park, 2017)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started