Skip to main content
Summary

팔을 움직이는 로봇에서 공간을 이동하는 로봇으로

앞 장에서는 "빨간 컵을 집어 오른쪽에 놓아줘"라는 명령을 로봇팔의 Pick and Place 작업으로 바꾸었다. 이번에는 로봇이 팔이 아니라 바퀴로 움직인다. 사용자는 이렇게 말한다.

"회의실 앞으로 가줘."

이 문장은 짧지만 로봇에게는 여러 단계의 정보가 필요하다.

  • "가줘"는 이동 의도다.

  • "회의실"은 목적지 이름이다.

  • "앞으로"는 회의실 내부가 아니라 문 앞, 입구 앞, 또는 회의실 근처 대기 지점을 뜻할 수 있다.

  • 현재 로봇 위치에서 목적지까지 갈 수 있는 경로가 있어야 한다.

  • 주행 중 장애물을 피하고, 막히면 다시 계획해야 한다.

이동 로봇 에이전트의 핵심은 자연어 장소 표현을 지도 위의 목표 자세로 바꾸는 것이다. LLM은 "회의실"이라는 말을 이해할 수 있지만, Nav2는 "회의실"을 이해하지 못한다. Nav2가 받는 것은 지도 좌표계의 x, y, yaw다. 따라서 중간에 semantic map, 즉 의미 지도가 필요하다.

자연어 명령 이동 로봇AI 생성 이미지

이동 로봇 에이전트의 전체 구조

자연어 명령 이동 로봇은 다음 모듈로 나눌 수 있다.

사용자 명령
  ↓
command_parser / LLM planner
  ↓
semantic_map
  ↓
navigation_dispatcher
  ↓
Nav2 NavigateToPose
  ↓
monitor / replanner
  ↓
실행 결과 보고

LLM은 사용자의 말을 구조화한다.

{
  "intent": "navigate",
  "destination_name": "회의실",
  "relation": "front",
  "constraints": ["avoid_obstacles"],
  "need_confirmation": false
}

그다음 semantic_map은 회의실 + front를 실제 지도상의 목표 후보로 바꾼다.

{
  "place_id": "meeting_room_a",
  "label": "회의실 A",
  "target_pose": {
    "frame_id": "map",
    "x": 4.20,
    "y": 1.85,
    "yaw": 1.57
  }
}

마지막으로 navigation_dispatcher는 이 pose가 허용된 구역 안에 있는지, 로봇이 주행 가능한 위치인지, 현재 안전 상태가 정상인지 확인한 뒤 Nav2에 목표를 보낸다.

여기서 중요한 원칙은 로봇팔 프로젝트와 같다. LLM은 직접 바퀴 속도를 만들지 않는다. LLM은 목적지와 조건을 해석한다. 실제 경로 계획, 장애물 회피, 위치 추정, 제어는 Nav2와 로봇 제어 계층이 담당한다.

먼저 지도가 있어야 한다

자연어 목적지를 좌표로 바꾸려면 먼저 좌표를 올려놓을 지도가 있어야 한다. ROS2 이동 로봇에서는 보통 2D 지도와 위치 추정이 기본이다.

일반적인 흐름은 다음과 같다.

  1. 라이다나 깊이 센서 기반 스캔을 준비한다.

  2. slam_toolbox로 환경의 2D 지도를 만든다.

  3. /map 토픽이 정상적으로 발행되는지 확인한다.

  4. map => odom 변환이 존재하는지 확인한다.

  5. 저장된 지도를 Nav2 map_server로 불러온다.

  6. AMCL 같은 위치 추정 노드로 로봇의 현재 pose를 추정한다.

  7. Nav2에 NavigateToPose 목표를 보낸다.

Nav2 문서에서는 slam_toolbox를 이용해 2D SLAM 지도를 만들 수 있고, 이 과정에서 /map 토픽과 map => odom 변환이 제공된다고 설명한다. 또한 slam_toolbox와 nav2_amcl은 레이저 스캔 입력을 사용하며, 보통 /scan 토픽을 scan_topic 파라미터로 지정한다1.

예를 들어 시뮬레이션에서는 다음처럼 시작할 수 있다.

ros2 launch slam_toolbox online_async_launch.py use_sim_time:=true

지도와 변환은 반드시 RViz에서 확인해야 한다. 지도는 보이는데 로봇 위치가 엉뚱하다면 자연어 명령 이전에 위치 추정부터 고쳐야 한다. tf2_tools로 프레임 트리를 확인하는 것도 좋은 습관이다.

ros2 run tf2_tools view_frames

