# CORE-01 실습 코드 · 로봇2 키보드 주행·실험 키·안내 미션

**『피지컬 AI로 배우는 RoboCo 로봇 구조와 Python 제어』 독자 제공 코드**

이 폴더에는 책의 실습 로봇 **로봇2(Book-CORE-01-SCRIPT)**를 Python 스크립트로 조종하는 코드가 들어 있습니다. 키보드로 로봇을 달리게 하고, 바퀴 방향을 바꾸고, 화면을 펼쳐 안내 문구를 보여 줄 수 있습니다. 책의 PART 4에서는 숫자 키로 바퀴 모터 설정 값을 바꿔 가며 움직임을 비교합니다.

| 순서 | 할 일 | 이 문서의 위치 |
| --- | --- | --- |
| 1 | 폴더를 RoboCo Scripts 폴더에 넣기 | 3. 설치 |
| 2 | 로봇2의 Microcontroller에 시작 파일 연결하기 | 4. 연결과 실행 |
| 3 | 키보드로 조종하면서 로봇 화면과 콘솔 읽기 | 5~7 |
| 4 | (PART 4) 같은 시작 파일에서 숫자 키로 값 바꿔 비교하기 | 9. 실험 키 |
| 5 | (PART 5) 같은 시작 파일에서 5번 키로 안내 미션 실행하기 | 10. 안내 미션 |

---

## 1. 이 폴더로 할 수 있는 것

- W·S·A·D·Z·C·Q·E 키로 전진, 후진, 방향 전환, 제자리 회전, 대각선 이동을 합니다.
- 1번 키로 로봇 화면을 펼치고 접습니다. 멈춘 상태에서 펼치면 안내 문구가 나타납니다.
- 로봇 화면(Text Screen)으로 지금 상태를, 콘솔 LOG로 지나간 사건을 확인합니다.
- 시작 파일 맨 위의 값을 바꾸어 속도, 조향 순서, 표시 방식을 비교합니다.
- 0번 키로 언제든 처음 상태로 되돌립니다(전체 RESET). 헷갈리면 0을 누릅니다.
- 책의 PART 4에서는 같은 시작 파일에서 숫자 키 2·3·4로, 코드를 열지 않고 바퀴 모터 설정 값을 바꿔 비교합니다.
- 책의 PART 5에서는 로봇이 정해진 경로를 스스로 달려 안내하고 돌아오는 안내 미션을 실행합니다.

## 2. 준비물

| 항목 | 내용 |
| --- | --- |
| 게임 | Steam용 RoboCo |
| 로봇 파일 | Book-CORE-01-SCRIPT (책과 함께 제공하는 로봇2) |
| 이 폴더 | Book_C01_Robot |

로봇1(Book-CORE-01-CONTROLS)은 스크립트 없이 Controls Mapping으로 조종하는 로봇이므로 이 코드를 쓰지 않습니다.

## 3. 설치 — 폴더를 놓는 위치

`Book_C01_Robot` 폴더를 RoboCo의 **Scripts 폴더 바로 아래**에 넣습니다.

```text
문서(Documents)
└─ My Games
   └─ RoboCo
      └─ [Steam 사용자 이름]
         └─ Scripts
            └─ Book_C01_Robot          ← 이 폴더
               ├─ README.md
               ├─ __init__.py
               ├─ c01_start.py
               └─ C01_Lib
                  ├─ __init__.py
                  └─ c01lib_*.py (11개)
```

RoboCo 화면의 Microcontroller 설정에서 **OPEN SCRIPT FOLDER**를 누르면 Scripts 폴더가 바로 열립니다.

설치할 때 지켜야 할 규칙은 세 가지입니다.

1. **Scripts 바로 아래에 둡니다.** RoboCo는 Scripts 폴더를 기준으로 파일을 찾습니다. `Scripts\다른폴더\Book_C01_Robot`처럼 한 단계 더 들어가면 스크립트가 라이브러리를 찾지 못합니다.
2. **폴더와 파일 이름을 바꾸지 않습니다.** 시작 파일은 `Book_C01_Robot`, `C01_Lib`라는 이름으로 라이브러리를 불러옵니다.
3. **새 버전을 받으면 기존 폴더를 지운 뒤 넣습니다.** 덮어쓰면 이전 버전에만 있던 파일이 남아 스크립트 목록이 헷갈릴 수 있습니다.

## 4. 연결과 실행

1. RoboCo에서 로봇 파일 **Book-CORE-01-SCRIPT**를 불러옵니다.
2. 로봇의 **Microcontroller** 설정을 열고 **LOAD**를 누릅니다.
3. `Book_C01_Robot` 폴더의 **c01_start.py**를 선택합니다.
4. 실행하면 콘솔에 다음 줄들이 나타납니다. `START` 줄이 보이면 준비가 끝난 것입니다. 로봇 파일의 모터 설정이 시작 파일의 값과 다르면 `Motor (script)` 줄이 하나 더 나타나 스크립트가 적용한 값을 보여 줍니다.

```text
[   0.00s] START   Ready - wheels 0°, screen folded, motors stopped
[   0.00s] KEYS    W forward · S back · A turn left · D turn right · Z rotate left · C rotate right · Q diag left · E diag right · 1 screen (fold = reset) · 0 reset
[   0.00s] KEYS    Experiment (PART 4): 2 next value · 3 level down · 4 level up | Mission (PART 5): 5 start
[   0.00s] SETUP   Steering wait 0.50s · stagger 0.30s · return 0.60s
[   0.00s] SETUP   Motor (robot file) RPM 120 rpm / ACCEL 0.50 s / BRAKE F 50.0 / BRAKE T 0.20 s
[   0.00s] SETUP   Motor (fixed)      TORQUE 6290 - not changed by the script
```

로봇에 연결하는 파일은 **시작 파일 `c01_start.py` 하나**이며, 책의 PART 1부터 PART 5까지 이 파일만 씁니다. PART 4의 실험은 숫자 키 2·3·4로, PART 5의 안내 미션은 5번 키로 같은 파일에서 합니다. `C01_Lib` 폴더의 파일은 이 스크립트가 불러다 쓰는 라이브러리이므로 로봇에 직접 연결하지 않습니다.

## 5. 키 조작

