Skip to main content
Summary

로봇을 사지 않고 로봇을 배우는 방법

로봇공학을 배우려는 사람이 가장 먼저 부딪히는 질문은 의외로 단순하다. "로봇을 꼭 사야 할까?"

답부터 말하면, 꼭 그렇지는 않다. 물론 실제 모터가 돌아가고 센서가 흔들리는 물리 로봇을 만지는 경험은 중요하다. 하지만 처음부터 배터리, 전선, 모터 드라이버, 납땜, 전원 노이즈, 고장 난 커넥터와 씨름하다 보면 정작 로봇 소프트웨어의 핵심 구조를 놓치기 쉽다. 로봇이 어떻게 상태를 추정하고, 목표를 정하고, 명령을 계산하고, 다시 센서로 확인하는지 배우기 전에 '왜 안 켜지지?'라는 문제에 하루를 쓸 수도 있다.

소프트웨어 중심 학습 루트의 장점은 여기서 나온다. 하드웨어를 잠시 옆으로 치워 두고, 로봇의 뇌와 신경망부터 배운다. 가상의 로봇을 만들고, 가상의 센서를 붙이고, 그 센서가 만들어 내는 데이터를 보고, 명령을 보내 움직이게 한다. 그러다 보면 로봇 시스템이 사실상 하나의 반복 루프라는 사실이 몸에 익는다.

로봇 소프트웨어의 가장 작은 순환은 이렇게 생겼다.

센서 데이터를 읽는다. 현재 상태를 추정한다. 목표와 비교한다. 다음 명령을 계산한다. 로봇을 움직인다. 다시 센서 데이터를 읽는다.

이 루프를 이해하면 ROS2, Gazebo, SLAM, Navigation은 갑자기 어려운 이름들의 모음이 아니라 같은 구조 위에 올라간 도구들로 보이기 시작한다. 이 장의 목표는 바로 그 길을 안내하는 것이다. Python으로 작은 2D 시뮬레이션을 만들고, ROS2의 통신 구조를 익히고, Gazebo와 RViz로 가상 로봇을 다루며, TurtleBot 예제를 통해 SLAM과 Navigation까지 연결해 보는 흐름이다.

소프트웨어 중심 학습 루트

첫 단계는 Python 2D 시뮬레이션이다

ROS2를 바로 시작해도 된다. 하지만 완전한 초보자라면 그 전에 작은 Python 시뮬레이션을 직접 만들어 보는 것이 좋다. 여기서 말하는 시뮬레이션은 거창한 3D 그래픽이 아니다. 화면 위에 원 하나를 로봇으로 그리고, 선 몇 개를 벽으로 그리고, 로봇이 바퀴 속도에 따라 움직이게 만드는 정도면 충분하다.

이 작은 장난감 세계에서 배워야 할 것은 그래픽이 아니라 구조다. 로봇의 상태를 (x, y, θ)로 표현한다. 여기서 x와 y는 평면 위 위치이고, θ는 로봇이 바라보는 방향이다. 6장에서 배운 위치와 자세가 코드 속 변수로 바뀌는 순간이다.

처음에는 차동구동 로봇을 흉내 내면 좋다. 차동구동 로봇은 왼쪽 바퀴와 오른쪽 바퀴의 속도 차이로 움직인다. 두 바퀴가 같은 속도로 돌면 앞으로 가고, 왼쪽 바퀴가 더 빠르면 오른쪽으로 돈다. 양쪽 바퀴가 반대 방향으로 돌면 제자리에서 회전한다. 10장에서 배운 바퀴 로봇의 움직임을 코드로 옮기는 것이다.

다음으로 가상 엔코더를 만든다. 엔코더는 바퀴가 얼마나 돌았는지 알려 주는 센서다. 시뮬레이션에서는 실제 바퀴가 없으니, 로봇이 움직였다고 가정한 거리에서 엔코더 값을 계산하면 된다. 여기에 작은 노이즈를 더해 보자. 예를 들어 왼쪽 바퀴가 실제보다 1% 더 많이 돈 것처럼 보이게 하거나, 매번 측정값에 랜덤한 흔들림을 넣는다. 그러면 오도메트리가 조금씩 틀어진다. 로봇은 분명 직진했다고 생각하는데, 추정 위치는 조금 옆으로 새기 시작한다.

그다음은 가상 LiDAR다. 로봇 중심에서 여러 방향으로 보이지 않는 광선을 쏘고, 벽에 닿는 거리를 계산한다. 처음에는 36개 방향만 해도 된다. 10도마다 하나씩 거리값을 얻는 식이다. 이 거리 배열이 바로 아주 단순한 LaserScan이다. 여기에 거리 노이즈를 넣고, 가끔 일부 값이 사라지게 만들고, 한두 프레임 늦게 도착하도록 지연을 넣어 보자. 그러면 7장에서 배운 센서 오차와 불확실성이 추상적인 말이 아니라 직접 보이는 현상이 된다.

이 단계에서 만들어 볼 작은 실습은 다음과 같다.

  • 로봇의 현재 자세 (x, y, θ)를 화면에 표시하고, 키보드 입력으로 전진과 회전을 시킨다.

  • 왼쪽·오른쪽 바퀴 속도로부터 로봇의 다음 위치를 계산하는 차동구동 이동 모델을 구현한다.

  • 가상 엔코더와 가상 LiDAR 값을 만들고, 노이즈와 지연을 넣어 추정 위치가 어떻게 흔들리는지 관찰한다.

  • 목표 지점을 하나 찍은 뒤, 로봇이 목표 방향으로 회전하고 전진하는 간단한 제어 루프를 만든다.

  • 실제 위치와 추정 위치를 서로 다른 색으로 그려서 오차가 쌓이는 모습을 확인한다.

