Instructions to use cagataydev/smolvla-tictactoe with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use cagataydev/smolvla-tictactoe with LeRobot:
# See https://github.com/huggingface/lerobot?tab=readme-ov-file#installation for more details git clone https://github.com/huggingface/lerobot.git cd lerobot pip install -e .[smolvla]
# Launch finetuning on your dataset python lerobot/scripts/train.py \ --policy.path=cagataydev/smolvla-tictactoe \ --dataset.repo_id=lerobot/svla_so101_pickplace \ --batch_size=64 \ --steps=20000 \ --output_dir=outputs/train/my_smolvla \ --job_name=my_smolvla_training \ --policy.device=cuda \ --wandb.enable=true
# Run the policy using the record function python -m lerobot.record \ --robot.type=so101_follower \ --robot.port=/dev/ttyACM0 \ # <- Use your port --robot.id=my_blue_follower_arm \ # <- Use your robot id --robot.cameras="{ front: {type: opencv, index_or_path: 8, width: 640, height: 480, fps: 30}}" \ # <- Use your cameras --dataset.single_task="Grasp a lego block and put it in the bin." \ # <- Use the same task description you used in your dataset recording --dataset.repo_id=HF_USER/dataset_name \ # <- This will be the dataset name on HF Hub --dataset.episode_time_s=50 \ --dataset.num_episodes=10 \ --policy.path=cagataydev/smolvla-tictactoe - Notebooks
- Google Colab
- Kaggle
smolvla_tictactoe_a
Post-tuned SmolVLA policy for the SO-101 arm playing tic-tac-toe by picking and placing coloured blocks.
Trained end-to-end through the strands-robots training abstraction on the public HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1 LeRobot v3 dataset.
This model is the "block-A" post-tune — first of a planned series. Vision + language + 6-DoF joint action, 30 Hz control.
TL;DR
| Base model | lerobot/smolvla_base |
| Dataset | HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1 (195 episodes, ~144k frames @ 30fps) |
| Robot | SO-101 follower (6-DoF, shoulder_pan / shoulder_lift / elbow_flex / wrist_flex / wrist_roll / gripper) |
| Cameras | 2× RGB: observation.images.top (mapped → camera1), observation.images.wrist (mapped → camera2) |
| Training | 20,000 steps · batch 8 · full fine-tune · single H100/Thor GPU |
| Final loss | 0.088 (after 2h 20m) |
| Trainer | strands_robots.training.create_trainer("lerobot_local", policy_type="smolvla") |
| Hardware | NVIDIA Thor (aarch64) |
Quick load
With strands_robots (recommended)
from strands_robots import create_policy
policy = create_policy(
"lerobot_local",
pretrained_name_or_path="cagataydev/smolvla_tictactoe_a",
policy_type="smolvla",
device="cuda",
trust_remote_code=True,
)
# → policy.get_actions(observation, instruction=...) returns 6-DoF SO-101 actions.
With raw LeRobot
from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy
policy = SmolVLAPolicy.from_pretrained("cagataydev/smolvla_tictactoe_a")
In strands_robots simulation (MuJoCo)
from strands_robots import Robot
sim = Robot("so101", mesh=False)
sim.add_camera(name="front", position=[0.50, 0, 0.30], target=[0.22, 0.025, 0.05])
sim.add_camera(name="wrist", position=[0.0, 0.0, 0.05], target=[0.05, 0, 0],
parent_body="so101/gripper", fov=70.0)
sim.run_policy(
robot_name="so101",
policy_provider="lerobot_local",
policy_config={
"pretrained_name_or_path": "cagataydev/smolvla_tictactoe_a",
"policy_type": "smolvla",
"device": "cuda",
},
instruction="pick up the red block and place it on the top-left square",
n_steps=500,
control_frequency=30,
)
Camera name contract: SmolVLA base expects
observation.images.camera1/camera2.
The dataset usestop+wrist→ training was done with a rename map (see below). At inference in sim, mount your two cameras and route them through the samerename_mapinpolicy_config, or name your sim camstopandwristand let the embodiment do the routing.
The task
Post-tuned to control an SO-101 arm to pick up a coloured block and place it on a 3×3 tic-tac-toe grid.
Instructions look like:
"pick up the red block and place it on the center square""place a blue block on the top-right""put your piece on the middle-left square"
The A variant is trained on the cleaned "block-A" split (single-block-type demonstrations, 195 episodes). Future variants (_b, _multiblock, ...) will follow.
Training recipe
Training was driven entirely through the strands_robots.training DX, not raw lerobot.scripts.lerobot_train — this was an end-to-end test of that abstraction on a real public HF dataset.
Full config
from strands_robots.training import TrainSpec, create_trainer
trainer = create_trainer("lerobot_local", policy_type="smolvla")
spec = TrainSpec(
dataset_repo_id="HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1",
base_model="lerobot/smolvla_base",
output_dir="./checkpoints",
steps=20_000,
global_batch_size=8,
method="full", # full fine-tune (not LoRA, not expert-only)
save_freq=1000,
num_gpus=1,
seed=42,
streaming=False, # full local download
extra={
"policy_type": "smolvla",
"job_name": "smolvla_tictactoe_a",
"rename_map": {
"observation.images.top": "observation.images.camera1",
"observation.images.wrist": "observation.images.camera2",
},
},
)
result = trainer.train(spec)
# → status=success, checkpoint at result.checkpoint_dir
Hyperparameters
| Knob | Value |
|---|---|
| Steps | 20,000 |
| Global batch size | 8 |
| Learning rate | LeRobot default cosine schedule (peak ≈ 1.5e-4, end ≈ 2.5e-6) |
| Optimizer | AdamW |
| Method | full (all params trainable) |
| Precision | bf16 mixed |
| Chunk size | 50 (SmolVLA default) |
| GPUs | 1 × NVIDIA Thor |
| Seed | 42 |
| Data | full download, LeRobot v3 format |
| Save frequency | every 1000 steps |
| Wall-clock | 2h 20m (≈2.4 steps/s average) |
Loss curve (from training log)
| Step | Samples | Epoch | Loss | Grad-norm | LR |
|---|---|---|---|---|---|
| 200 | 2K | 0.01 | 0.352 | 6.19 | 1.5e-05 |
| 400 | 3K | 0.02 | 0.254 | 4.73 | 4.5e-05 |
| 600 | 5K | 0.03 | 0.273 | 4.82 | 7.5e-05 |
| 5,000 | ~40K | 0.26 | 0.147 | ~2.1 | ~1.4e-04 |
| 10,000 | ~80K | 0.52 | 0.109 | ~1.8 | ~9.8e-05 |
| 14,000 | ~112K | 0.72 | 0.097 | ~1.7 | ~4.9e-05 |
| 15,000 | 120K | 0.78 | 0.095 | 1.71 | 1.7e-05 |
| 18,000 | 144K | 1.00 | 0.092 | 1.57 | 5.1e-06 |
| 19,000 | 152K | 1.05 | 0.087 | 1.53 | 3.2e-06 |
| 20,000 | 160K | 1.11 | 0.088 | 1.61 | 2.5e-06 |
Loss dropped from 0.35 → 0.088 over 20K steps. First epoch reached at ~step 18,000 (1× full traversal of 144K frames).
Checkpoints
Automatic checkpoints saved every 1000 steps to checkpoints/{step}/pretrained_model. This upload is the step 20,000 / final checkpoint.
Dataset details
| Source | HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1 |
| Format | LeRobot v3 (parquet) |
| Episodes | 195 |
| Frames | ~144,000 |
| FPS | 30 |
| Robot type | so101 |
| State/action dim | 6 (joint positions) |
| Camera resolution | 480×640 |
Feature routing (rename_map):
observation.images.top → observation.images.camera1
observation.images.wrist → observation.images.camera2
camera3 is not populated — SmolVLA-base tolerates missing 3rd cam when only 2 of 3 slots are provided, but you should apply the same rename at inference time.
Files in this repo
| File | Purpose |
|---|---|
config.json |
Full SmolVLA policy config (frozen at training time) |
model.safetensors |
906 MB, all trainable params (VLM backbone + action expert) |
policy_preprocessor.json + .safetensors |
Input normalizers (state/image scaling) |
policy_postprocessor.json + .safetensors |
Output unnormalizer (action denormalization) |
train_config.json |
Full training config for reproducibility (steps, batch, lr schedule, seed, …) |
policy_preprocessor.json and policy_postprocessor.json are required for correct action denormalization — do not omit them when loading.
Reproducibility
To reproduce this exact model on your hardware:
git clone https://github.com/strands-labs/robots
cd robots && pip install -e '.[lerobot,smolvla]'
Then run the shipped training script:
# train_smolvla_tictactoe.py — same script that trained this checkpoint
from strands_robots.training import TrainSpec, create_trainer
trainer = create_trainer("lerobot_local", policy_type="smolvla")
spec = TrainSpec(
dataset_repo_id="HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1",
base_model="lerobot/smolvla_base",
output_dir="./checkpoints",
steps=20_000,
global_batch_size=8,
method="full",
save_freq=1000,
seed=42,
extra={
"policy_type": "smolvla",
"rename_map": {
"observation.images.top": "observation.images.camera1",
"observation.images.wrist": "observation.images.camera2",
},
},
)
trainer.train(spec)
Wall-clock on NVIDIA Thor: 2h 20m for 20K steps.
Evaluation
Post-training in-domain training loss: 0.088.
Real-world evaluation on physical SO-101 hardware and MuJoCo sim (using the strands_robots sim pipeline to recreate the tic-tac-toe scene) is in progress — will publish success rates + video rollouts as a follow-up here + on Telegram/GitHub.
Planned eval metrics:
- Task success rate: block correctly placed on target grid square (10 trials × 9 squares × 3 colours).
- Sim-to-real gap: same policy in MuJoCo scene reconstruction vs. real SO-101.
- Instruction following: text-conditioned generalisation to unseen instructions (colour × position combos).
Known limitations
- Single dataset variant — trained only on the "block-A clean" split. May not generalise to different block colours/shapes without further tuning.
- Two cameras only — no wrist-only or third-person-only ablation yet.
- 6-DoF joint control — inherits the SO-101 kinematic constraints (no end-effector cartesian control).
- Rename map is required at inference — if you name cameras differently, you must apply the same routing.
- No LoRA / expert-only variant yet — full fine-tune only (906 MB). LoRA variants are planned to drop below 100 MB.
Citation & lineage
- Base:
lerobot/smolvla_base(SmolVLA architecture, HuggingFace / LeRobot team) - Dataset:
HashtagRobotics/tic-tac-toe-so101-block-a-clean-v1 - Training abstraction:
strands_robots.training - Trainer/uploader: @cagataycali
- Compute: NVIDIA Thor dev kit (aarch64)
Please cite the base model + dataset if you use this checkpoint.
Contact
- Issues / questions → strands-labs/robots issues
- Author → @cagataycali
- License → Apache-2.0 (matches base model)
- Downloads last month
- 5
Model tree for cagataydev/smolvla-tictactoe
Base model
lerobot/smolvla_baseDataset used to train cagataydev/smolvla-tictactoe
Evaluation results
- Final Training Loss on tic-tac-toe-so101-block-a-clean-v1self-reported0.088
- Loss After Padding Removal on tic-tac-toe-so101-block-a-clean-v1self-reported0.085