| 키 | 동작 | 바퀴 방향 |
| --- | --- | --- |
| W | 전진 | 네 바퀴 0° |
| S | 후진 | 네 바퀴 0° |
| A | 왼쪽 방향 전환 | 네 바퀴 0°, 좌우 바퀴를 반대로 회전 |
| D | 오른쪽 방향 전환 | 네 바퀴 0°, 좌우 바퀴를 반대로 회전 |
| Z | 제자리 좌회전 | 바퀴별 45° 조향 후 회전 |
| C | 제자리 우회전 | 바퀴별 45° 조향 후 회전 |
| Q | 왼쪽 대각선 전진 | 네 바퀴를 10시 방향으로 정렬 |
| E | 오른쪽 대각선 전진 | 네 바퀴를 2시 방향으로 정렬 |
| 1 | 화면 펼치기 / 접기. **접으면 0과 같은 전체 RESET** (9장) | — |
| 0 | **전체 RESET** (Reset All). 헷갈리면 0 | 바퀴 정지, 0°로 복귀 |
| 2 · 3 · 4 | PART 4 실험 키 (Next Parameter · Level Down · Level Up). 화면이 접혀 있으면 펼침 (9장) | — |
| 5 | 안내 미션 시작 (Start Mission) (10장) | 경로를 스스로 주행 |

여덟 주행 키는 모두 같은 Target RPM(`TARGET_RPM`)으로 바퀴를 돌립니다. 키마다 다른 것은 속도가 아니라 바퀴 방향(조향 각도)과 바퀴마다 도는 방향(앞·뒤)입니다.

키 배정은 시작 파일의 **2. KEYS** 구역(55번째 줄)에 표로 적혀 있습니다. 키를 바꾸면 로봇 파일의 Controls Mapping에도 같은 키를 넣어야 합니다.

바퀴 방향을 바꿔야 하는 키(Z·C·Q·E, 그리고 이 키를 쓴 뒤의 W·S·A·D)는 다음 순서로 움직입니다.

```text
키를 누를 때 : 앞바퀴 조향 → (0.3초) → 뒷바퀴 조향 → (0.5초) → 바퀴 구동
키를 뗄 때   : 바퀴 구동 정지 + 네 바퀴를 0.6초 동안 천천히 0°로 복귀
```

조향하는 동안 잠깐 멈춰 있는 것은 고장이 아닙니다. 바퀴가 방향을 바꾸는 동안 차체가 틀어지지 않도록 기다리는 시간입니다.

## 6. 로봇 화면(Text Screen) 읽기

로봇 화면은 **지금 이 순간의 상태**를 4줄로 보여 줍니다.

```text
FORWARD      ← 지금 동작
KEY W        ← 스크립트가 읽은 키
RPM 120      ← 바퀴 모터의 Target RPM
READY        ← 조향 상태
```

| 줄 | 표시 | 뜻 |
| --- | --- | --- |
| 1 | STOP · FORWARD · BACKWARD · TURN L · TURN R · ROTATE L · ROTATE R · DIAG L · DIAG R | 지금 동작 |
| 2 | KEY W … KEY E, KEY - | 스크립트가 읽은 키(-는 누른 키 없음) |
| 3 | RPM 0 ~ 1000 | 바퀴 모터에 설정된 Target RPM. 실제로 잰 회전수가 아니라 목표값이며, 조향 중·정지 중에는 0 |
| 4 | READY / STEERING | 조향이 끝났는지(READY), 바퀴 방향을 바꾸는 중인지(STEERING) |

화면에는 상태 정보 대신 다음 네 가지가 나타날 때가 있습니다.

- **안내 문구**: 멈춘 상태에서 1번 키로 화면을 펼치면 `GUIDE_MESSAGE`에 넣은 글(기본 `WELCOME`)이 나타납니다. 주행 키를 누르면 상태 정보로 바뀌고, 화면을 다시 펼칠 때까지 상태 정보를 유지합니다. 주행 중에 펼치면 조종에 필요한 상태 정보를 그대로 보여 줍니다.
- **RESET / KEY 0 / RPM 0 / READY**: 0번 키로 전체 RESET한 직후입니다. 주행 키를 누르면 상태 정보로 돌아갑니다.
- **실험 화면** (예: `RPM / LV 4/10 / 120 rpm / READY`): PART 4 실험 모드에서 멈춰 있을 때입니다. 화면이 접혀 있어도 2·3·4를 누르면 저절로 펼쳐집니다(9장).
- **미션 화면** (예: `MOVE / STEP 2/3 / ROTATE R / READY`): 안내 미션 중에 지금 상태와 단계를 보여 줄 때입니다(10장).
- **ERROR / STOPPED / SEE LOG**: 스크립트 실행 중 오류가 나서 멈춘 상태입니다. 바퀴 모터는 이미 멈춰 있습니다. 콘솔의 `ERROR` 줄을 확인합니다.

## 7. 콘솔 LOG 읽기

콘솔은 **언제 무슨 일이 일어났는지**를 한 줄에 하나씩 남깁니다. 지금 상태는 로봇 화면이 보여 주므로, 콘솔에는 상태가 바뀐 사건만 기록합니다. LOG는 로봇 화면·키 이름·RoboCo API와 같은 언어가 되도록 **영어**로 씁니다.

```text
[  20.00s] STEER   Pivot posture (front → rear stagger)
 ────┬────  ──┬──   ───────────────┬───────────────
 시작 후     분류                 내용
 경과 시간
```

| 분류 | 뜻 | 기록하는 사건 |
| --- | --- | --- |
| `START` | 시작 | 스크립트 준비 완료 |
| `KEYS` | 키 안내 | 쓸 수 있는 키 |
| `SETUP` | 설정 | 이번 실행의 조향 시간, 모터 설정 값(로봇 파일 값, 스크립트가 적용한 값, 고정값) |
| `STEER` | 조향 | 바퀴 자세가 바뀔 때 (기본 주행, 제자리 회전, 대각선) |
| `SCREEN` | 화면 | 1번 키로 화면을 펼치거나 접을 때 |
| `DRIVE` | 주행 | 주행 키를 뗄 때 한 줄 요약 (`LOG_LEVEL` 2일 때만) |
| `EXP` | 실험 | 실험 모드, 값 변경, RESET, 받지 않은 키 |
| `STATE` | 미션 상태 | 안내 미션의 상태 변화와 경로 단계 (WAIT → MOVE 등) |
| `END` | 종료 | 스크립트 종료 |
| `ERROR` | 오류 | 실행 중 오류 |
| `NOTE` | 알림 | LOG 줄 수 제한, 안내 문구 길이 초과, 범위를 벗어난 모터 설정 값, 쓰지 않는 값 이름 |

