12. Configuração de câmeras e coleta de dados LeRobot
Capítulo 12 do curso para iniciantes em SO-ARM Physical AI — conecte e verifique as câmeras frontal e lateral, entenda a sincronização imagem/ação, grave seu primeiro dataset LeRobot com lerobot-record e domine os controles de teclado durante a gravação.
Retire itens de valor e pessoas alheias a até 1 metro do espaço de trabalho. Antes deste capítulo, confirme que o braço está bem fixado e que energia e cabos estão corretos.
12.1 Configuração de uma câmera vs. duas
12.1 Configuração de uma câmera vs. duas
A coleta e o treinamento deste curso exigem duas câmeras e duas vistas. Conforme a posição, os layouts comuns são:
| Layout | Instalação e propósito | Cenários |
|---|---|---|
| Vista da garras | Câmera superior cobrindo a área; câmera na garras observando o detalhe do agarre | Operações finas que exigem ver o contato garras-objeto |
| Vista lateral de apoio | Câmera superior na frente da mesa; câmera lateral observando a altura do braço e o movimento da garras | Pegar, transportar e colocar |

Este curso usa o layout de vista lateral de apoio (frontal + lateral), o mesmo do Capítulo 22 sem código.
12.2 Localizar os nomes de dispositivo das câmeras
12.2 Localizar os nomes de dispositivo das câmeras
O índice de uma câmera pode mudar ao reiniciar ou reconectar — comportamento que depende sobretudo do sistema operacional. Antes de cada sessão, detecte as câmeras ligadas:
lerobot-find-cameras opencv
O terminal imprime algo assim:
--- Detected Cameras ---
Camera #0:
Name: OpenCV Camera @ 0
Type: OpenCV
Id: 0
Backend api: AVFOUNDATION
Default stream profile:
Width: 1920
Height: 1080
Fps: 15.0
--------------------
(more cameras ...)
As fotos de cada câmera ficam em outputs/captured_images — abra-as para confirmar qual índice é a frontal e qual a lateral.
Evite ligar câmeras USB num hub. Conecte direto, de preferência em portas USB 3.0, para garantir banda suficiente.
12.3 Verificar as câmeras durante a teleoperação
12.3 Verificar as câmeras durante a teleoperação
Antes de gravar, confirme que as duas vistas estão ativas e bem enquadradas teleoperando com exibição ligada:
lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_awesome_follower_arm \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30, fourcc: "MJPG"}, side: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30, fourcc: "MJPG"}}" \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM1 \
--teleop.id=my_awesome_leader_arm \
--display_data=true
Os valores de index_or_path vêm do último dígito do ID reportado por lerobot-find-cameras opencv.
fourcc: "MJPG" pede quadros comprimidos. Com MJPG, esta configuração suporta 3 câmeras a 1920×1080 @ 30FPS. YUYV é sem compressão — reduz a resolução útil e o FPS e pode travar o braço. Não ligue duas câmeras no mesmo hub USB.
12.4 Opcional: câmeras de profundidade (RealSense / Orbbec)
12.4 Opcional: câmeras de profundidade (RealSense / Orbbec)
- RealSense D435i / D405
- Orbbec Gemini 2 / Gemini 336
As RealSense trazem percepção RGB-D para tarefas de reconhecimento e manipulação próxima. Recomendadas: D405 (estéreo de curto alcance, 7–50 cm, ideal para mesa) e D435i (profundidade + RGB + IMU).
1. Mude para a ramificação de câmeras — o suporte a profundidade está em DepthCameraSupport:
git checkout DepthCameraSupport
git pull origin DepthCameraSupport
git branch --show-current # esperado: DepthCameraSupport
2. Instale o extra RealSense:
pip install -e ".[realsense]"
3. Permissões e detecção:
chmod a+rw /dev/bus/usb/*/*
lerobot-find-cameras realsense
4. Exemplo — teleoperação com color+profundidade de uma D435i (use o número de série detectado em serial_number_or_name):
lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_awesome_follower_arm \
--robot.cameras='{
d435i_color: {
type: realsense_d435i_color,
serial_number_or_name: "419522072950",
width: 640, height: 480, fps: 30,
color_mode: rgb, color_stream_format: rgb8,
rotation: 0, warmup_s: 1
},
d435i_depth: {
type: realsense_d435i_depth,
serial_number_or_name: "419522072950",
width: 640, height: 480, fps: 30,
max_depth_m: 2.0, depth_alpha: 0.2,
rotation: 0, warmup_s: 5
}
}' \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM1 \
--teleop.id=my_awesome_leader_arm \
--display_data=true
Notas de parâmetros: depth_alpha escala a imagem de profundidade (comece em 0.2); com 3+ câmeras de profundidade, reduza fps a 15; mantenha 640×480 por estabilidade.
A Orbbec Gemini 2 é uma RGB-D de alto desempenho com fluxos RGB e profundidade sincronizados; a Gemini 336 melhora a profundidade em áreas refletivas e escuras.
1. Mude para a ramificação de câmeras:
git checkout DepthCameraSupport
git pull origin DepthCameraSupport
2. Instale o extra Orbbec:
pip install -e ".[orbbec]"
3. Permissões e cache USBFS (os 16MB padrão são pouco para fluxos de profundidade):
chmod a+rw /dev/bus/usb/*/*
cat /sys/module/usbcore/parameters/usbfs_memory_mb # ver
sudo sh -c 'echo 128> /sys/module/usbcore/parameters/usbfs_memory_mb' # subir para 128MB
4. Detecte as câmeras e anote o número de série:
lerobot-find-cameras orbbec
5. Exemplo — teleoperação com uma Orbbec color+profundidade:
lerobot-teleoperate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_awesome_follower_arm \
--robot.cameras='{
orbbec_color: {
type: orbbec_color,
serial_number_or_name: "CP9JA530003A",
width: 640, height: 480, fps: 30,
color_mode: rgb, rotation: 0, warmup_s: 1
},
orbbec_depth: {
type: orbbec_depth,
serial_number_or_name: "CP9JA530003A",
width: 640, height: 400, fps: 30,
depth_alpha: 0.2, rotation: 0, warmup_s: 5
}
}' \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM1 \
--teleop.id=my_awesome_leader_arm \
--display_data=true
Uma Orbbec ocupa 3 números consecutivos de /dev/video. Ao misturá-la com câmeras USB comuns, ligue a Orbbec primeiro e as comuns por último para os índices delas ficarem no fim. Se os quadros expirarem (Timed out waiting for frame ...), reconecte a câmera.
12.5 Sincronização de imagem e ação
12.5 Sincronização de imagem e ação
Ao gravar, o LeRobot executa um ciclo por quadro:
- ler as duas câmeras (frontal, lateral);
- ler as juntas do líder → ações;
- ler as juntas do seguidor → estado;
- carimbar e anexar uma linha.
Como as câmeras entregam no próprio ritmo, o ciclo alinha por carimbo de tempo, não por desejo: cada linha do dataset emparelha as imagens mais frescas com os dados de juntas do mesmo instante. É por isso que o --dataset.fps não pode superar o que suas câmeras entregam com estabilidade (30 fps a 640×480 MJPG é a escolha segura).
12.6 Gravar seu primeiro dataset
12.6 Gravar seu primeiro dataset
Grave localmente a tarefa do Capítulo 11 (A para B):
lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_awesome_follower_arm \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30, fourcc: "MJPG"}, side: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30, fourcc: "MJPG"}}" \
--teleop.type=so101_leader \
--teleop.port=/dev/ttyACM1 \
--teleop.id=my_awesome_leader_arm \
--display_data=true \
--dataset.repo_id=seeedstudio123/test \
--dataset.num_episodes=5 \
--dataset.single_task="Grab the black cube" \
--dataset.push_to_hub=false \
--dataset.episode_time_s=30 \
--dataset.reset_time_s=30
O repo_id é personalizável; com push_to_hub=false o dataset fica em ~/.cache/huggingface/lerobot/seeedstudio123/test/.
Parâmetros de gravação
| Parâmetro | Descrição | Padrão |
|---|---|---|
--dataset.episode_time_s | Duração por episódio (segundos) | 60 |
--dataset.reset_time_s | Tempo de reset do ambiente após cada episódio (segundos) | 60 |
--dataset.num_episodes | Total de episódios a gravar | 50 |
Controles de teclado durante a gravação
| Tecla | Ação |
|---|---|
| → (seta à direita) | Encerrar cedo o episódio/reset atual; passar ao próximo |
| ← (seta à esquerda) | Cancelar o episódio atual; regravá-lo |
| ESC | Parar a sessão imediatamente, codificar vídeos e salvar o dataset |
Se os atalhos não responderem, confira se $DISPLAY está definido, ou rebaixe o pynput: pip install pynput==1.6.8
Durante a gravação verá linhas como:
INFO ol_robot.py:219 dt:33.34 (30.0hz) dtRlead: 5.06 (197.5hz) dtWfoll: 0.25 (3963.7hz) dtRfoll: 6.22 (160.7hz) dtRlaptop: 32.57 (30.7hz) dtRphone: 33.84 (29.5hz)
Um dt perto de 33 ms indica que o ciclo sustenta 30 Hz — sessão saudável.
Checkpoints e retomada
- Checkpoints são criados automaticamente durante a gravação.
- Para retomar após uma interrupção, rode o mesmo comando com
--resume=true. - Nesse caso
--dataset.num_episodesconta os episódios DESTA sessão, não o total — 15 gravados de 50 → ponha 35. - Para recomeçar do zero, apague o diretório do dataset.
Gravar no Hugging Face Hub (opcional)
Entre uma vez com um token de escrita das configurações da HF:
huggingface-cli login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
HF_USER=$(huggingface-cli whoami | head -n 1)
Depois use --dataset.repo_id=${HF_USER}/record-test e --dataset.push_to_hub=true para publicá-lo.
12.7 Dicas para coletar bons dados
12.7 Dicas para coletar bons dados
- Tarefa: pegar objetos nos pontos marcados e colocá-los em B (a cruz do Capítulo 11).
- Escala: 50 episódios ou mais (10 por ponto).
- Consistência:
- Câmeras fixas;
- Comportamento de agarre idêntico;
- Objeto visível nas duas vistas.
- Progressão: domine o agarre confiável antes de variar (novas posições, técnicas, câmeras). Evite subir a complexidade de supetão.
- Ambiente: posições, ângulos e luz estáveis; sem fundos instáveis ou gente atravessando — grandes diferenças entre coleta e implantação derrubam as políticas.
💡 Regra de bolso: você deveria conseguir fazer a tarefa olhando só as imagens das câmeras. Se você não consegue, o modelo também não.
Defina --dataset.num_episodes alto o bastante antes de começar e não pare no meio sem necessidade — as estatísticas (média e variância) são calculadas ao final e o treinamento precisa delas.