LeKiwi 제어 코드 커스터마이징과 텔레오퍼레이션

텔레오퍼레이션에서 LeRobot 예제 스크립트로 텔레오퍼레이션을 해 보았습니다. 동작은 했지만 IP나 id를 바꾸려면 매번 코드 파일을 열어야 했고, 이후 데이터 기록과 추론에서도 같은 불편이 반복됩니다. 이 페이지에서는 왜 예제 코드가 그렇게 되어 있는지 살펴보고, 설정을 명령줄 인자로 넘길 수 있게 정리한 커스텀 스크립트로 갈아탑니다. 라즈베리파이 쪽 호스트도 한 줄로 띄울 수 있게 스크립트로 묶습니다. 이 페이지 이후의 모든 실습은 이 두 가지 위에서 진행됩니다.

학습 목표

  • LeRobot 기본 도구가 LeKiwi 텔레오퍼레이션에 바로 쓰이지 않는 이유를 설명할 수 있다.
  • 호스트 실행 스크립트의 각 부분이 무엇을 해결하는지 이해하고 실행할 수 있다.
  • 커스텀 클라이언트 스크립트로 인자만 바꿔 텔레오퍼레이션을 수행할 수 있다.

1. 예제 코드의 한계

1.1. 설정이 코드 안에 있다

텔레오퍼레이션에서 쓴 examples/lekiwi/teleoperate.py 의 앞부분입니다.

FPS = 30

def main():
    # Create the robot and teleoperator configurations
    robot_config = LeKiwiClientConfig(remote_ip="192.168.0.207", id="lekiwi07")
    teleop_arm_config = SO100LeaderConfig(port="/dev/ttyACM0", id="leader")
    keyboard_config = KeyboardTeleopConfig(id="my_laptop_keyboard")

    # Initialize the robot and teleoperator
    robot = LeKiwiClient(robot_config)
    leader_arm = SO100Leader(teleop_arm_config)
    keyboard = KeyboardTeleop(keyboard_config)

IP, 로봇 id, 리더 암 포트가 모두 코드 안에 하드코딩 되어 있습니다. 예제로서는 읽기 쉽지만, 로봇이 바뀌거나 네트워크 대역이 바뀔 때마다 파일을 열어 고쳐야 합니다. 여러 대의 로봇을 번갈아 쓰거나 다른 장소로 옮기면 실수하기 쉽습니다.

1.2. 기본 명령줄 도구는 LeKiwi를 지원하지 않는다

LeRobot에는 lerobot-teleoperate 라는 명령줄 도구가 있어 SO-ARM 같은 로봇은 인자만으로 텔레오퍼레이션이 됩니다. 그런데 이 도구는 조종 장치를 하나만 받습니다. LeKiwi는 팔은 리더 암으로, 바퀴는 키보드로 조종하므로 조종 장치가 두 개 필요하고, 그래서 기본 도구를 쓸 수 없습니다. 예제 스크립트가 따로 있는 이유가 이것입니다.

1.3. 커스텀 저장소

두 문제를 함께 해결한 스크립트 모음을 저장소에서 받습니다(PC에서 실행). 예제와 같은 구조를 유지하면서 설정을 전부 인자로 뺐고, 리더 암과 키보드를 함께 다룹니다. 스크립트는 저장소의 feature/lerobot 브랜치에 있으므로 -b feature/lerobot 을 붙여 받습니다.

Ubuntu
mkdir -p ~/workspace
cd ~/workspace
git clone -b feature/lerobot https://github.com/roboseasy/lekiwi.git
cd lekiwi
Windows
cd %USERPROFILE%
mkdir workspace
cd workspace
git clone -b feature/lerobot https://github.com/roboseasy/lekiwi.git
cd lekiwi
스크립트하는 일사용하는 페이지
lekiwi-teleoperate.py기록 없이 조작만이 페이지
lekiwi-record.py조작하면서 데이터셋 기록데이터 기록
lekiwi-replay.py기록한 에피소드를 로봇에서 재생데이터 재생
lekiwi-rollout.py학습된 정책으로 자율 실행. --duration_s 로 시간 제한 (실험·디버깅용)ACT 추론
lekiwi-inference.py학습된 정책으로 자율 실행. ESC로 멈출 때까지 계속 (시연용)ACT 추론, SmolVLA 추론
lekiwi-evaluate.py정책 성공률 측정ACT 추론