자주 보는 낱말: `step` 경로 단계, `route` 경로, `mission complete` 미션 완료, `forward` 전진, `back`·`backward` 후진, `turn` 방향 전환, `rotate` 제자리 회전, `diagonal` 대각선, `posture` 바퀴 자세, `stagger` 시간차, `return` 0° 복귀, `base` 기준값, `level (LV)` 단계, `held` 키를 누른 시간, `rpm` Target RPM, `fixed` 고정값, `ignored` 받지 않음.

기록할 양은 `LOG_LEVEL`로 정합니다.

| LOG_LEVEL | 기록 내용 | 쓰는 때 |
| --- | --- | --- |
| 0 | START · KEYS · SETUP · END · ERROR · RESET | 콘솔을 거의 보지 않을 때 |
| 1 (기본) | 0 + 바퀴 자세 변경, 화면 전환, 실험 값 변경, 미션 상태 | 평소 실습 |
| 2 | 1 + 주행 동작 한 줄씩 (`W forward \| held 0.32s \| rpm 120`) | 값을 바꾸어 비교하는 실험 |

`SETUP` 줄에는 이번 실행에 쓴 조향 시간과 모터 설정 값이 남습니다. 값을 바꾸어 실험할 때 이 줄을 함께 기록해 두면 어느 결과가 어떤 설정에서 나왔는지 헷갈리지 않습니다.

## 8. 먼저 바꿔 볼 값

시작 파일 `c01_start.py`에는 바꿔 볼 값과 키 배정만 모여 있습니다. 로봇을 움직이는 코드는 `C01_Lib` 폴더의 라이브러리에 있습니다(11장). 맨 위의 "준비 코드"는 라이브러리를 올바르게 불러오기 위한 코드이므로 바꾸지 않습니다.

| 구역 | 줄 | 들어 있는 것 | 쓰는 곳 |
| --- | --- | --- | --- |
| 1. CONTROL PARAMETERS | 32 | 바퀴 모터 설정, 조향 시간, 안내 문구, 콘솔 기록 양 | 모든 PART |
| 2. KEYS | 55 | 키 배정 (주행 키, 명령 키) | 모든 PART |
| 3. EXPERIMENT STEPS | 79 | 실험 단계표 (값마다 LV 1~10의 값) | PART 4 (9장) |
| 4. MISSION | 94 | 안내 미션의 경로와 시간 | PART 5 (10장) |
| 5. RUN | 109 | 한 번 준비하고, 멈출 때까지 반복하는 세 줄 | 모든 PART |

**1. CONTROL PARAMETERS**의 값은 다음과 같습니다.

| 값 | 기본값 | 뜻 | 바꿀 때 |
| --- | --- | --- | --- |
| `TARGET_RPM` | 120 | 바퀴 목표 회전수 (Target RPM, 0~1000). 여덟 주행 키의 바퀴 속도 | 너무 빠르면 낮춤. PART 4 실험의 기준값 |
| `ACCELERATION_TIME` | 0.5 | 출발할 때 Target RPM까지 걸리는 시간(초, 0~10) | 출발할 때 차체가 들썩이면 늘림. PART 4 실험의 기준값 |
| `MAX_BRAKE_FORCE` | 50 | 멈출 때의 제동력 (Max Brake Force, 0~1570) | PART 4 실험의 기준값 |
| `BRAKING_TIME` | 0.2 | 멈출 때 제동이 걸리는 시간(초, 0~10) | PART 4 실험의 기준값 |
| `STEERING_SETTLE_SEC` | 0.5 | 조향 후 바퀴 구동까지 기다리는 시간(초) | 0이면 조향과 구동이 동시에 시작 |
| `STEERING_STAGGER_SEC` | 0.3 | 앞바퀴 조향 뒤 뒷바퀴 조향까지의 시간(초) | 0이면 네 바퀴 동시 조향 |
| `RELEASE_RETURN_SEC` | 0.6 | 키를 뗄 때 바퀴를 0°로 되돌리는 시간(초) | 복귀할 때 차체가 들썩이면 늘림 |
| `GUIDE_MESSAGE` | "WELCOME" | 안내 문구 (한 줄 9자·4줄 이내, 줄바꿈은 `\n`) | 예: "EXIT\nTHIS WAY" |
| `LOG_LEVEL` | 1 | 콘솔 기록 양 (0·1·2) | 7장 표 참고 |

**바퀴 모터 설정 네 값은 RoboCo 설정 창(DC Motor)과 이름·범위가 같습니다.** 스크립트를 시작하면 네 바퀴에 적용되고, 범위를 벗어난 값은 범위 끝 값으로 맞춘 뒤 콘솔에 `NOTE` 줄을 남깁니다(예: `TARGET_RPM = 1200` → 1000). 설정 창의 나머지 두 항목은 스크립트에서 바꾸지 않습니다. **Max Torque**(6290)는 고정값이고, **Start On**(ON)은 Python API에 해당 속성이 없습니다. Max Torque 값은 시작할 때 콘솔의 `SETUP  Motor (fixed)` 줄에 남습니다.

**더 바꿀 수 있는 값.** 아래 값은 자주 바꾸지 않아 시작 파일에 적어 두지 않았습니다. 바꾸려면 1번 구역에 같은 이름으로 한 줄을 적습니다. 예를 들어 `LOG_MAX_LINES = 500`처럼 적으면 됩니다.

| 값 | 기본값 | 뜻 |
| --- | --- | --- |
| `STEERING_STAGGER_ORDER` | "front_rear" | 시간차 조향 순서. "diagonal"은 대각선 바퀴끼리 먼저 |
| `STAGGER_ON_RELEASE` | False | 키를 뗄 때도 시간차 조향을 할지 (`RELEASE_RETURN_SEC`가 0일 때만 씀) |
| `LOOP_DELAY_SEC` | 0.02 | 키 입력을 확인하는 주기(초). 보통 바꾸지 않음 |
| `LOG_MAX_LINES` | 200 | 콘솔 기록 최대 줄 수 |
| `TEXT_SCREEN_FONT_SIZE` | None | 로봇 화면 글자 크기. 글자가 잘리면 숫자로 줄임 (예: 40) |

