
DISPLAY도 없는 클라우드 서버에서 GAZEBO 시뮬레이션 시도해보기 4 (움직이는 물체 정의)
요약
클라우드 서버 환경에서 GAZEBO 시뮬레이터를 활용하여 강화학습(RL)용 포크리프트 모델을 정의하는 방법을 다룹니다. 실제 장비의 복잡한 구조 대신 RL 실험에 필요한 핵심 운동 구조(차동 구동, 리프트 등)만을 남긴 간략화된 SDF 모델 설계 과정을 설명합니다.
핵심 포인트
- 클라우드 서버 환경에서의 GAZEBO 실행을 위한 환경 변수 설정 방법
- 강화학습 실험을 위해 기능을 단순화한 포크리프트 SDF 모델 설계 사상
- 차동 구동(Differential Drive) 방식을 적용한 주행 모델 구현
- Python을 이용한 SDF 모델 파일 자동 생성 및 관리
지난번에는 움직이는 것(운반 대상)을 정의해 보았습니다만,
포크리프트(Forklift)라고 하며, 본래는 다음과 같은 형태의 창고에서 자주 사용되는 운반차입니다.

(TOYOTA L&F 상품 카탈로그 WEB에서 인용: https://www.toyota-lf.com/products/)
뭐, 움직임과 기본적인 형태만을 흉내 낸 것을 이번에 정의해 보았습니다.
전진·후진·회전 운동, 들어 올리는 막대 부분을 상하로 움직이는 기능을 갖추고 있습니다. 일단 이미 일부 충돌 감지 (Collision Detection) 센서는 넣어 두었습니다.
자, 향후 해설에 대해서입니다만, GAZEBO를 기동하기 위해 여러 가지 명령어를 입력해야 하는데, 조금이라도 매번 명령어를 직접 입력하는 것을 피하기 위해, ~/.bashrc의 마지막에 아래 내용을 추가해 둡니다. 이번 시리즈의 해설 기사에서는 이 부분을 생략하겠습니다.
# export DISPLAY=:99
# export XDG_RUNTIME_DIR=/tmp/runtime-root
# mkdir -p "$XDG_RUNTIME_DIR"
...
마지막의 /opt/gz_samples/myproj0/는 본인의 모델을 생성하는 위치에 맞춰 조정해 주세요.
제 환경에서는 이런 식으로 모델이 정의되어 있습니다.
# pwd
/opt/gz_samples/myproj0/models
# tree.
...
참고로, 실제 포크리프트와 이번 강화학습 (RL)에서 사용하려고 하는 포크리프트 모델은 이러한 대응을 상상하고 있습니다.

참고로 동일한 '움직임'을 LOOP로 표현할 수 있기 때문에, 포크리프트의 model.sdf 자체는 PYTHON으로 정의하여 생성하고 있습니다.
from pathlib import Path
OUT_DIR = Path("models/forklift_simple")
OUT_DIR.mkdir(parents=True, exist_ok=True)
...
위에서 만든 포크리프트는 다음과 같은 강화학습 (RL)을 위한 설계 사상에 기반하여 정의되어 있습니다.
이것은 실제와 똑같은 CAD 포크리프트가 아니라,
**창고 내 자율 주행 RL용 「기능을 남긴 간략화된 포크리프트 모델」**을 만들려고 하고 있습니다.
요컨대, 겉모습은 쌓기 놀이 블록 같지만, 설계 사상은 다음과 같습니다.
주행하는 차체
+
좌우 구동륜
+
후방/보조 캐스터
+
수직 리프트용 마스트
+
상하로 움직이는 포크 캐리지 (Fork Carriage)
+
좌우 2개의 포크
를 가진, 최소한의 포크리프트로서 필요한 운동 구조를 남긴 SDF 모델입니다.
👉 무엇을 재현하고 있는가
-
차체 / Base Chassis
base_link가 포크리프트 본체.
질량은 400 kg, 사이즈는 1.4m × 1.0m × 0.3m.
이것은 실제 포크리프트의 차체, 배터리·무게 중심 부분, 운전석이나 본체 프레임을 한데 모아 표현한 것입니다. -
좌우 구동륜
left_wheel_link와 right_wheel_link가 좌우 바퀴.
각각 left_wheel_joint, right_wheel_joint로 base_link에 연결되어 있습니다. 조인트 타입은 revolute.
나아가 DiffDrive plugin을 사용하여,
/model/forklift_simple/cmd_vel
로의 속도 명령으로 주행할 수 있도록.
즉, 현실의 스티어링 방식 포크리프트를 완전히 재현한다기보다, RL 실험용으로 좌우 구동륜으로 이동하는 차동 구동 (Differential Drive) 모델로서 작성. -
후방/보조 캐스터
caster_link는 구형의 보조 바퀴.
caster_joint는 fixed로, base_link에 고정.
이것은 실제 포크리프트의 리어 휠(Rear Wheel)이나 지지륜을 상당히 간략화하여 표현한 것. -
마스트 / 리프트 가이드
lift_guide_link가 마스트·리프트 가이드.
lift_guide_joint는 fixed로, 차체에 고정.
이것은 실제 포크리프트에서 말하는 수직 마스트, 포크를 상하로 안내하는 레일에 대응. -
포크 캐리지
fork_carriage_link가 상하로 움직이는 받침대.
이것은 lift_joint라는 prismatic joint로 lift_guide_link에 연결.
중요한 점은,
lift_joint type = prismatic
axis = z방향
lower = 0.0
...
즉, 포크 전체를 0m~8.4m 범위 내에서 상하로 움직이기 위한 기구입니다.
이는 이번 target_level=0-3, 즉 선반의 높이에 따라 포크를 올리는 RL (강화학습)의 핵심입니다.
- 좌우 2개의 포크
left_fork_link와 right_fork_link가 좌우 포크입니다.
각각 left_fork_joint, right_fork_joint를 통해 fork_carriage_link에 고정됩니다.
즉, 포크 자체가 개별적으로 움직이는 것이 아니라, 캐리지(carriage)와 일체가 되어 상하로 움직이는 구조입니다.
실제 Python 파일을 실행하면 다음과 같은 model.sdf로서 포크리프트의 Gazebo에서의 정의가 생성됩니다.
SDF 모델
SDF (Simulation Description Format)는 Gazebo에서 사용하는 XML 형식의 모델 정의 파일입니다.
모델의 형상, 무게, 관성, 충돌 판정, 조인트 (joint), 센서, 제어 플러그인 등을 정의합니다.
이 프로젝트에서는 포크리프트 모델을 Python으로 생성하여 다음과 같은 파일로 출력하고 있습니다.
models/forklift_simple/model.sdf
이 모델은 외형은 간략화되어 있지만, 강화학습에 필요한 구조로서 차체, 좌우 구동륜, 보조 캐스터, 마스트, 포크 캐리지, 좌우 2개의 포크를 가지고 있습니다. Python 생성 스크립트에서는 model.sdf와 model.config를 출력합니다.
주요 SDF 태그: 이 모델에서의 의미
<sdf> SDF 파일 전체의 루트 (root)
<model> 하나의 모델, 여기서는 forklift_simple을 정의
<link> 차체, 바퀴, 마스트, 포크 등의 부품<pose> 각 부품의 위치와 방향
<inertial> 질량과 관성 모멘트
<collision> 물리 계산 및 충돌 판정에 사용하는 형상
<visual> Gazebo 상에서 보이는 형상이나 색상
<surface><friction> 마찰 계수 mu, mu2 설정
<sensor type="contact"> 접촉·충돌 검출용 센서
<joint> 부품 간의 연결과 움직임 방식
<parent> / <child> 조인트로 연결되는 부모-자식 관계
<axis> 조인트의 이동·회전 방향
<limit> 가동 범위, 최대 힘, 최대 속도
<dynamics> 댐핑 (damping) 및 조인트 마찰
<plugin> Gazebo의 제어 기능, 주행이나 리프트 제어 등
이 포크리프트의 구조
base_link
├─ left_wheel_link / revolute joint
├─ right_wheel_link / revolute joint
...
중요한 것은 lift_joint입니다.
이는 prismatic joint, 즉 직선 방향으로 움직이는 조인트입니다.
이 모델에서는 Z 방향으로 0.0m에서 8.4m까지 움직이도록 설정되어 있으며, 선반의 높이에 따라 포크를 상하로 움직이기 위해 사용합니다.
이번에는 여기까지입니다. 그럼 다음 회차부터는 여기서부터 Gazebo라는 물리 시뮬레이션 내에서의 강화학습으로 무엇을 할 수 있는지 해설해 나가겠습니다!
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기