이 실습을 해 보면 중요한 감각이 생긴다. 로봇 소프트웨어는 '정답 좌표를 알고 움직이는 프로그램'이 아니다. 불완전한 센서값을 받아 현재 상태를 대충 추정하고, 그 추정에 기반해 다음 명령을 계산하는 프로그램이다. 나중에 ROS2에서 /odom, /scan, /cmd_vel, /tf 같은 이름을 만나도 당황하지 않는 이유가 여기에 있다. 이미 작은 세계에서 같은 일을 해 보았기 때문이다.

ROS2에 들어가기 전에 알아야 할 생각법

Python 시뮬레이션에서는 모든 것을 한 파일 안에 넣을 수 있다. 로봇 상태도, 센서도, 제어기도, 화면 출력도 같은 코드 안에 있다. 하지만 실제 로봇 시스템은 그렇게 만들기 어렵다. 카메라 데이터는 빠르게 들어오고, LiDAR는 다른 주기로 들어오고, 경로 계획은 상대적으로 느리며, 모터 제어는 아주 안정적으로 돌아야 한다. 모든 기능을 한 덩어리로 묶으면 고치기도 어렵고, 재사용하기도 어렵고, 어디서 문제가 생겼는지 찾기도 어렵다.

ROS2는 이 문제를 '노드'와 '메시지'로 푼다. 노드는 하나의 독립된 프로그램 단위다. 카메라 노드, LiDAR 노드, 위치 추정 노드, 경로 계획 노드, 모터 제어 노드처럼 기능별로 나눈다. 이 노드들은 직접 서로의 내부를 들여다보지 않는다. 대신 정해진 메시지 형식으로 데이터를 주고받는다.

이 구조를 사람 몸에 비유하면 쉽다. 눈은 시각 정보를 만든다. 귀는 소리를 듣는다. 뇌는 여러 감각을 조합해 판단한다. 근육은 명령을 받아 움직인다. 눈이 근육 속 코드를 직접 수정하지 않듯, 로봇 시스템에서도 LiDAR 노드가 모터 제어 노드의 내부 변수를 직접 바꾸지 않는다. LiDAR는 거리 데이터를 발행하고, 필요한 노드가 그 데이터를 구독한다.

ROS2 학습은 이 생각법을 받아들이는 과정이다. 처음에는 명령어가 많아 보이지만, 순서는 단순하게 잡으면 된다.

  1. Ubuntu 환경을 준비한다.

  2. ROS2 배포판을 설치하고 환경 설정을 한다.

  3. 워크스페이스를 만들고 src, build, install, log 구조를 익힌다.

  4. 패키지를 생성한다.

  5. colcon build로 빌드하고 source install/setup.bash로 현재 터미널에 반영한다.

  6. launch 파일로 여러 노드를 한 번에 실행한다.

  7. Python 노드를 작성해 토픽을 발행하고 구독한다.

  8. 서비스로 요청·응답 구조를 실습한다.

  9. 액션으로 오래 걸리는 작업을 시작하고, 진행 상태와 결과를 받아 본다.

명령어 예시는 이런 흐름을 따른다.

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
ros2 pkg create my_first_robot --build-type ament_python
cd ~/ros2_ws
colcon build
source install/setup.bash

여기서 핵심은 명령어 자체가 아니다. 워크스페이스는 로봇 소프트웨어 프로젝트의 작업장이고, 패키지는 기능 단위의 묶음이며, 빌드는 내가 작성한 코드를 ROS2가 실행할 수 있는 형태로 정리하는 과정이라는 점을 이해해야 한다.

토픽, 서비스, 액션은 로봇의 대화 방식이다

ROS2에서 가장 먼저 익혀야 할 통신 방식은 토픽이다. 토픽은 계속 흘러가는 데이터에 적합하다. LiDAR 스캔, 카메라 이미지, 바퀴 오도메트리, 로봇 속도 명령처럼 반복적으로 주고받는 데이터는 토픽으로 다룬다. 발행자는 데이터를 내보내고, 구독자는 필요한 데이터를 받아 간다.

예를 들어 키보드 조종 노드는 /cmd_vel이라는 토픽에 속도 명령을 발행할 수 있다. 이동 로봇의 구동 노드는 /cmd_vel을 구독하다가 새 명령이 오면 바퀴 속도로 바꾼다. 둘은 서로의 코드 구조를 몰라도 된다. 같은 토픽 이름과 같은 메시지 형식만 약속하면 된다.

서비스는 짧은 요청과 응답에 어울린다. "지금 맵을 저장해 줘", "이 파라미터 값을 바꿔 줘", "현재 상태를 알려 줘"처럼 한 번 묻고 한 번 답을 받는 구조다. 토픽이 라디오 방송이라면, 서비스는 전화 통화에 가깝다.

액션은 시간이 오래 걸리는 작업에 적합하다. "저 목표 지점까지 이동해"라는 명령은 즉시 끝나지 않는다. 로봇은 경로를 만들고, 이동하고, 중간에 장애물을 만나면 다시 계획하고, 실패할 수도 있다. 이럴 때 액션은 목표를 보내고, 진행 상황을 받고, 필요하면 취소하고, 마지막 결과를 받는 구조를 제공한다. Navigation에서 목표 지점까지 이동하는 작업이 대표적인 액션 사용 사례다.

처음 ROS2를 배울 때는 복잡한 로봇을 만들려고 하기보다 다음 세 가지를 확실히 해 보는 편이 좋다.

  • 숫자를 1초마다 발행하는 talker 노드와 그 숫자를 출력하는 listener 노드를 만든다.

  • 두 정수를 보내면 합을 돌려주는 service server와 client를 만든다.

  • 목표 숫자까지 천천히 세면서 중간 진행률을 보내는 action server와 client를 만든다.