모든 스크립트는 --robot.*(로봇 접속), --display_data 같은 인자 이름을 공유하고, 리더 암을 쓰는 lekiwi-teleoperate.py·lekiwi-record.py 는 --teleop.* 도 공유합니다. 이 페이지에서 익힌 인자를 이후 단계에서 그대로 씁니다.


2. [Pi] 호스트 실행 스크립트

2.1. 왜 스크립트로 묶는가

카메라 포트 고정에서 완성한 호스트 명령은 카메라 설정 때문에 일곱 줄이 넘습니다. 라즈베리파이에 접속할 때마다 이것을 입력하는 것은 번거롭고 오타가 나기 쉽습니다. 게다가 호스트는 모터에 기록된 캘리브레이션 값이 파일과 다르면 시작할 때 “기존 캘리브레이션을 쓸까요?”라고 묻는데, 나중에 자동 실행으로 돌릴 때 이 질문에 답할 사람이 없으면 오류로 죽습니다. 이런 것들을 셸 스크립트 하나에 담습니다.

2.2. 스크립트 작성

라즈베리파이에 SSH로 접속해 홈 폴더에 만듭니다.

킷에 이미 들어 있을 수 있습니다.

ls ~/start_lekiwi_host.sh 로 먼저 확인합니다. 파일이 있으면 cp ~/start_lekiwi_host.sh ~/start_lekiwi_host.sh.bak 으로 백업한 뒤, 아래 내용과 비교해 ROBOT_ID 기본값, --robot.port, 카메라 경로를 고칩니다. 없으면 아래처럼 새로 만듭니다.

nano ~/start_lekiwi_host.sh
#!/usr/bin/env bash
# LeKiwi 호스트 실행 스크립트
# 사용법: ./start_lekiwi_host.sh [robot_id] [run_time_sec]
set -e

VENV_DIR="$HOME/lerobot060_venv"
ROBOT_ID="${1:-lekiwi07}"
RUN_TIME_SEC="${2:-14400}"  # 기본 4시간

if [ ! -x "$VENV_DIR/bin/python" ]; then
        echo "[lekiwi] error: venv not found at $VENV_DIR"
        exit 1
fi

# 카메라는 MJPG 강제 — 기본값(YUYV)은 두 카메라 동시 스트림이
# Pi USB 대역폭을 초과해 wrist 카메라가 타임아웃 난다 (실측)
# (아래 주석 줄은 손목 카메라를 세로로 달았을 때의 예시)
# CAMERAS_CONFIG='{front: {type: opencv, index_or_path: /dev/cam_front, fps: 30, width: 640, height: 480, rotation: 0, fourcc: MJPG}, wrist: {type: opencv, index_or_path: /dev/cam_wrist, fps: 30, width: 480, height: 640, rotation: -90, fourcc: MJPG}}'
CAMERAS_CONFIG='{front: {type: opencv, index_or_path: /dev/cam_front, fps: 30, width: 640, height: 480, rotation: 0, fourcc: MJPG}, wrist: {type: opencv, index_or_path: /dev/cam_wrist, fps: 30, width: 640, height: 480, rotation: 0, fourcc: MJPG}}'

echo "[lekiwi] starting host: id=$ROBOT_ID run_time=${RUN_TIME_SEC}s"
echo "[lekiwi] laptop should connect to this Pi's IP (ZMQ 5555/5556)"
# 시작 시 "기존 캘리브레이션 사용?" 프롬프트에 자동 ENTER —
# nohup/systemd처럼 stdin이 없는 환경에서 EOFError로 죽는 것 방지.
# 재캘리브레이션은 lerobot-calibrate로 별도 수행.
exec "$VENV_DIR/bin/python" -m lerobot.robots.lekiwi.lekiwi_host \
        --robot.id="$ROBOT_ID" \
        --robot.port=/dev/lekiwi \
        --robot.cameras="$CAMERAS_CONFIG" \
        --host.connection_time_s="$RUN_TIME_SEC" <<< ''