값 이름을 잘못 적으면(예: `LOG_LEVL`) 그 값은 쓰이지 않고, 시작할 때 콘솔에 `NOTE  Unknown setting 'LOG_LEVL'` 줄이 남습니다.

값을 바꿀 때는 다음 순서를 지킵니다.

1. **한 번에 한 값만** 바꿉니다. 여러 값을 함께 바꾸면 무엇 때문에 결과가 달라졌는지 알 수 없습니다.
2. 바꾸기 전 값을 적어 둡니다. 위 표의 기본값이 제공된 원래 값입니다.
3. 같은 출발 위치에서 다시 실행해 결과를 비교합니다.

## 9. 실험 키로 값 바꿔 보기 (PART 4)

책의 PART 4 실험은 **같은 시작 파일**(`c01_start.py`)에서 숫자 키로 합니다. 코드를 열지 않고 키만으로 바퀴 모터 설정 값을 바꿔 움직임을 비교할 수 있습니다.

| 키 | 이름 | 하는 일 | 받는 때 |
| --- | --- | --- | --- |
| 2 | Next Parameter | 처음 누르면 **실험 모드 시작**(RPM). 그 뒤로는 다음 값 고르기 (RPM → ACCEL → BRAKE F → BRAKE T → 다시 RPM). 3·4로 바꾼 값은 그대로 유지 | 멈춘 상태 |
| 3 / 4 | Level Down / Level Up | 고른 값을 한 단계 낮추기 / 높이기. 누르는 순간 로봇에 적용 | 실험 모드 + 멈춘 상태 |
| 0 | Reset All | **전체 RESET.** 3·4로 바꾼 값을 모두 기준값으로, 바퀴 정지, 바퀴 방향 0°, 실험 모드 끝 | 언제든 |
| 1 (접을 때) | Toggle Screen | 화면을 접으면 **0과 같은 전체 RESET** | 미션 중이 아닐 때 |

- **실험 모드는 2번을 처음 누를 때 시작합니다.** 그 전까지는 화면과 LOG가 평소와 같고, 3·4를 눌러도 값이 바뀌지 않습니다(콘솔에 "먼저 2를 누르라"는 한 줄이 남음).
- **멈춘 상태**는 주행 키를 누르지 않았고 로봇 화면 넷째 줄이 `READY`인 때입니다. 달리는 중이거나 바퀴 방향을 바꾸는 중(`STEERING`)에 2·3·4를 누르면 값은 바뀌지 않습니다.
- **2·3·4를 누르면 화면이 펼쳐집니다.** 실험 결과는 로봇 화면에 나타나므로, 화면이 접혀 있으면 저절로 펼쳐 실험 화면을 보여 줍니다. 1번으로 펼쳐 안내 문구가 보이는 중에 2를 누르면 실험 화면으로 바뀝니다. 키를 받지 않을 때(달리는 중, 실험 모드 전의 3·4)는 화면도 움직이지 않습니다.
- **3·4로 바꾼 값은 2번으로 다른 값을 골라도 유지됩니다.** 예를 들어 RPM을 300으로 올린 뒤 2번으로 ACCEL을 고르면, RPM 300을 유지한 채 ACCEL을 바꿉니다. 여러 값을 함께 바꿔 둔 채 주행할 수 있으며, 콘솔의 `Next:` 줄 끝에 `RPM kept at 300 rpm`처럼 유지된 값이 남습니다.
- **바꾼 값을 기준값으로 되돌리려면 0을 누르거나 1번으로 화면을 접습니다.** 둘 다 같은 전체 RESET입니다. 그때 누르고 있던 주행 키는 한 번 뗐다가 다시 눌러야 움직입니다. 주행 중에 1번으로 화면을 접어도 바퀴가 멈춥니다.

실험 모드에서 멈춰 있을 때 로봇 화면은 실험 화면을 보여 줍니다. 주행 키를 누르면 6장의 상태 화면으로 바뀝니다.

```text
RPM          ← 고른 값 (2번 키로 바꿈)
LV 4/10      ← 지금 단계
120 rpm      ← 지금 값
READY        ← 조향 상태 (READY / STEERING)
```

실험할 수 있는 값은 네 가지이며, 2번 키를 누르면 이 순서대로 고릅니다. 값마다 10단계이고, **굵게 쓴 값이 기준값(8장의 CONTROL PARAMETERS 값)**입니다. LV 10은 RoboCo 설정 창의 최댓값이므로, 4번 키로 최댓값까지 올릴 수 있습니다.

| 순서 | 화면 이름 | 바꾸는 값 (기준값이 있는 곳) | LV 1 ~ LV 10 | 기준 단계 |
| --- | --- | --- | --- | --- |
| 1 | RPM | 바퀴 목표 회전수 (`TARGET_RPM`) | 30 · 60 · 90 · **120** · 200 · 300 · 400 · 600 · 800 · 1000 | LV 4 |
| 2 | ACCEL | 출발 가속 시간 (`ACCELERATION_TIME`, 초) | 0.1 · 0.2 · **0.5** · 1 · 2 · 3 · 4 · 6 · 8 · 10 | LV 3 |
| 3 | BRAKE F | 제동력 (`MAX_BRAKE_FORCE`) | 0 · 25 · **50** · 100 · 200 · 400 · 600 · 900 · 1200 · 1570 | LV 3 |
| 4 | BRAKE T | 제동 시간 (`BRAKING_TIME`, 초) | 0 · 0.1 · **0.2** · 0.5 · 1 · 2 · 4 · 6 · 8 · 10 | LV 3 |

단계표는 `c01_start.py`의 **3. EXPERIMENT STEPS 구역(79번째 줄)**에 값 그대로 적혀 있습니다. 단계를 바꾸려면 목록의 값을 작은 값부터 고쳐 적습니다.