이 장난감 예제가 로봇과 무슨 관련이 있냐고 생각할 수 있다. 하지만 이 구조가 그대로 로봇으로 확장된다. 숫자 발행은 센서 데이터 발행이 되고, 합 계산 서비스는 맵 저장이나 모드 전환 요청이 되며, 숫자를 세는 액션은 목표 지점 이동이나 로봇팔 pick-and-place 작업이 된다.

ROS2가 현대 로봇 소프트웨어의 중심이 된 이유

ROS2는 단순한 라이브러리 모음이 아니다. 여러 센서, 여러 프로세스, 여러 컴퓨터가 동시에 얽히는 로봇 시스템을 다루기 위한 통신 구조다. ROS1에서는 ROS Master가 노드 발견과 연결의 중심 역할을 했다. 반면 ROS2는 DDS 기반의 분산 발견 방식을 사용한다. DDS의 기본 발견 시스템은 중앙의 master 같은 도구 없이도 프로그램들이 서로를 찾고 통신할 수 있게 해 주며, 그래서 중앙 지점 하나가 고장 나 전체 통신이 막히는 위험을 줄일 수 있다.1

이 차이는 실제 로봇에서 중요하다. 작은 교육용 로봇 하나라면 중앙 관리 방식도 큰 문제가 아닐 수 있다. 하지만 로봇팔, 모바일 베이스, 카메라 PC, LiDAR PC, 원격 모니터링 노드, 클라우드 연결 노드가 함께 돌아가는 시스템에서는 통신 구조가 유연해야 한다. ROS2의 노드는 같은 컴퓨터 안에서도, 여러 컴퓨터에 흩어져서도 동작할 수 있다.

또 하나 중요한 개념은 QoS, 즉 Quality of Service다. 쉽게 말해 통신의 성격을 정하는 설정이다. 모든 메시지가 반드시 도착해야 하는가? 최신 메시지만 빠르게 받으면 되는가? 늦게 접속한 노드도 마지막 메시지를 받아야 하는가? 메시지가 일정 시간 안에 오지 않으면 문제로 볼 것인가? ROS2는 DDS의 QoS 정책을 활용해 이런 통신 방식을 세밀하게 조절한다.2

예를 들어 LiDAR나 카메라처럼 빠르게 쏟아지는 센서 데이터는 모든 프레임을 완벽히 받는 것보다 최신 데이터를 제때 받는 것이 더 중요할 때가 많다. 이때는 best effort 방식이 어울린다. 반대로 설정값 변경이나 중요한 상태 메시지는 손실되면 곤란하므로 reliable 방식이 더 적합할 수 있다. 지도처럼 늦게 켜진 RViz도 받아야 하는 데이터에는 transient local 같은 내구성 설정이 필요할 수 있다.

ROS2가 강조하는 실시간성도 이 맥락에서 이해할 수 있다. 여기서 실시간성은 무조건 빠르다는 뜻이 아니다. 정해진 시간 안에 응답해야 한다는 뜻이다. 모터 제어, 충돌 회피, 안전 정지 같은 기능은 평균 속도보다 최악의 지연 시간이 중요하다. ROS2의 QoS에는 deadline, lifespan, liveliness 같은 정책이 있어 통신이 시간 조건을 만족하는지 다루는 데 도움을 준다.2

보안도 ROS2에서 더 중요해졌다. 로봇이 실험실 안에서만 움직일 때는 보안이 뒤로 밀릴 수 있다. 하지만 병원, 공장, 물류창고, 도로, 가정에서 움직이는 로봇은 네트워크 위협을 고려해야 한다. ROS2는 DDS-Security 위에서 SROS2 도구를 통해 인증, 접근 제어, 암호화 같은 보안 기능을 사용할 수 있도록 설계되어 있다.3

결국 ROS2가 현대 로봇 소프트웨어에서 표준처럼 쓰이는 이유는 하나의 기능 때문이 아니다. 분산 통신, QoS, 시뮬레이션 연동, 풍부한 패키지 생태계, 다양한 언어 지원, 실제 로봇으로의 확장 가능성이 함께 작동하기 때문이다. 초보자에게 ROS2는 처음엔 거대한 산처럼 보이지만, 사실은 로봇 시스템을 기능별로 나누고 다시 연결하는 공통 언어라고 이해하면 된다.

Gazebo는 가상의 물리 실험실이다

Python 2D 시뮬레이션이 손으로 만든 종이 지도라면, Gazebo는 훨씬 정교한 가상 실험실이다. 여기서는 로봇이 중력을 받고, 바퀴가 바닥과 마찰하고, 관절이 제한 각도를 가지며, LiDAR와 카메라가 가상 세계를 측정한다. 실제 로봇을 만들기 전에 알고리즘을 시험하기 좋은 공간이다.

Gazebo에서 중요한 파일 형식이 몇 가지 있다. SDF는 Simulation Description Format의 약자로, 시뮬레이션 세계와 모델을 표현하는 형식이다. 월드 안에 바닥, 벽, 조명, 중력, 물리 엔진 설정, 장애물 등을 정의할 수 있다. URDF는 Unified Robot Description Format으로, 로봇의 링크와 관절 구조를 설명하는 데 자주 쓰인다. xacro는 URDF를 더 편하게 작성하기 위한 매크로 도구다. 반복되는 구조나 파라미터를 줄여 로봇 모델을 관리하기 쉽게 해 준다.

Gazebo 안의 로봇은 그냥 예쁜 3D 모델이 아니다. 바퀴가 달린 로봇이라면 차동구동 플러그인이 필요하고, 로봇팔이라면 관절 제어 플러그인이 필요하다. LiDAR를 달았다면 센서 플러그인이 가상의 LaserScan 데이터를 만들어야 한다. 카메라를 달았다면 이미지 토픽이 나와야 한다. 즉 Gazebo는 '보이는 장면'과 '로봇이 받는 데이터'를 동시에 만들어 주는 도구다.