각 부분이 해결하는 문제는 다음과 같습니다.

부분역할
VENV_DIRPi LeRobot 설치에서 만든 가상환경의 Python을 직접 지정. 가상환경을 활성화하지 않은 셸에서도 동작합니다
ROBOT_ID="${1:-lekiwi07}"첫 번째 인자를 로봇 id로 쓰고, 없으면 lekiwi07. 다른 로봇이면 이 기본값을 바꿔 둡니다
RUN_TIME_SEC두 번째 인자를 실행 시간으로 쓰고, 없으면 4시간
--robot.port=/dev/lekiwiUSB 포트 고정에서 만든 모터 보드 고정 포트. 포트를 고정하지 않았다면 /dev/ttyACM0 으로 바꿉니다
CAMERAS_CONFIG카메라 포트 고정에서 고정한 /dev/cam_front, /dev/cam_wrist 별칭과 MJPG 설정. 주석 처리된 줄은 손목 카메라를 세로로 달았을 때의 예시입니다
<<< ''빈 줄을 표준 입력으로 넣어 캘리브레이션 질문에 ENTER를 자동으로 답합니다
exec셸을 Python 프로세스로 교체해, Ctrl + C 가 호스트에 바로 전달되게 합니다

카메라 해상도 규칙

width 와 height 는 회전을 적용한 뒤의 크기입니다. 640×480 웹캠을 rotation: 0 으로 쓰면 640, 480, 세로로 달아 rotation: -90 을 쓰면 480, 640 이어야 합니다. 이 조합이 어긋나면 카메라 열기에 실패합니다.

저장한 뒤 실행 권한을 줍니다.

chmod +x ~/start_lekiwi_host.sh

2.3. 실행

./start_lekiwi_host.sh lekiwi07

첫 번째 인자가 로봇 id입니다. 생략하면 스크립트의 기본값 lekiwi07 이 쓰이므로, 다른 로봇에서는 그 로봇의 id를 넘기거나 기본값을 바꿉니다. 캘리브레이션에서 쓴 id와 같아야 합니다.

예시 화면은 호스트를 직접 실행했을 때의 화면(id lekiwi01)입니다. 스크립트로 실행하면 맨 앞에 [lekiwi] starting host: id=lekiwi07 run_time=14400s 같은 안내 줄이 먼저 찍힙니다.

텔레오퍼레이션과 같은 대기 화면이 나오면 정상입니다. 이 창은 그대로 두고 PC로 넘어갑니다. 이후 데이터 기록, 데이터 재생, ACT 추론, SmolVLA 추론에서 “호스트 실행”은 모두 이 한 줄을 뜻합니다.


3. [PC] 커스텀 클라이언트로 텔레오퍼레이션

3.1. 명령

PC의 conda 환경을 활성화하고 저장소 폴더로 이동한 뒤 실행합니다. 텔레오퍼레이션에서 코드에 적었던 값들이 그대로 인자가 되었습니다.

Ubuntu
conda activate lerobot060
cd ~/workspace/lekiwi
Windows
conda activate lerobot060
cd %USERPROFILE%\workspace\lekiwi
Ubuntu
python lekiwi-teleoperate.py \
    --robot.remote_ip=192.168.0.207 \
    --robot.id=lekiwi07 \
    --teleop.port=/dev/ttyACM0 \
    --teleop.id=leader \
    --fps=30 \
    --display_data=true
Windows
python lekiwi-teleoperate.py ^
    --robot.remote_ip=192.168.0.207 ^
    --robot.id=lekiwi07 ^
    --teleop.port=COM22 ^
    --teleop.id=leader ^
    --fps=30 ^
    --display_data=true
인자의미
--robot.remote_ip고정 IP · 무선 연결에서 만든 LeKiwi의 고정 IP
--robot.id호스트와 같은 로봇 id
--teleop.port리더 암이 꽂힌 PC의 USB 포트
--teleop.id캘리브레이션에서 리더 암을 캘리브레이션한 id
--fps제어 주기. 기본 30
--display_dataRerun 시각화 창 표시
--teleop_time_s지정한 초 뒤 자동 종료. 생략하면 ESC를 누를 때까지