- **기준값은 CONTROL PARAMETERS 한 곳에만 둡니다.** 단계표에 기준값과 같은 값이 없으면 가장 가까운 단계를 기준값으로 바꾸고 콘솔에 `NOTE` 줄을 남깁니다. 예를 들어 `TARGET_RPM`을 150으로 고치면 RPM의 LV 4가 120 대신 150이 됩니다.
- 설정 창의 범위를 넘는 단계는 범위 끝 값으로 맞추고 `NOTE` 줄을 남깁니다.
- 조향 시간도 실험하려면 단계표에 `("STAGGER", [...])`(`STEERING_STAGGER_SEC`)나 `("RETURN", [...])`(`RELEASE_RETURN_SEC`) 줄을 더합니다. 값은 초 단위입니다.

알아 둘 점이 두 가지 있습니다.

- **스크립트를 시작하면 CONTROL PARAMETERS의 모터 설정 값이 로봇에 적용됩니다.** RoboCo 설정 창의 값도 이 값으로 바뀝니다. 그래서 모든 실험은 같은 조건에서 시작합니다. 시작할 때 콘솔의 `SETUP  Motor (robot file)` 줄에는 스크립트가 바꾸기 전의 로봇 파일 값이 남습니다.
- **스크립트를 멈추면(오류로 멈출 때도) 값을 다시 기준값으로 되돌립니다.** 게임을 바로 끄는 등 스크립트가 마무리 코드를 실행하지 못하면 바뀐 값이 로봇에 남을 수 있습니다. 로봇이 평소와 다르게 움직이면 스크립트를 다시 켜고 0을 누르거나, 로봇 파일을 다시 불러옵니다.

값을 바꿀 때마다 콘솔에 `EXP` 줄이 남습니다. 이 줄을 그대로 실험 기록으로 쓸 수 있습니다.

```text
[   2.70s] EXP     Target RPM 120 rpm → 200 rpm (LV 4 → 5)
[   3.40s] EXP     Next: ACCEL (Acceleration Time) LV 3/10 0.50 s | RPM kept at 200 rpm
[   9.10s] EXP     RESET (key 1 - screen folded) - all values to base, wheels stopped and back to 0°. Release drive keys, then press again
```

## 10. 안내 미션 (PART 5)

책의 PART 5에서는 **같은 시작 파일**(`c01_start.py`)에서 5번 키로 안내 미션을 실행합니다. 5번 키를 누르면 로봇이 정해진 경로를 스스로 달려 안내 지점에 가서, 화면을 펼쳐 안내 문구를 보여 주고, 같은 길을 되짚어 출발한 자리로 돌아옵니다.

| 키 | 이름 | 하는 일 |
| --- | --- | --- |
| 5 | Start Mission | 미션 시작. 로봇이 멈춰 있고 화면 넷째 줄이 `READY`일 때만 받음 |
| 0 | Reset All | **미션 중단과 전체 RESET.** 바퀴를 멈추고, 화면을 접고, 대기 상태로 돌아감. 헷갈리면 0 |

미션은 네 가지 상태를 차례로 지나갑니다. 로봇 화면 첫 줄과 콘솔의 `STATE` 줄에 지금 상태가 보입니다.

```text
WAIT(대기) ──5번 키──▶ MOVE(이동) ──경로 끝──▶ GUIDE(안내) ──안내 시간 끝──▶ RETURN(복귀) ──▶ WAIT
                          │                        │                          │
                          └────────────── 0번 키: 언제든 WAIT로 ──────────────┘
```

| 상태 | 로봇이 하는 일 | 로봇 화면 |
| --- | --- | --- |
| WAIT | 평소처럼 키로 주행할 수 있음. 5번 키를 누르면 미션 시작 | 평소 상태 화면 (`STOP / KEY - / …`) |
| MOVE | `MISSION_ROUTE`의 동작을 차례로 실행 | `MOVE / STEP 2/3 / ROTATE R / READY` |
| GUIDE | 멈춰서 화면을 펼치고 안내 문구를 보여 줌 | 안내 문구 (예: `WELCOME`) |
| RETURN | 같은 경로를 거꾸로 되짚어 돌아옴 | `RETURN / STEP 1/3 / BACKWARD / READY` |

**바꿔 볼 값**은 시작 파일의 **4. MISSION 구역(94번째 줄)**에 모여 있습니다. 안내 문구는 1번 구역의 `GUIDE_MESSAGE`를 함께 씁니다.

| 값 | 기본값 | 뜻 |
| --- | --- | --- |
| `MISSION_ROUTE` | 앞으로 2초 → 오른쪽 제자리 회전 0.5초(약 90°) → 앞으로 1.5초 | 출발한 자리에서 안내 지점까지 가는 길. `(동작, 시간(초))`를 순서대로 적음 |
| `GUIDE_SEC` | 3.0 | 화면을 펼치고 안내하는 시간(초) |
| `STEP_PAUSE_SEC` | 0.5 | 동작 하나를 마치고 다음 동작까지 쉬는 시간(초) |
| `RETURN_TO_START` | True | True면 안내한 뒤 출발한 자리로 돌아옴. False면 안내 지점에서 끝냄 |

경로에 쓸 수 있는 동작은 `forward`(앞으로), `backward`(뒤로), `turn_left`·`turn_right`(방향 전환), `rotate_left`·`rotate_right`(제자리 회전), `pause`(멈춰서 기다리기)입니다. 모르는 동작이나 시간이 0인 동작은 건너뛰고 콘솔에 `NOTE` 줄을 남깁니다.

**출발 위치와 경로.** 경로는 지도 위의 좌표가 아니라 **로봇이 서 있는 자리를 기준으로 한 동작**입니다. 그래서 샌드박스 중앙에서 시작하든, 다른 시작 지점을 체크해 그곳에서 시작하든, 로봇이 놓인 자리에서 같은 경로를 그대로 실행합니다. 미션을 시작하기 전에 로봇이 가려는 방향을 보고 있는지 확인합니다. 돌아올 때는 경로의 순서를 뒤집고 각 동작을 반대 동작(앞으로 ↔ 뒤로, 왼쪽 ↔ 오른쪽)으로 바꿔 실행하므로, 차체 방향을 그대로 유지한 채 출발한 자리로 되돌아옵니다.

알아 둘 점이 다섯 가지 있습니다.