여기서 ROS2와 Gazebo 사이를 연결하는 다리가 필요하다. 현재 Gazebo Sim과 ROS2는 서로 다른 통신 체계를 사용하므로, ros_gz_bridge 같은 브리지를 통해 데이터를 주고받는다. Gazebo 쪽의 토픽을 ROS2 쪽 토픽으로 옮기거나, ROS2에서 보낸 /cmd_vel 명령을 Gazebo 속 로봇의 속도 명령으로 전달하는 식이다. 공식 Gazebo 문서에서도 ros_gz_bridge가 ROS2와 Gazebo Transport 사이의 메시지 교환을 가능하게 하는 네트워크 브리지라고 설명한다.4

예를 들어 Gazebo에서 LiDAR가 /scan에 해당하는 데이터를 만들고 있다면, 브리지는 이를 ROS2의 sensor_msgs/msg/LaserScan 메시지로 바꿔 ROS2 노드들이 구독할 수 있게 한다. 반대로 ROS2에서 geometry_msgs/msg/Twist 형식의 속도 명령을 발행하면, 브리지가 Gazebo 쪽 이동 명령으로 전달한다. ROS2 공식 Gazebo 튜토리얼에서도 차동구동 로봇을 Gazebo에서 실행한 뒤, 브리지를 통해 속도 명령과 LiDAR 데이터를 주고받고 RViz에서 센서 데이터를 시각화하는 흐름을 보여 준다.5

이때 RViz의 역할도 구분해야 한다. Gazebo는 물리 세계를 시뮬레이션하는 도구다. 반면 RViz는 로봇이 '알고 있다고 믿는 정보'를 시각화하는 도구다. Gazebo에는 벽과 로봇이 실제로 존재한다. RViz에는 로봇 모델, TF 트리, LiDAR 스캔, 점유 격자 지도, 계획 경로, 로봇의 추정 위치가 표시된다. 그래서 디버깅할 때는 두 창을 동시에 보는 습관이 중요하다.

Gazebo에서 로봇은 실제로 벽 앞에 있는데 RViz의 LiDAR 점들이 엉뚱한 방향에 나타난다면, 센서가 고장 난 것이 아니라 좌표계 설정이 틀렸을 가능성이 크다. Gazebo에서는 로봇이 움직이는데 RViz의 로봇 모델이 가만히 있다면 TF나 joint state 연결을 확인해야 한다. Gazebo와 RViz를 함께 볼 줄 아는 능력은 로봇 소프트웨어 학습자의 중요한 디버깅 감각이다.

RViz는 로봇의 머릿속을 보여 준다

초보자가 ROS2를 배울 때 가장 답답한 순간은 "분명 실행은 됐는데 무슨 일이 일어나는지 모르겠다"는 느낌이 들 때다. 터미널에는 로그가 흐르고, 토픽 이름은 많고, 노드도 여러 개다. 이때 RViz는 로봇의 머릿속을 눈으로 보여 주는 창이 된다.

RViz에서 가장 먼저 확인할 것은 Fixed Frame이다. Fixed Frame은 모든 데이터를 어떤 기준 좌표계에서 볼지 정하는 설정이다. map, odom, base_link 같은 프레임 중 하나를 기준으로 삼는다. SLAM이나 Navigation을 할 때는 보통 map 또는 odom을 자주 쓴다. Fixed Frame이 잘못되면 데이터가 안 보이거나, 보이더라도 엉뚱한 위치에 나타난다.

그다음은 TF 트리다. TF는 좌표계 사이의 관계를 나타낸다. map에서 odom, odom에서 base_link, base_link에서 laser로 이어지는 관계가 있어야 LiDAR 데이터가 지도 위에 제대로 놓인다. TF가 끊기면 로봇은 센서값을 받아도 그 값이 어느 위치에서 측정된 것인지 알 수 없다. 사람으로 치면 눈은 보이는데 눈이 얼굴 어디에 붙어 있는지 모르는 상태다.

RViz에서 자주 보는 표시 항목은 RobotModel, TF, LaserScan, Map, Path, Odometry, Marker 등이다. RobotModel은 URDF로 정의한 로봇 형상을 보여 준다. LaserScan은 LiDAR가 본 장애물 점들을 보여 준다. Map은 SLAM으로 만든 점유 격자 지도를 보여 준다. Path는 Navigation이 계획한 경로를 보여 준다. Odometry는 로봇이 자신이 움직였다고 추정하는 궤적을 보여 준다.

초보자는 RViz를 예쁜 시각화 도구로만 생각하기 쉽다. 하지만 실제로는 강력한 디버깅 도구다. 로봇이 목표로 가지 못할 때, RViz를 보면 원인이 보이는 경우가 많다. 지도는 맞는데 로봇 위치가 틀렸는지, LiDAR 점은 맞는데 장애물 레이어가 반영되지 않는지, 전역 경로는 만들어졌는데 지역 경로가 막혔는지, TF가 끊겼는지 눈으로 확인할 수 있다.

TurtleBot으로 전체 파이프라인을 한 번에 경험하기

ROS2와 Gazebo를 어느 정도 익혔다면 TurtleBot 계열 예제로 전체 흐름을 경험하는 것이 좋다. TurtleBot은 교육과 연구에서 널리 쓰이는 작은 모바일 로봇 플랫폼이다. 실제 로봇도 있고 Gazebo 시뮬레이션도 잘 갖춰져 있어, 소프트웨어 중심 학습자에게 좋은 출발점이 된다.

