본문으로 건너뛰기

[Webots 로보틱스] Q-Learning 자율주행: 648개 상태 공간 이산화와 소켓 통신 아키텍처 (#1)

· 약 5분
Jihwan Park (jaybee)
AI & Robotics Engineer

Webots 시뮬레이션 환경

📌 핵심 요약 (TL;DR)

  • 문제(Problem): 연속적인 센서 거리·각도 데이터로 인해 고전적 표 기반 Q-Learning(Tabular Q-Learning) 적용 시 상태 공간 폭발 및 Webots 시뮬레이터와 학습 스크립트 간 실시간 동기화 오버헤드 발생.
  • 해결(Solution): 5차원 연속 센서 입력을 기하학적·물리적 의미를 가진 구간으로 분할하여 3 × 8 × 3 × 3 × 3 = 648개의 이산 상태(Bin)로 매핑(discretizer.py)하고, TCP 로컬 소켓(Port 5000, Line-delimited JSON) 기반의 IPC 제어 파이프라인 구축.
  • 결과(Result): O(1)의 초고속 Q-Table 갱신 속도를 확보하고, 시뮬레이션 물리 스텝(32ms)에 맞춘 안정적인 동기화 루프 완성.

1. 프로젝트 배경: 왜 Webots와 Q-Learning인가?​

자율주행 로봇이 미지의 실내 환경에서 장애물을 회피하며 목표 지점(Target Goal)에 도달하도록 만드는 것은 로보틱스의 고전적이면서도 까다로운 문제입니다. 심층 강화학습(DQN, PPO 등)은 복잡한 환경에 유연하게 대응할 수 있지만, 학습에 막대한 연산 자원이 소모되고 모델 가중치의 해석 가능성(Interpretability)이 낮다는 한계가 있습니다.

본 프로젝트에서는 **가장 직관적이면서도 수학적 수렴성이 검증된 표 기반 강화학습(Tabular Q-Learning, Off-policy TD(0))**을 Webots 물리 시뮬레이터 상의 차동 구동(Differential-drive) 모바일 로봇에 적용하여, 1.2m × 1.2m 실내 아레나에서 안정적인 목표 탐색 및 충돌 회피 정책을 달성하는 것을 목표로 설정했습니다.


2. 전체 시스템 아키텍처 (Webots ↔ Python IPC)​

시스템은 크게 **시뮬레이션 물리 환경을 담당하는 Webots 컨트롤러(Server)**와 **강화학습 알고리즘을 수행하는 Python 클라이언트(Client)**로 물리적·논리적으로 분리되어 동작합니다.

flowchart LR
subgraph Webots["Webots 3D Simulator (localhost:5000)"]
Robot["Supervisor Robot<br>(초음파 센서 + 모터)"]
HeadScan["헤드스캔 기구부<br>(좌/우 1.1 rad 회전)"]
Server["TCP Socket Server<br>(Line-delimited JSON)"]
Robot -→ HeadScan -→ Server
end

subgraph Python["Python RL Engine (Client)"]
Env["WebotsEnv (gym-like)<br>reset() / step()"]
Disc["Discretizer<br>(연속값 → 0~647 Bin)"]
Agent["QLearningAgent<br>(Q-Table 648x4)"]
Env -→ Disc -→ Agent
end

Server <-→|"request: {type: step, action: 0..3}<br>response: {state: [...], reward, done}"| Env
  • Webots 컨트롤러 (RL_controller.py): Webots의 Supervisor API를 활용하여 매 액션 스텝마다 로봇 바퀴를 480ms(15 ticks × 32ms) 동안 구동하고, 서보 모터로 헤드를 좌우 1.1 rad 회전시켜 장애물 거리를 정밀 스캔합니다.
  • 통신 프로토콜: 소켓 통신 시 오버헤드를 최소화하기 위해 줄바꿈 구분자 기반의 JSON(Line-delimited JSON)을 사용합니다.
    • 요청: {"type": "step", "action": 0} (0: 전진, 1: 후진, 2: 좌회전, 3: 우회전)
    • 응답: {"state": [0.45, 0.12, 0.35, 0.28, 0.40], "reward": 0.05, "done": false, "info": {...}}

3. 연속 센서의 648개 이산 상태 공간(Bin) 설계​

초음파 센서와 목표점 간의 위치 데이터는 연속 실수(Float) 값이므로, 표 기반 Q-Learning을 적용하려면 이를 **유한한 정수 상태 인덱스(State Index)**로 변환해야 합니다.

임의로 균등 분할할 경우 상태 수가 기하급수적으로 폭발(Curse of Dimensionality)하므로, 로봇의 기하학적 주행 특성에 맞추어 5가지 핵심 관측 변수를 정의하고 아래와 같이 세심하게 구간을 분할했습니다.

관측 변수구간 수분할 기준 (Thresholds)물리적 의미
목표 거리 (goal_distance)3[0.6m, 1.2m]근거리(Near), 중거리(Mid), 원거리(Far)
목표 상대각도 (goal_heading)8[-π, π] 균등 8등분 (각 45°)전방, 전우, 우, 후우, 후방, 후좌, 좌, 전좌
전방 장애물 (front_distance)3[0.15m, 0.30m]위험/충돌임박, 주의/접근, 안전
좌측 장애물 (left_distance)3[0.15m, 0.30m]위험, 주의, 안전
우측 장애물 (right_distance)3[0.15m, 0.30m]위험, 주의, 안전

총 상태 공간 크기: N_states = 3 × 8 × 3 × 3 × 3 = 648

이로 인해 Q-Table의 크기는 단 648 × 4 = 2,592개의 파라미터로 극적으로 압축되었으며, 일반 PC 메모리에서 수 밀리초 내에 인덱싱이 가능해졌습니다.

💻 상태 변환 구현 코드 (discretizer.py)​

import numpy as np

# 상태 차원 정의
N_GOAL_D, N_GOAL_H, N_FRONT, N_LEFT, N_RIGHT = 3, 8, 3, 3, 3
N_STATES = N_GOAL_D * N_GOAL_H * N_FRONT * N_LEFT * N_RIGHT # 648

# 임계값 정의
_HEADING_BINS = np.linspace(-np.pi, np.pi, 9)[1:-1] # 8분할 경계
_NEAR_BINS = np.array([0.15, 0.30]) # 초음파 안전/주의/위험 경계
_DIST_BINS = np.array([0.6, 1.2]) # 목표 거리 경계

def discretize(state_dict: dict) → int:
"""5차원 실수 관측값을 0~647 사이의 정수 인덱스로 변환"""
gd = int(np.digitize(state_dict["goal_distance"], _DIST_BINS))
gh = int(np.digitize(state_dict["goal_heading"], _HEADING_BINS))
df = int(np.digitize(state_dict["front_distance"], _NEAR_BINS))
dl = int(np.digitize(state_dict["left_distance"], _NEAR_BINS))
dr = int(np.digitize(state_dict["right_distance"], _NEAR_BINS))

# 다차원 인덱스를 1차원 평탄화(Flatten) 인덱스로 변환
index = ((((gd * N_GOAL_H + gh) * N_FRONT + df) * N_LEFT + dl) * N_RIGHT + dr)
return int(index)

4. 헤드스캔(Head-Scan) 메커니즘과 충돌 판정​

시스템 아키텍처

일반적인 2륜 로봇은 전방에 센서가 고정되어 있어 좌우 측면의 사각지대 장애물을 감지하기 어렵습니다. 이를 극복하기 위해 컨트롤러에 헤드스캔(Head-Scan) 메커니즘을 탑재했습니다.

  1. 액션 실행: 바퀴 모터를 3.0 rad/s 속도로 480ms 동안 구동.
  2. 헤드 회전: 서보 모터를 좌측으로 +1.1 rad (약 63°) 회전하여 좌측 거리 측정 후, 우측으로 -1.1 rad 회전하여 우측 거리를 측정.
  3. 정위치 복귀: 다시 정면(0 rad)으로 복귀하여 전방 거리를 최종 갱신.
  4. 종료 판정:
    • 목표 도달: 목표물과의 유클리드 거리가 $0.10m$ 이하일 때 $\rightarrow$ 성공 (done = True, 보상 $+1.0$)
    • 벽 충돌: 전방/좌/우 거리 중 하나라도 $0.08m$ 이하로 떨어질 때 $\rightarrow$ 실패 (done = True, 보상 $-1.0$)

5. 다음 편 예고​

이번 1회차에서는 강화학습 에이전트가 주행을 시작하기 위한 Webots 가상 환경 구축, TCP 소켓 기반 IPC 인터페이스, 648개 상태 공간 이산화 기법을 정리했습니다.

다음 2회차 포스팅에서는 본격적으로 Tabular Q-Learning 수식 구현, ε-Greedy 감쇠 탐색 전략, 그리고 시뮬레이션 환경에서의 보상 함수(Reward Function) 튜닝 과정을 다루겠습니다!