- **이 로봇은 시간으로 움직입니다.** 위치를 재는 센서가 없으므로, 동작이 끝났다는 것은 "정한 시간만큼 움직였다"는 뜻이지 "목표 지점에 도착했다"는 뜻은 아닙니다. 바닥이 미끄럽거나 차체가 밀리면 조금씩 어긋나고, 돌아온 자리도 출발 위치와 조금 다를 수 있습니다. 주행 시간은 바퀴가 실제로 구르기 시작한 때부터 잽니다(바퀴 방향을 바꾸는 시간은 빼고 잼).
- **회전 각도도 시간으로 정합니다.** 기본 경로의 `("rotate_right", 0.5)`는 기준값(Target RPM 120, Acceleration Time 0.5초)에서 약 90°를 도는 시간입니다. 출발할 때 0.5초 동안 속도가 올라가는 구간이 있어 각도가 시간에 정비례하지 않으므로, 180°에서 90°로 줄일 때도 시간을 딱 절반으로 줄이지 않았습니다. 바닥이나 로봇 상태에 따라 각도가 다르면 0.05초씩 고쳐 맞춥니다(더 돌면 줄이고, 덜 돌면 늘림).
- **미션도 1번 구역의 주행 설정을 그대로 씁니다.** 바퀴 모터 설정이나 조향 시간을 바꾸면 미션의 움직임도 함께 바뀝니다. 실험 모드에서 값을 바꿔 둔 채 5번을 누르면, 모든 값을 기준값으로 되돌린 뒤 출발합니다(콘솔에 `EXP  Experiment mode off …` 줄).
- **미션 중에는 주행 키·1번·2·3·4를 받지 않습니다.** 달리는 미션을 키로 흐트러뜨리지 않기 위해서이며, 누른 키는 `STATE  Key W ignored - mission is running`처럼 콘솔에 남습니다. 멈추려면 0을 누릅니다.
- **경로에는 대각선(Q·E) 동작이 없습니다.** 되돌아올 때 쓸 "뒤로 대각선" 동작이 없기 때문입니다. 방향은 제자리 회전으로 맞춥니다.

콘솔에는 상태와 단계가 한 줄씩 남습니다. 미션을 마치면 걸린 시간이 함께 남으므로, 경로를 고치기 전과 후를 비교할 수 있습니다.

```text
[   0.50s] STATE   WAIT → MOVE - 3 steps
[   0.50s] STATE   MOVE step 1/3: forward 2.00s
[   8.36s] STATE   MOVE → GUIDE - showing guide 3.0s
[  11.36s] STATE   GUIDE → RETURN - same route in reverse
[  19.24s] STATE   RETURN → WAIT - mission complete (18.7s)
```

## 11. 파일 구성과 제어 흐름

| 파일 | 역할 |
| --- | --- |
| `c01_start.py` | **시작 파일.** 바꿔 볼 값, 키 배정, 실험 단계표, 미션 경로, 그리고 "준비 → 반복" 세 줄 |
| `C01_Lib/c01lib_app.py` | 시작 파일의 값으로 로봇을 준비하고, 메인 루프에서 키를 읽어 주행·실험·미션을 움직임 |
| `C01_Lib/c01lib_keys.py` | 키 입력. 주행 키(누르고 있는 동안, 가장 나중 키 우선)와 명령 키(누르는 순간 한 번) |
| `C01_Lib/c01lib_robot.py` | 로봇 전체를 다루는 `Robot` 클래스. 조향 → 구동 순서, 화면·LOG 표시 |
| `C01_Lib/c01lib_drive.py` | 바퀴 DC 모터 4개를 한 묶음으로 제어 |
| `C01_Lib/c01lib_steering.py` | 조향 서보 4개를 한 묶음으로 제어 (자세별 각도, 시간차 조향) |
| `C01_Lib/c01lib_screen.py` | 화면 펼침·접힘 서보 제어 |
| `C01_Lib/c01lib_led.py` | 입력 반응 LED의 색 |
| `C01_Lib/c01lib_log.py` | 콘솔 LOG 형식과 기록 양 |
| `C01_Lib/c01lib_experiment.py` | PART 4 실험. 실험 모드, 값 적용, 자동 복원, RESET, 실험 화면 |
| `C01_Lib/c01lib_mission.py` | PART 5 안내 미션. 상태 흐름(`Mission`), 경로 실행(`RouteRunner`), 되돌아오는 경로 |
| `C01_Lib/c01lib_ports.py` | **하드웨어 대응표.** 부품별 Port, 모터 방향, 조향 부호, 화면 각도 |

시작 파일에서 시작해, 궁금한 주제가 생기면 해당 라이브러리 파일을 열어 봅니다.

| 알고 싶은 것 | 열어 볼 파일 |
| --- | --- |
| 한 번 준비하고 계속 반복하는 구조 | `c01_start.py`의 5. RUN, `c01lib_app.py` |
| 키를 누르고 떼는 것을 읽는 방법, 가장 나중 키 우선 | `c01lib_keys.py` |
| 네 바퀴 모터를 한 묶음으로 돌리는 방법 | `c01lib_drive.py` |
| 바퀴 방향과 화면 각도, 제한각 | `c01lib_steering.py`, `c01lib_screen.py`, `c01lib_ports.py` |
| 조향 → 기다림 → 구동 순서 | `c01lib_robot.py` |
| 콘솔 기록과 로봇 화면 표시 | `c01lib_log.py`, `c01lib_robot.py` |
| 실험 키로 값이 바뀌는 방법 | `c01lib_experiment.py` |
| 미션의 상태 흐름과 경로 실행 | `c01lib_mission.py` |

키를 누르면 명령이 다음 순서로 전달됩니다.

```text
키 입력 (W)
 → c01_start.py              app.update() 반복
 → c01lib_app.py / c01lib_keys.py   키를 읽고 robot.forward() 호출
 → c01lib_robot.py            조향 자세 확인, 필요하면 조향 후 구동
 → c01lib_steering.py / c01lib_drive.py
 → RoboCo Python API          ServoMotor.spin_to_degrees(), DCMotor.spin()
 → Port 1~8                    실제 서보와 모터
 → 로봇 움직임
```

이 책은 **RoboCo 공식 API**와 **저자가 만든 교육용 라이브러리**를 구분합니다. 이 코드에서 `robot.forward()` 같은 함수는 공식 API가 아니라 저자 라이브러리입니다.