가장 먼저 할 일은 시뮬레이션에서 TurtleBot을 실행하는 것이다. 사용하는 ROS2 배포판에 맞는 TurtleBot 패키지를 설치하고, 모델 환경 변수를 설정한 뒤 Gazebo 월드를 실행한다. TurtleBot 튜토리얼에서는 TURTLEBOT3_MODEL을 설정하고 Gazebo 월드를 띄운 다음, 키보드 조종 노드로 로봇을 움직이는 기본 흐름을 안내한다.6

source /opt/ros/<ros2-distro>/setup.bash
export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

다른 터미널에서는 키보드 조종 노드를 실행한다.

source /opt/ros/<ros2-distro>/setup.bash
export TURTLEBOT3_MODEL=waffle
ros2 run turtlebot3_teleop teleop_keyboard

이제 로봇을 움직이며 ROS2 토픽을 확인한다.

ros2 topic list
ros2 topic info /scan
ros2 topic echo /odom --once

이 짧은 확인이 중요하다. /scan은 LiDAR 데이터, /odom은 오도메트리, /cmd_vel은 속도 명령, /tf는 좌표계 관계다. TurtleBot이 움직이는 전체 파이프라인은 이 토픽들 위에 세워진다.

다음 단계는 SLAM이다. SLAM은 Simultaneous Localization and Mapping, 즉 위치 추정과 지도 작성을 동시에 하는 기술이다. 로봇은 처음 보는 공간에서 자신이 어디 있는지도 모르고 지도도 없다. 그래서 움직이면서 LiDAR와 오도메트리 데이터를 모아 지도를 만들고, 그 지도 안에서 자신의 위치도 함께 추정한다.

ROS2에서는 2D LiDAR 기반 SLAM에 slam_toolbox가 널리 사용된다. 이 패키지는 LaserScan과 TF 정보를 이용해 2D 지도를 만들고, 일반적인 지도 생성뿐 아니라 저장된 pose graph를 불러와 이어서 매핑하거나 localization 모드로 사용하는 기능도 제공한다.7

시뮬레이션에서 SLAM을 실행하고 로봇을 천천히 움직이면 RViz에 지도가 자라나기 시작한다. 처음에는 로봇 주변의 작은 영역만 보인다. 로봇이 복도를 따라 움직이면 벽이 생기고, 방을 한 바퀴 돌면 닫힌 구조가 만들어진다. 여기서 중요한 것은 빠르게 움직이는 것이 아니라 좋은 데이터를 모으는 것이다.

지도 작성이 끝나면 map saver로 지도를 저장한다.

ros2 run nav2_map_server map_saver_cli -f maps/my_map

보통 두 파일이 생긴다. my_map.pgm은 흑백 이미지 형태의 점유 격자 지도이고, my_map.yaml은 해상도, 원점, 점유 임계값 같은 메타데이터를 담는다. 이 둘은 한 쌍으로 관리해야 한다. yaml 파일 안의 이미지 경로가 틀리면 Navigation에서 지도를 불러오지 못한다.

이제 Navigation으로 넘어간다. Navigation은 현재 위치, 지도, 목표 지점을 받아 로봇을 목적지까지 보내는 전체 시스템이다. Nav2 문서에서는 로봇이 현재 pose, map, goal을 바탕으로 목표에 도달할 계획을 생성하고, 안전 제약과 장애물 회피를 고려해 주행 명령을 출력한다고 설명한다.8

ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=True map:=maps/my_map.yaml

RViz가 열리면 먼저 2D Pose Estimate로 로봇의 초기 위치를 알려 준다. 실제 로봇이 지도 어디쯤에 있는지 대략 찍어 주는 과정이다. 완벽하게 맞출 필요는 없다. LiDAR 데이터와 지도 윤곽이 어느 정도 맞으면 localization이 점차 보정한다. 그다음 Nav2 Goal을 눌러 목표 지점을 찍는다. 그러면 전역 경로가 만들어지고, 로봇은 지역 장애물을 보며 속도 명령을 계산해 이동한다.

이 전체 흐름은 소프트웨어 중심 학습에서 매우 중요하다. Gazebo에서 로봇 실행, ROS2 토픽 확인, SLAM으로 지도 작성, 지도 저장, Navigation에 지도 연결, RViz에서 초기 위치 지정, 목표 지점 명령, 자율 주행. 이 한 사이클을 여러 번 반복하면 로봇 소프트웨어의 큰 구조가 손에 잡힌다.

좋은 SLAM 지도는 천천히 만들어진다

SLAM을 처음 실행하면 지도가 만들어지는 모습이 신기해서 로봇을 빨리 움직이고 싶어진다. 하지만 좋은 지도는 급하게 만들기 어렵다. 로봇이 너무 빠르게 회전하면 LiDAR 스캔과 오도메트리 사이의 정합이 어려워진다. 긴 복도처럼 특징이 적은 공간에서는 로봇이 실제보다 더 많이 이동했다고 착각할 수도 있다. 바퀴가 미끄러지면 오도메트리가 틀어지고, 유리벽이나 반사 물체는 LiDAR를 혼란스럽게 만든다.

지도 작성의 기본 요령은 단순하다. 천천히 움직이고, 같은 장소를 다른 방향에서 다시 보고, 큰 루프를 닫고, 좁은 구역은 나중에 세밀하게 훑는다. '루프를 닫는다'는 것은 로봇이 출발 지점이나 이전에 지나간 장소로 돌아와 "아, 여기를 전에 봤다"라고 인식하게 만드는 것이다. 이 과정이 잘 되면 누적 오차가 줄어들고 지도가 더 일관되게 맞춰진다.