인자를 생략하면 스크립트 안의 기본값이 쓰이는데, 이 기본값은 실습 환경과 다를 수 있으니 처음에는 위처럼 전부 명시하는 것을 권합니다.

3.2. 조작키

리더 암은 그대로 따라 움직이고, 바퀴는 키보드로 조종합니다.

키동작
W / S전진 / 후진
A / D왼쪽 / 오른쪽 이동
Q / E제자리 왼쪽 / 오른쪽 회전
R / F속도 단계 올리기 / 내리기
SPACE바퀴 정지
ESC종료

텔레오퍼레이션 예제에서는 회전이 Z / X 였는데 커스텀 스크립트는 손이 닿기 쉬운 Q / E 로 옮겼습니다. 속도 키는 누르고 있으면 순식간에 최대나 최소로 튀기 때문에 0.4초에 한 단계씩만 바뀌도록 되어 있습니다.

종료하면 바퀴를 멈추고, 팔은 현재 자세를 목표로 한 번 더 보내 튀지 않게 한 뒤 연결을 끊습니다. Ctrl + C 도 같은 순서로 정리됩니다.

3.3. 키보드 입력 방식

텔레오퍼레이션에서 Wayland를 X11로 바꾼 이유는 예제가 쓰는 전역 키 감지 방식이 Wayland에서 막히기 때문이었습니다. 커스텀 스크립트는 --base_control 인자로 입력 방식을 고르며, 기본값 auto 가 환경을 보고 자동으로 정합니다.

값방식조건
pynput전역 키 감지. 누르고 있는 동안 이동X11에서만 동작
terminal이 터미널 창의 입력을 읽음Wayland에서도 동작. 터미널 창에 포커스가 있어야 함
none바퀴 조종 끄고 팔만팔만 확인할 때

즉 텔레오퍼레이션의 X11 전환은 이 스크립트에서는 필수가 아닙니다. Wayland 환경이면 자동으로 터미널 입력 방식이 되고, 시작 로그에 어느 방식이 선택됐는지 출력됩니다. 터미널 방식일 때는 Rerun 창이 아니라 명령을 실행한 터미널 창을 클릭한 상태에서 키를 눌러야 합니다.

3.4. 화면 확인

텔레오퍼레이션과 같은 화면입니다. 정면과 손목 카메라, 관측과 행동 그래프가 보이고 리더 암을 움직이면 팔이 따라옵니다. 시작할 때 실제로 들어온 카메라 이름과 해상도가 로그에 한 번 찍히는데, 카메라 포트 고정에서 정한 front, wrist 와 480×640×3(높이×너비×채널) 형태인지 확인해 두면 데이터 기록에서 기록할 데이터의 구조를 미리 아는 셈입니다.


4. 문제 해결

증상확인할 것
venv not found 로 호스트가 멈춤Pi LeRobot 설치에서 만든 가상환경 경로가 ~/lerobot060_venv 인지 확인
호스트가 카메라를 못 엶카메라 포트 고정 별칭 ls -l /dev/cam_* 확인. 해상도와 회전 조합 확인
호스트 시작 직후 EOFError스크립트 마지막의 <<< '' 누락
클라이언트 연결 timeout호스트가 떠 있는지, --robot.remote_ip 가 맞는지, PC와 Pi가 같은 와이파이인지
바퀴가 키에 반응하지 않음시작 로그의 입력 방식 확인. 터미널 방식이면 터미널 창을 클릭. --base_control=terminal 로 강제 가능
팔이 안 따라옴--teleop.port 확인, 리더 암 전원, --teleop.id 가 캘리브레이션에서 쓴 리더 암 id(leader)와 같은지 확인
No module named 'zmq'LeRobot 설치 (Ubuntu) / LeRobot 설치 (Windows)에서 lekiwi extra 설치 여부

5. 정리

다음 단계 데이터 기록에서는 같은 인자에 데이터셋 이름만 더해 실제 학습 데이터를 기록합니다.