| 구분 | 예 | 어디에 있는가 |
| --- | --- | --- |
| RoboCo 공식 API | `DCMotor.spin(power)`, `DCMotor.stop()`, `ServoMotor.spin_to_degrees(angle)`, `ServoMotor.limits_degrees`, `LED.color`, `TextScreen.text`, `Input.stream(key)`, `Runtime.quitting()`, `DCMotor.max_rpm`·`acceleration_time`·`brake_force`·`brake_time`, `DCMotor.max_torque`(읽기만) | RoboCo에 들어 있음 (Microcontroller의 **API** 버튼) |
| 저자 교육용 라이브러리 | `Robot.forward()`, `Robot.rotate_left()`, `Robot.toggle_screen()`, `DriveMotors.all_forward()`, `SteeringServos.begin_posture()`, `DriveKeys.update()`, `Experiment.step()`, `Mission.update()`, `reverse_route()`, `log()` | 이 폴더의 `C01_Lib` |

**시작 파일 맨 위의 "준비 코드"**는 두 가지 일을 합니다.

- **라이브러리를 새로 읽어 옵니다.** RoboCo는 스크립트를 다시 실행해도 Python을 처음부터 다시 시작하지 않고, 전에 읽은 라이브러리를 기억해 둡니다. 그러면 `C01_Lib`의 파일을 고쳐도 옛 내용이 계속 쓰입니다. 준비 코드는 실행할 때마다 RoboCo가 기억해 둔 이전 내용을 비우고 라이브러리를 새로 읽어 옵니다. 파일을 지우는 것은 아닙니다.
- **임시 파일을 만들지 않습니다.** Python은 보통 `__pycache__`라는 폴더에 임시 파일을 만듭니다. 매번 새로 읽어 오므로 이 임시 파일은 필요 없고, 폴더가 어지러워지지 않도록 만들지 않습니다.

## 12. 로봇 부품과 Port

같은 종류의 부품이 여러 개이므로, 코드는 부품마다 Port 번호를 직접 지정합니다.

| Port | 부품 | Port | 부품 |
| --- | --- | --- | --- |
| 0 | Input LED | 6 | RR-Steering Servo |
| 1 | FR-DC Motor | 7 | RL-Steering Servo |
| 2 | RR-DC Motor | 8 | FL-Steering Servo |
| 3 | RL-DC Motor | 9 | Screen-Fold Servo |
| 4 | FL-DC Motor | 10 | Text Screen |
| 5 | FR-Steering Servo | | |

FL은 앞왼쪽, FR은 앞오른쪽, RL은 뒤왼쪽, RR은 뒤오른쪽입니다. 화면을 보는 사람이 아니라 로봇이 향한 방향을 기준으로 합니다.

## 13. 바퀴나 화면이 다른 방향으로 움직일 때

로봇을 직접 고쳐 만들었거나 부품 방향이 다르면, `C01_Lib/c01lib_ports.py`에서 다음 값을 확인합니다. 방향 보정은 이 파일에서만 하고, 키 배정은 바꾸지 않습니다.

| 증상 | 확인할 값 |
| --- | --- |
| W를 눌렀는데 제자리에서 돈다 | `FLIP_FL_DRIVE`, `FLIP_RL_DRIVE` 등 모터 방향 (제공 로봇은 왼쪽 두 바퀴가 True) |
| Q를 눌렀는데 바퀴가 2시 방향을 본다 | `STEERING_SIGN_FL` 등 조향 부호 (제공 로봇은 네 개 모두 -1) |
| 1번 키를 눌렀는데 화면이 차체 안쪽으로 간다 | `SCREEN_DEPLOYED_DEG` (제공 로봇은 -90) |

한 번에 한 축만 바꾸고 다시 실행해 확인합니다. 같은 축을 두 곳에서 함께 뒤집으면 원래대로 돌아갑니다.

## 14. 문제 해결

| 증상 | 확인 | 해결 |
| --- | --- | --- |
| 콘솔에 `ModuleNotFoundError: No module named 'Book_C01_Robot'` | 폴더 위치와 이름 | `Book_C01_Robot`을 Scripts 바로 아래에 두고 이름을 바꾸지 않음 (3장) |
| 스크립트 목록에 모르는 파일이 보임 | 이전 버전 파일이 남았는지 | 폴더를 지우고 새로 넣음 |
| 실행했는데 콘솔에 `START` 줄이 없음 | 스크립트 연결 | 4장 순서로 시작 파일를 다시 LOAD |
| 키를 눌러도 움직이지 않음 | 불러온 로봇 파일 | 로봇2(Book-CORE-01-SCRIPT)인지 확인. 로봇1은 스크립트를 쓰지 않음 |
| Z·C·Q·E를 누르면 잠깐 멈췄다 움직임 | 화면 4번째 줄 | STEERING이면 정상. 조향 중에는 바퀴를 돌리지 않음 |
| 출발하거나 회전할 때 차체가 들썩임 | 8장의 값 | `ACCELERATION_TIME`을 늘리거나 `TARGET_RPM`을 낮춤. 키를 뗄 때 들썩이면 `RELEASE_RETURN_SEC`를 늘림 |
| 로봇 화면 글자가 잘림 | 글자 크기와 문구 길이 | `TEXT_SCREEN_FONT_SIZE`를 숫자로 줄임. 안내 문구는 한 줄 9자·4줄 이내 |
| 안내 문구가 나타나지 않음 | 화면을 펼친 순간의 상태 | 멈춘 상태에서 1번 키로 펼침. 주행 키를 누른 뒤라면 접었다가 다시 펼침 |
| 안내 문구의 한글이 보이지 않음 | 화면 글꼴 | 영문으로 씀 |
| 콘솔 기록이 더 늘지 않음 | 마지막 `NOTE` 줄 | `LOG_MAX_LINES`에 도달한 것. 값을 늘리거나 `LOG_LEVEL`을 낮춤 |
| 로봇 화면에 ERROR / STOPPED / SEE LOG | 콘솔의 `ERROR` 줄과 그 아래 Python 메시지 | 메시지에 나온 파일과 줄을 확인해 고친 뒤 다시 실행 |
| 2·3·4를 눌러도 값이 바뀌지 않고 화면도 펼쳐지지 않음 | 콘솔의 `EXP` 줄 | 3·4는 먼저 2를 눌러 실험 모드를 시작. 주행 키를 떼고 화면 넷째 줄이 READY가 된 뒤 누름 |
| 로봇이 이상하게 움직이거나 무엇을 바꿨는지 모르겠음 | — | 0을 눌러 전체 RESET |
| 1번으로 화면을 접었더니 바퀴가 멈추고 실험 값이 기준값으로 돌아감 | 콘솔의 `EXP  RESET (key 1 - screen folded)` 줄 | 정상. 화면 접기는 0과 같은 전체 RESET (9장) |
| 실험 뒤 다시 켰는데 로봇이 평소와 다르게 움직임 | 콘솔의 `SETUP  Motor (robot file)` 줄 | 스크립트를 켜고 0을 누른 뒤 멈추거나, 로봇 파일을 다시 불러옴 (9장) |
| 5번을 눌러도 미션이 시작되지 않음 | 콘솔의 `STATE`·`NOTE` 줄 | 주행 키를 떼고 화면이 READY일 때 누름. `MISSION_ROUTE`가 비어 있으면 시작하지 않음 |
| 미션 중에 키가 듣지 않음 | 콘솔의 `STATE  Key … ignored` 줄 | 정상. 미션 중에는 주행 키·1·2·3·4를 받지 않음. 멈추려면 0 |
| 시작 파일의 값을 바꿨는데 달라지지 않음 | 콘솔의 `NOTE  Unknown setting` 줄 | 값 이름의 철자를 확인 (8장) |
| 콘솔에 `NOTE  'MOTOR_POWER_RATIO' is no longer used …` | 1.6.0 시작 파일을 그대로 쓰는지 | 1.6.3 시작 파일로 바꾸거나 그 줄을 지움. 속도는 `TARGET_RPM`으로 정함 (15장) |
| 콘솔에 `NOTE  … is outside … is used` | 바퀴 모터 설정 값의 범위 | 8장 표의 범위 안으로 고침 |
| 미션에서 로봇이 목표 지점을 지나치거나 못 미침 | `MISSION_ROUTE`의 시간 | 한 번에 한 동작의 시간만 조금씩 고치고 다시 실행 (10장) |
| 미션을 마쳤는데 출발 위치와 조금 다른 곳에 섬 | — | 시간으로 움직이는 로봇이라 생기는 오차. 로봇 파일을 다시 불러와 출발 위치에서 다시 시작 (10장) |
| 라이브러리를 고쳤는데 결과가 그대로임 | 다시 실행했는지 | 스크립트를 멈추고 다시 실행. 실행할 때마다 라이브러리를 새로 불러옴 |