SLAM 실습 중에는 RViz에서 세 가지를 자주 확인하자. 첫째, LiDAR 점들이 실제 벽 윤곽과 잘 맞는가. 둘째, 로봇의 추정 위치가 갑자기 튀지 않는가. 셋째, 만들어진 지도에 이중 벽이나 찢어진 복도가 생기지 않는가. 이중 벽은 보통 위치 추정이 조금씩 밀린 상태에서 같은 벽을 여러 번 본 결과다.

지도 품질을 높이는 습관은 다음과 같다.

  • 로봇을 빠르게 회전시키지 말고, 일정한 속도로 천천히 움직인다.

  • 긴 복도만 왕복하지 말고, 기둥·모서리·문처럼 구분되는 특징을 충분히 보게 한다.

  • 가능한 한 큰 루프를 먼저 닫은 뒤, 작은 공간과 세부 구역을 채운다.

  • 움직이는 사람이나 임시 장애물이 지도에 오래 남지 않도록 주의한다.

  • 저장한 지도는 RViz나 이미지 뷰어로 확인하고, 실제 환경과 크게 다른 부분이 없는지 검토한다.

이 단계에서 중요한 태도는 'SLAM은 마법이 아니다'라는 사실을 받아들이는 것이다. SLAM은 센서 데이터와 이동 추정을 이용해 가장 그럴듯한 지도를 만드는 알고리즘이다. 입력 데이터가 나쁘면 결과도 흔들린다. 좋은 로봇 소프트웨어 개발자는 알고리즘 이름을 외우는 사람이 아니라, 데이터가 왜 나빠졌는지 찾아낼 수 있는 사람이다.

Navigation을 실행하면 마치 하나의 프로그램이 로봇을 알아서 움직이는 것처럼 보인다. 하지만 실제로는 여러 기능이 동시에 협력한다. 지도 서버는 저장된 지도를 제공한다. localization 노드는 현재 로봇 위치를 추정한다. planner는 목표까지의 전역 경로를 만든다. controller는 지금 당장 따라갈 속도 명령을 계산한다. costmap은 장애물과 위험 구역을 표현한다. behavior tree는 전체 행동 흐름을 조율한다.

이 구조를 이해하면 문제가 생겼을 때 덜 당황한다. 로봇이 출발하지 않는다고 해서 항상 모터 문제가 아니다. localization이 안 되어 현재 위치를 모를 수도 있다. 전역 경로가 만들어지지 않는다면 지도 위에서 목표 지점이 장애물 안에 찍혔을 수 있다. 전역 경로는 있는데 로봇이 멈춘다면 local costmap이 장애물로 가득 차 있을 수 있다. 목표로 가다가 자꾸 돌아선다면 recovery behavior가 반복되고 있을 수 있다.

RViz에서 Navigation을 디버깅할 때는 다음을 차례로 본다.

  • 지도와 LiDAR 스캔이 서로 잘 겹치는지 확인한다.

  • 로봇 모델의 위치와 방향이 실제 Gazebo 속 로봇과 비슷한지 본다.

  • global path가 목표까지 생성되는지 확인한다.

  • local costmap에 장애물이 과도하게 표시되어 있지 않은지 본다.

  • /cmd_vel이 실제로 발행되는지 ros2 topic echo /cmd_vel로 확인한다.

  • TF 트리에서 map → odom → base_link → laser 흐름이 끊기지 않았는지 확인한다.

초보자에게 Navigation은 복잡한 검은 상자처럼 느껴질 수 있다. 하지만 앞에서 Python 시뮬레이션으로 만든 루프를 떠올리면 구조가 보인다. 현재 상태를 추정하고, 목표와 비교하고, 경로를 만들고, 속도 명령을 계산하고, 센서로 다시 확인한다. 다만 이 일을 혼자 하는 함수 하나가 아니라 여러 ROS2 노드가 나누어 맡고 있을 뿐이다.

디버깅 도구를 먼저 익히면 성장 속도가 빨라진다

소프트웨어 중심 학습에서 가장 큰 차이를 만드는 능력은 디버깅이다. 로봇 개발은 '한 번에 되는 코드'를 쓰는 일이 아니다. 거의 항상 무언가 안 된다. 토픽 이름이 다르거나, QoS가 맞지 않거나, TF가 끊겼거나, 시뮬레이션 시간이 안 맞거나, launch 파일이 다른 파라미터를 읽고 있을 수 있다.

ROS2에서는 터미널 도구를 적극적으로 써야 한다.

ros2 node list
ros2 topic list
ros2 topic info /scan
ros2 topic echo /scan --once
ros2 topic hz /scan
ros2 service list
ros2 action list
ros2 param list

ros2 node list는 지금 어떤 노드들이 살아 있는지 보여 준다. ros2 topic list는 데이터가 흐르는 통로를 보여 준다. ros2 topic info는 누가 발행하고 누가 구독하는지 알려 준다. ros2 topic hz는 데이터가 몇 Hz로 들어오는지 확인하게 해 준다. 센서 데이터가 너무 느리거나 아예 들어오지 않을 때 매우 유용하다.

rqt_graph도 좋은 도구다. 노드와 토픽의 연결 관계를 그래프로 보여 준다. 초보자에게는 이 그림 하나가 수십 줄의 로그보다 더 많은 것을 말해 준다. 내가 만든 노드가 /cmd_vel을 발행한다고 생각했는데 실제로는 /robot/cmd_vel에 발행하고 있었다면, rqt_graph에서 바로 보인다.

rosbag도 일찍 익히면 좋다. rosbag은 ROS2 토픽 데이터를 녹화하고 재생하는 도구다. 실제 로봇을 움직이며 얻은 /scan, /odom, /tf 데이터를 저장해 두면, 나중에 로봇 없이도 같은 데이터를 재생하며 SLAM이나 인식 알고리즘을 테스트할 수 있다. 소프트웨어 중심 학습자에게 rosbag은 가상의 실험 데이터를 쌓아 두는 창고와 같다.