이동 명령은 map 좌표계에서 해석된다. semantic map의 waypoint도 같은 map 프레임을 기준으로 저장해야 한다. odom이나 base_link 기준 좌표를 섞으면 로봇은 다른 곳으로 갈 수 있다.

저장된 지도를 Nav2가 불러오는 방식

SLAM으로 만든 지도는 보통 이미지 파일과 YAML 메타데이터로 저장된다. YAML에는 이미지 경로, 해상도, 원점, 점유 임계값 같은 정보가 들어 있다.

image: office_map.png
resolution: 0.05
origin: [-2.0, -1.5, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196

여기서 resolution은 픽셀 하나가 실제 몇 미터인지 나타낸다. origin은 지도 이미지가 map 좌표계에서 어디에 놓이는지 정한다. semantic map에 저장하는 회의실, 복도, 충전 스테이션 좌표는 이 지도 좌표계와 일치해야 한다.

Nav2의 map_server는 지도 YAML 파일을 명령줄 인자로 직접 받기보다 ROS2 파라미터의 yaml_filename으로 불러오는 구조를 사용한다2. 예를 들면 다음과 같다.

map_server:
  ros__parameters:
    yaml_filename: "maps/office_map.yaml"

이 파일을 Nav2 bringup 설정에 포함하면 map_server가 저장된 지도를 OccupancyGrid로 발행한다. OccupancyGrid는 각 셀이 비어 있는지, 장애물인지, 모르는 영역인지를 표현하는 격자 지도다. Nav2의 전역 costmap은 이 정보를 바탕으로 주행 가능한 경로를 계산한다.

장소 이름을 semantic map으로 연결하기

고정 좌표만 있으면 "x=4.2, y=1.8로 가"는 가능하다. 하지만 사용자는 보통 그렇게 말하지 않는다. 사용자는 "회의실", "복사기 옆", "충전 스테이션", "문 앞", "가까운 엘리베이터"라고 말한다.

그래서 이동 로봇에는 다음과 같은 semantic map이 필요하다.

places:
  - id: meeting_room_a
    names: ["회의실", "큰 회의실", "회의실 A"]
    type: room
    area:
      polygon: [[3.5, 1.0], [5.2, 1.0], [5.2, 3.0], [3.5, 3.0]]
    entry_pose:
      x: 4.20
      y: 0.85
      yaw: 1.57
    front_pose:
      x: 4.20
      y: 0.65
      yaw: 1.57
    policies:
      allow_autonomous_entry: false

  - id: charger
    names: ["충전기", "충전 스테이션", "도킹 스테이션"]
    type: station
    docking_pose:
      x: -0.80
      y: 2.10
      yaw: 3.14

회의실은 단순한 문자열이 아니다. 지도 위의 영역이고, 문 앞 pose가 있고, 내부 진입 정책이 있고, 별칭이 있다. "회의실 앞으로 가줘"라고 하면 front_pose를 선택한다. "회의실 안으로 들어가"라고 하면 allow_autonomous_entry 정책을 확인한 뒤, 허용되지 않으면 사용자에게 확인해야 한다.

"회의실 안으로 들어가는 것은 현재 정책상 확인이 필요합니다.
회의실 문 앞까지만 이동할까요?"

이런 설계가 필요한 이유는 자연어가 항상 정확하지 않기 때문이다. "앞", "옆", "가까운 쪽"은 지도, 로봇의 현재 위치, 문의 방향, 사용자의 의도에 따라 달라진다. LLM은 추측으로 좌표를 만들면 안 된다. 후보가 여러 개면 질문해야 한다.

"회의실이 두 곳 있습니다. 큰 회의실과 작은 회의실 중 어디로 갈까요?"

LLM이 추출해야 할 정보

이동 명령에서 LLM이 추출할 항목은 로봇팔 명령과 조금 다르다.

항목예시설명
intentnavigate이동, 따라가기, 순찰, 복귀 등
destination회의실목적지 이름
relation앞, 옆, 내부장소 기준 상대 위치
route constraint복도 쪽으로, 유리문 피해서경로 조건
safety constraint사람 많은 곳 피해서안전 조건
priority빨리, 천천히속도나 정책 힌트
ambiguity후보 2개질문 필요 여부

예를 들어 "회의실 앞으로 가줘"는 단순하다.

{
  "intent": "navigate",
  "destination": "회의실",
  "relation": "front",
  "constraints": [],
  "confirmation_required": false
}

하지만 "가까운 회의실로 가줘"는 현재 위치가 필요하다. semantic_map에서 type이 meeting_room인 장소를 찾고, 현재 로봇 pose에서 각 entry pose까지의 경로 비용을 계산해 가장 가까운 후보를 선택할 수 있다. 이때 직선거리보다 Nav2 경로 비용을 쓰는 편이 좋다. 벽 너머로 가까운 장소가 실제로는 멀 수 있기 때문이다.

LLM 기반 내비게이션 연구에서도 LLM은 사용자 의도, 작업 유형, 목적지, 장애물이나 안전 구역 같은 제약을 추출하고, 모호한 명령에 대해서는 임의로 가정하지 않고 사용자에게 명확화를 요청해야 한다고 본다3. 이 원칙은 실험실 프로젝트에서도 그대로 적용된다.

semantic map이 target pose를 반환하면 navigation_dispatcher는 Nav2 액션 목표를 만든다. 핵심은 frame_id가 map이어야 한다는 점이다.

{
  "skill": "navigate_to",
  "args": {
    "target": {
      "frame_id": "map",
      "x": 4.20,
      "y": 0.65,
      "yaw": 1.57
    },
    "speed_policy": "normal",
    "timeout_sec": 120
  }
}

실제 ROS2에서는 이 값을 geometry_msgs/PoseStamped로 바꾸어 Nav2의 NavigateToPose 액션에 보낸다.

header.frame_id = "map"
pose.position.x = 4.20
pose.position.y = 0.65
pose.orientation = yaw_to_quaternion(1.57)

실행 중에는 다음 정보를 계속 모니터링한다.

  • 현재 로봇 위치가 추정되는가

  • 전역 경로가 생성되었는가

  • 로컬 장애물 때문에 멈춰 있는가

  • 목표까지 남은 거리는 얼마인가

  • Nav2 recovery 동작이 반복되고 있는가

  • 사용자가 취소나 정지 명령을 내렸는가

  • 제한 시간 안에 도착했는가

이 정보는 LLM에 그대로 원시 로그로 보내지 않는다. 디스패처가 요약한다.

{
  "skill": "navigate_to",
  "status": "failed",
  "reason_code": "PATH_BLOCKED",
  "message": "회의실 앞 경로가 35초 동안 막혀 있습니다.",
  "retryable": true,
  "suggested_recovery": ["choose_alternate_pose", "ask_user", "wait_and_retry"]
}

LLM은 이 실패 보고서를 보고 다음 계획을 세운다. 하지만 다시 말해, 최종 실행 여부는 디스패처와 안전 정책이 검사해야 한다.

막힌 경로와 불확실한 목적지 처리

이동 로봇에서 자주 생기는 실패는 다음과 같다.

실패원인대응
UNKNOWN_PLACEsemantic map에 장소 없음사용자에게 장소 선택 요청
AMBIGUOUS_PLACE후보가 여러 개후보 목록 제시
NO_VALID_PATH전역 경로 없음대체 waypoint 선택
PATH_BLOCKED사람, 박스, 문 등으로 막힘대기, 재계획, 우회 pose
LOCALIZATION_LOST현재 위치 추정 실패정지 후 재초기화 요청
TIMEOUT제한 시간 초과상태 보고 후 재시도 여부 확인
SAFETY_ZONE_VIOLATION금지 구역 포함목표 거부 또는 대체 pose

예를 들어 회의실 문 앞이 사람들로 막혀 있다면, semantic map에 front_pose_2나 nearby_waiting_pose를 준비해 둘 수 있다.

alternate_poses:
  - name: meeting_room_a_waiting_left
    x: 3.70
    y: 0.75
    yaw: 1.57
  - name: meeting_room_a_waiting_right
    x: 4.80
    y: 0.75
    yaw: 1.57

재계획은 다음 순서로 진행한다.

  1. 같은 목표로 Nav2 재계획을 한 번 시도한다.

  2. 일정 시간 막히면 가까운 대체 pose를 선택한다.

  3. 대체 pose도 실패하면 사용자에게 묻는다.

  4. 위험하거나 위치 추정이 깨졌으면 즉시 정지한다.

사용자에게는 짧게 말한다.

"회의실 앞이 막혀 있습니다. 회의실 왼쪽 대기 지점으로 이동할까요?"

좋은 에이전트는 실패를 숨기지 않는다. 현재 상태와 선택지를 알려 준다.

언어만으로 부족할 때

모든 장소를 말로 설명할 수 있는 것은 아니다. "저쪽 벽 근처", "방금 내가 표시한 곳", "이 경로로 돌아서 가" 같은 명령은 언어만으로 불명확하다. 이때는 보조 입력이 유용하다.

  • UI 지도에서 목적지를 클릭한다.

  • 지도 위에 금지 구역을 그린다.

  • 지나가야 할 경로를 선으로 그린다.

  • 음성으로 "여기 말고 이쪽 복도로 가"라고 수정한다.

LLM 기반 멀티모달 내비게이션 연구에서는 음성이나 텍스트 입력뿐 아니라 지도 위 스케치를 함께 사용해 장애물, 원하는 경로, 안전 거리 같은 조건을 보완하는 접근을 제안한다3. 실용적인 로봇 프로젝트에서도 같은 구조를 쓸 수 있다. LLM은 "사용자가 지도에 그린 선을 따라가라"는 의도를 해석하고, 실제 경로 제한은 내비게이션 모듈이 처리한다.

고정 장소 사전의 한계

semantic map 방식은 실용적이고 안전하다. 하지만 한계도 있다.

첫째, 사람이 미리 등록한 장소만 이해한다. 회의실, 복사기, 충전기는 가능하지만 "창문이 많은 조용한 곳"은 어렵다.

둘째, 환경이 바뀌면 갱신이 필요하다. 책상이 옮겨지고, 문이 막히고, 새로운 표지판이 생기면 semantic map도 바뀌어야 한다.

셋째, 장소 이름과 시각 단서를 직접 연결하지 못한다. 로봇이 카메라로 "EXIT" 표지판이나 "회의실" 문패를 읽고 목적지를 찾는 능력은 별도의 인식 시스템이 필요하다.

이 문제를 해결하기 위해 언어, 비전, 행동 모델을 결합하는 연구가 진행되고 있다. LM-Nav는 언어 모델, 이미지-언어 모델, 내비게이션 모델을 조합해 고정된 명령 데이터셋 없이 자연어 기반 장거리 이동을 수행하는 방향을 보여 주었다4. 이런 접근은 앞으로 "회의실"이라는 이름뿐 아니라 "빨간 소파가 있는 라운지", "프린터가 보이는 곳" 같은 표현을 시각 단서와 연결하는 데 중요해질 것이다.

다만 현재 실전 시스템에서는 고정 semantic map과 안전한 Nav2 실행 계층이 여전히 출발점이다. 최신 언어 기반 내비게이션 모델을 쓰더라도, 최종 목표 pose 검증, 금지 구역 검사, 충돌 회피, 정지 권한은 기존 로봇 안전 계층 안에 있어야 한다.

프로젝트 구현 순서

처음부터 자연어, LLM, Nav2, 음성, 비전 장소 인식을 모두 붙이면 디버깅이 어렵다. 다음 순서로 작게 만든다.

  1. slam_toolbox로 지도를 만들고 /map, map => odom을 확인한다.

  2. Nav2에서 RViz의 2D Goal Pose로 목표 이동을 성공시킨다.

  3. 주요 장소의 waypoint를 semantic_map.yaml에 수동 등록한다.

  4. navigate_to(place_id) 스킬을 만든다.

  5. "회의실로 가" 같은 간단한 명령을 JSON 계획으로 변환한다.

  6. 모호한 장소 후보가 있으면 사용자에게 질문한다.

  7. PATH_BLOCKED, TIMEOUT, LOCALIZATION_LOST 실패 코드를 표준화한다.

  8. 대체 waypoint와 재계획 루프를 추가한다.

  9. UI 지도 클릭이나 음성 명령을 보조 입력으로 붙인다.

  10. 카메라 기반 장소 인식, 표지판 인식, CLIP 기반 검색 같은 확장 기능을 실험한다.

이 프로젝트의 목표는 "LLM이 알아서 운전하는 로봇"이 아니다. 목표는 사용자의 장소 기반 명령을 안전한 내비게이션 목표로 바꾸고, 실행 결과를 다시 이해 가능한 형태로 돌려주는 로봇 에이전트를 만드는 것이다.

이제 로봇 에이전트는 물체를 집을 수 있을 뿐 아니라, 공간 안에서 목적지를 찾아갈 수 있다. 다음 단계에서는 키보드 입력 대신 음성으로 명령을 받고, 로봇이 실행 상태와 실패 이유를 말로 되돌려주는 인터페이스로 확장한다.

참고

1Mapping and Localization — Nav2 1.0.0 documentation

2nav2_map_server - ROS Package Overview

3Language and Sketching: An LLM-driven Interactive Multimodal Multitask Robot Navigation Framework

4LM-Nav: Robotic Navigation with Large Pre-Trained Models of Language, Vision, and Action

책 목록

AI 로봇 에이전트 만들기: 비전·로봇팔·학습·LLM으로 자연어 명령을 실행하는 로봇

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기