## 15. 버전

이 폴더의 배포 버전은 **1.6.3**입니다. 각 코드 파일의 맨 위에도 같은 번호가 적혀 있습니다.

**1.6.3에서 바뀐 점 (1.6.2 대비)**

- 안내 미션(5번 키) 경로의 제자리 회전을 0.8초에서 0.5초로 줄였습니다. 1.6.1부터 Z·C 회전도 Target RPM으로 돌게 되어 0.8초에 약 180°를 돌던 것을 약 90°로 맞췄습니다. 되돌아오는 경로의 왼쪽 회전도 같은 0.5초를 씁니다.

**1.6.2에서 바뀐 점 (1.6.1 대비)**

- `TARGET_RPM`의 최댓값을 1000으로 바로잡았습니다(RoboCo 설정 창의 범위).
- 실험 항목을 바퀴 모터 설정 네 가지로 정리했습니다. 2번 키는 RPM → ACCEL → BRAKE F → BRAKE T 순서로 고릅니다. STAGGER·RETURN은 기본 단계표에서 빠졌고, 단계표에 줄을 더하면 다시 쓸 수 있습니다.
- 실험 단계를 5단계에서 10단계로 늘렸습니다. 단계표에는 배수 대신 LV 1~10의 값을 그대로 적고, LV 10은 설정 창의 최댓값입니다. 그래서 4번 키로 최댓값까지 올릴 수 있습니다.
- 3·4로 바꾼 값은 2번으로 다른 값을 골라도 유지됩니다. 기준값으로 되돌리려면 0을 누르거나 1번으로 화면을 접습니다.
- 1번 키로 화면을 접으면 0번과 같은 전체 RESET을 합니다.

**1.6.1에서 바뀐 점 (1.6.0 대비)**

- 바퀴 모터 설정을 RoboCo 설정 창(DC Motor)과 같은 이름·기본값·범위로 맞췄습니다. `TARGET_RPM` 120 (0~10000), `ACCELERATION_TIME` 0.5 (0~10), `MAX_BRAKE_FORCE` 50 (0~1570), `BRAKING_TIME` 0.2 (0~10). 범위를 벗어난 값은 범위 끝 값으로 맞춥니다.
- `MAX_TORQUE`를 없앴습니다. Max Torque는 6290으로 고정된 값이라 스크립트가 바꾸지 않고, 시작할 때 `SETUP  Motor (fixed)` 줄에 기록만 합니다. 실험 항목 `TORQUE`도 없앴습니다.
- `MOTOR_POWER_RATIO`·`TURN_POWER_RATIO`·`PIVOT_POWER_RATIO`를 없앴습니다. 바퀴 속도는 `TARGET_RPM` 하나로 정하고, 여덟 주행 키가 모두 같은 Target RPM으로 바퀴를 돌립니다. 실험 항목 `POWER`도 없앴습니다(실험 항목 8개 → 6개).
- 로봇 화면 셋째 줄과 `DRIVE` 줄이 출력 비율(`PWR 1.00`, `power 1.00`) 대신 Target RPM(`RPM 120`, `rpm 120`)을 보여 줍니다.
- 1.6.0 시작 파일에 있던 이름이 남아 있으면 그 값은 쓰이지 않고, 이유를 적은 `NOTE` 줄이 콘솔에 남습니다.

이 코드는 책의 "이 책을 따라 하는 데 필요한 것"에 적힌 RoboCo 버전에서 확인했습니다. RoboCo가 업데이트되어 동작이 달라지면, 책의 추가 자료 페이지에서 최신 배포 버전과 정오표를 확인합니다. 새 버전을 받으면 3장의 규칙대로 기존 폴더를 지운 뒤 넣습니다.

## 16. 알아 두기

RoboCo는 Filament Games가 만든 게임입니다. 이 코드는 RoboCo 공식 예제나 공식 교재가 아니며, 이 책의 실습을 위해 저자가 작성했습니다. 사용과 재배포 조건은 이 책의 저작권 안내(판권면)를 따릅니다.