자주 만나는 문제에는 패턴이 있다.

  • RViz에 아무것도 안 보이면 Fixed Frame과 TF를 먼저 확인한다.

  • Gazebo에서는 움직이는데 ROS2에서 데이터가 안 보이면 bridge와 토픽 이름을 확인한다.

  • LiDAR 데이터는 있는데 지도 작성이 안 되면 scan_topic, base_frame, odom_frame 설정을 확인한다.

  • Navigation이 출발하지 않으면 초기 위치 지정, map 파일 경로, lifecycle 상태를 확인한다.

  • 시뮬레이션에서 시간이 이상하면 use_sim_time 설정과 /clock 토픽을 확인한다.

  • 토픽이 있는데 메시지가 오지 않으면 QoS 호환성을 의심해 본다.

이런 디버깅 습관은 나중에 실제 로봇으로 넘어갈 때 큰 힘이 된다. 실제 로봇에서는 여기에 배터리, Wi-Fi, 센서 케이블, 모터 드라이버, CPU 부하 같은 문제가 추가된다. 시뮬레이션에서 소프트웨어 문제를 구분해 내는 훈련을 해 두면 실제 하드웨어 문제를 만났을 때도 훨씬 침착하게 접근할 수 있다.

소프트웨어 중심 학습자의 추천 순서

이제 전체 학습 루트를 하나의 흐름으로 정리해 보자. 중요한 것은 처음부터 모든 것을 완벽히 이해하려 하지 않는 것이다. 로봇 소프트웨어는 나선형으로 배운다. 한 바퀴 돌 때마다 같은 개념을 더 깊게 이해하게 된다.

첫 번째 순환은 Python 2D 시뮬레이션이다. 좌표계, 차동구동, 가상 엔코더, 가상 LiDAR, 노이즈, 지연, 간단한 목표 추종을 직접 만든다. 이 단계에서는 ROS2를 쓰지 않아도 된다. 로봇 소프트웨어 루프의 감각을 얻는 것이 목표다.

두 번째 순환은 ROS2 기본 통신이다. 노드, 토픽, 서비스, 액션을 작은 예제로 익힌다. 워크스페이스와 패키지 구조, colcon 빌드, launch 파일, 파라미터를 다룬다. 이 단계에서는 로봇보다 통신 구조가 중요하다.

세 번째 순환은 Gazebo 연동이다. URDF나 xacro로 간단한 로봇 모델을 보고, SDF 월드를 실행하고, ros_gz_bridge로 Gazebo와 ROS2 토픽을 연결한다. RViz에서 RobotModel, TF, LaserScan을 확인한다. Gazebo는 물리 세계, RViz는 로봇의 내부 인식이라는 구분을 몸에 익힌다.

네 번째 순환은 TurtleBot SLAM이다. Gazebo에서 TurtleBot을 실행하고, /scan, /odom, /tf를 확인한 뒤, slam_toolbox로 지도를 만든다. 로봇을 천천히 움직이며 좋은 지도와 나쁜 지도의 차이를 본다. map saver로 지도를 저장하고 yaml과 pgm 파일의 관계를 이해한다.

다섯 번째 순환은 Navigation이다. 저장한 지도를 Nav2에 연결하고, RViz에서 초기 위치를 지정하고, 목표 지점을 보낸다. global path, local costmap, /cmd_vel, TF를 보며 로봇이 왜 움직이는지 또는 왜 멈추는지 분석한다.

여섯 번째 순환은 반복과 변형이다. 월드를 바꾸고, 장애물을 추가하고, 센서 노이즈를 키우고, 로봇 모델을 바꾸고, 파라미터를 조정한다. 같은 파이프라인을 여러 조건에서 반복하면 '명령어를 따라 했다'는 수준에서 '시스템을 이해한다'는 수준으로 넘어간다.

그다음에 배워야 할 로봇 소프트웨어 공학

소프트웨어 중심 루트의 끝은 Gazebo에서 TurtleBot을 움직이는 것이 아니다. 그것은 출발선에 가깝다. 다음 단계는 로봇 소프트웨어를 더 안정적이고 재사용 가능하게 만드는 능력이다.

Python은 시작하기 좋지만, 많은 로봇 시스템에서는 C++도 중요하다. 센서 데이터 처리, 실시간에 가까운 제어, 고성능 플래닝에서는 C++ 노드가 자주 쓰인다. 따라서 Python으로 구조를 익힌 뒤에는 C++, CMake, ament_cmake 패키지 구조를 배워야 한다. 처음부터 C++로 모든 것을 하려 하면 문법에 막힐 수 있으니, Python으로 개념을 익힌 뒤 C++로 확장하는 순서가 자연스럽다.

launch 관리도 중요하다. 실제 로봇 시스템은 노드 하나로 실행되지 않는다. 센서 드라이버, robot_state_publisher, localization, planner, controller, RViz, bridge, parameter file이 함께 실행된다. launch 파일은 이 복잡한 실행 절차를 재현 가능하게 만든다. 좋은 launch 파일은 실험을 안정적으로 반복하게 해 준다.

파라미터 관리도 학습해야 한다. SLAM과 Navigation은 수많은 파라미터를 가진다. 로봇 크기, inflation radius, planner 설정, controller gain, scan topic, frame name, update rate 같은 값이 모두 동작에 영향을 준다. 파라미터를 코드에 박아 넣지 않고 yaml 파일로 관리하는 습관은 프로젝트가 커질수록 중요해진다.

테스트와 로그 분석도 로봇 개발자의 기본기다. "내 컴퓨터에서는 됐는데요"는 로봇에서는 위험한 말이다. 같은 launch를 실행했을 때 같은 노드가 뜨는지, 필요한 토픽이 나오는지, 메시지 주기가 유지되는지, 특정 상황에서 노드가 죽지 않는지 확인해야 한다. 로그를 읽고, rosbag을 재생하고, 실패한 실험을 다시 재현하는 능력이 프로젝트의 품질을 좌우한다.

소프트웨어 중심 학습자가 확장해야 할 역량은 다음과 같다.

  • C++ 노드 작성, CMake, ament_cmake 패키지 구조 이해

  • launch 파일, 파라미터 yaml, namespace, remapping 관리

  • TF 트리 설계와 robot_state_publisher 사용

  • rosbag 기록·재생, 로그 분석, topic hz 측정

  • 단위 테스트와 통합 테스트 작성

  • Docker나 dev container를 활용한 재현 가능한 개발 환경 구성

  • Git 브랜치 전략, 코드 리뷰, 실험 기록 관리

이 역량들은 화려해 보이지 않을 수 있다. 하지만 실제 로봇 프로젝트에서는 알고리즘만큼이나 중요하다. 로봇은 복잡한 시스템이고, 복잡한 시스템은 '잘 정리된 소프트웨어 습관' 없이는 오래 버티지 못한다.

시뮬레이션에서 실제 로봇으로 넘어갈 때

소프트웨어 중심 학습의 가장 큰 장점은 실제 로봇으로 넘어갈 때도 많은 지식이 그대로 이어진다는 점이다. Gazebo에서 /cmd_vel을 받아 움직이던 로봇은 실제 로봇에서도 같은 이름의 토픽을 사용할 수 있다. Gazebo에서 /scan으로 들어오던 LiDAR 데이터는 실제 LiDAR 드라이버가 같은 형식으로 발행할 수 있다. RViz에서 보던 TF와 지도, 경로도 그대로 쓸 수 있다.

물론 모든 것이 자동으로 해결되지는 않는다. 실제 로봇에는 바퀴 미끄러짐, 센서 장착 오차, 네트워크 지연, 전원 문제, 바닥 재질 차이, LiDAR 반사 문제, 모터 응답 지연이 있다. 하지만 인터페이스가 같다면 소프트웨어 구조는 유지할 수 있다. 이것이 ROS2와 시뮬레이션을 함께 배우는 이유다.

좋은 전환 전략은 '한 번에 전부 바꾸지 않는 것'이다. 먼저 Gazebo에서 잘 동작하는 Navigation 파이프라인을 만든다. 그다음 실제 로봇에서 센서 토픽만 확인한다. /scan이 제대로 나오는지, /odom이 움직임과 맞는지, TF가 끊기지 않는지 본다. 그 후 SLAM을 시도하고, 마지막으로 Navigation을 연결한다. 문제가 생기면 방금 바꾼 부분부터 의심한다.

시뮬레이션과 실제 로봇을 잇는 핵심 질문은 이것이다.

"내 알고리즘은 실제 하드웨어를 직접 알고 있는가, 아니면 표준 메시지 인터페이스만 알고 있는가?"

후자에 가까울수록 좋은 구조다. Navigation은 모터 드라이버의 내부 회로를 몰라도 /cmd_vel을 보낼 수 있어야 한다. SLAM은 LiDAR 제조사를 몰라도 LaserScan을 받을 수 있어야 한다. 로봇 소프트웨어의 힘은 이 추상화에서 나온다.

이 루트의 최종 목표

소프트웨어 중심 학습 루트의 최종 목표는 "명령어를 따라 쳐서 TurtleBot을 움직였다"가 아니다. 목표는 로봇 소프트웨어 시스템을 스스로 분해하고 다시 조립할 수 있는 능력이다.

처음에는 Python 2D 시뮬레이션에서 작은 원 하나를 움직인다. 그 원은 곧 ROS2 노드가 되고, Gazebo 속 TurtleBot이 되고, RViz의 지도 위를 달리는 자율주행 로봇이 된다. 그 과정에서 좌표계, 센서, 오도메트리, TF, 토픽, QoS, SLAM, Navigation이 하나의 흐름으로 연결된다.

이 루트를 따라가면 하드웨어가 없어도 로봇공학의 핵심을 꽤 깊게 배울 수 있다. 오히려 처음에는 하드웨어가 없기 때문에 더 잘 보이는 것들이 있다. 데이터가 어디서 생기고, 어떤 메시지로 흐르고, 어떤 노드가 판단하고, 어떤 명령이 움직임으로 바뀌는지 또렷하게 볼 수 있다.

나중에 실제 로봇을 만나면 그때는 더 이상 낯선 기계가 아니다. Gazebo에서 보았던 구조가 금속과 플라스틱, 전선과 모터 위에 올라간 것뿐이다. 로봇은 여전히 센서로 보고, 상태를 추정하고, 계획하고, 명령을 보내고, 피드백으로 보정한다. 차이는 가상 세계의 노이즈가 실제 세계의 먼지와 마찰과 배터리 전압으로 바뀐다는 것뿐이다.

소프트웨어 중심 학습자는 이 흐름을 먼저 배운다. 그래서 로봇을 사기 전에도 로봇을 이해할 수 있고, 로봇을 산 뒤에는 더 빠르게 성장할 수 있다.

참고

1ROS on DDS

2ROS 2 Quality of Service settings

3Setting up security with sros2

4Use ROS 2 to interact with Gazebo

5Setting up a robot simulation with Gazebo and ROS 2

6TurtleBot in ROS 2

7slam_toolbox documentation

8ROS 2 Navigation tutorial

책 목록

처음 만나는 로봇공학: 로봇은 어떻게 보고, 생각하고, 움직이는가

전체 목차 보기

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

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

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

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

참여하기