Ubuntu ROS 실습(6)

zeusqoi·2026년 9월 22일

개인공부

목록 보기
25/42

지난 실습에서는 ROS 2 Talker/Listener 예제를 Snap으로 패키징하고, 생성된 Snap을 설치하여 실행하는 과정까지 진행했다.

이번에는 Canonical Robotics의 로봇 소프트웨어 Snap 패키징 실습을 이어서 진행했다.

이번 실습에서는 기존 Ubuntu 22.04 환경이 아닌 Ubuntu 20.04 + ROS 1 Noetic 환경이 필요하기 때문에 Multipass를 이용하여 별도의 Ubuntu 20.04 환경을 구성했다.

이후 TurtleBot3 Gazebo 실행을 확인하고, TurtleBot3c를 ROS 1 Noetic 기반 Snap으로 패키징하기 위한 Snapcraft 프로젝트를 구성했다.


1. Multipass로 Ubuntu 20.04 환경 구성

기존에 사용하던 Ubuntu 22.04 환경과 별도로 ROS 1 Noetic 실습 환경을 구성하기 위해 Multipass를 사용했다.

먼저 Multipass를 설치하고 버전을 확인했다.

sudo snap install multipass
multipass --version

이후 Ubuntu 20.04 기반의 Multipass 인스턴스를 생성했다.

처음에는 기본 디스크 용량으로 인스턴스를 생성했지만, 이후 패키지 설치 및 Snap 빌드에 필요한 공간이 부족한 문제가 발생했다.

따라서 기존 인스턴스를 삭제하고 디스크와 메모리, CPU를 충분히 할당하여 다시 생성했다.

multipass launch 20.04 --name ros1-snap --disk 30G --memory 8G --cpus 4

생성된 인스턴스에 접속했다.

multipass shell ros1-snap

이후 Ubuntu 버전을 확인하여 Ubuntu 20.04 환경이 정상적으로 구성된 것을 확인했다.


2. 디스크 용량 확인

Ubuntu 20.04 환경이 정상적으로 구성된 후 디스크 용량을 확인했다.

df -h /

결과:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        29G  1.7G   28G   6%

약 29GB의 디스크가 할당되어 있었고, 약 28GB의 여유 공간이 남아 있는 것을 확인했다.


3. ROS 1 Noetic 저장소 등록

Ubuntu 20.04 환경에서 ROS 1 Noetic을 설치하기 위해 ROS 패키지 저장소를 등록했다.

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu focal main" > /etc/apt/sources.list.d/ros1-latest.list'

이후 ROS 저장소에서 필요한 패키지를 받을 수 있도록 curl을 설치했다.

sudo apt install -y curl

ROS 저장소 인증키도 등록했다.

sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key F42ED6FBAB17C654

정상적으로 인증키가 등록된 후 패키지 목록을 갱신했다.

sudo apt update

apt update가 정상적으로 완료되는 것을 확인했다.


4. ROS 1 Noetic 설치

이제 ROS 1 Noetic의 데스크톱 전체 패키지를 설치했다.

sudo apt install -y ros-noetic-desktop-full

설치가 완료된 후 ROS Noetic 환경을 사용할 수 있도록 환경 설정을 적용했다.

source /opt/ros/noetic/setup.bash

이를 통해 Ubuntu 20.04 환경에 ROS 1 Noetic을 구성했다.


5. TurtleBot3 Gazebo 실행 확인

ROS Noetic 환경이 정상적으로 구성되었는지 확인하기 위해 TurtleBot3 Gazebo 시뮬레이션을 실행했다.

먼저 TurtleBot3 Gazebo 패키지를 설치했다.

sudo apt install -y ros-noetic-turtlebot3-gazebo

TurtleBot3 모델을 burger로 설정했다.

export TURTLEBOT3_MODEL=burger

이후 TurtleBot3 Gazebo 월드를 실행했다.

roslaunch turtlebot3_gazebo turtlebot3_world.launch

처음 multipass shell 환경에서 실행했을 때는 Gazebo GUI를 표시하는 과정에서 문제가 발생했다.

Multipass 환경에서 GUI를 직접 표시하기 어려웠기 때문에 X11 forwarding을 이용하여 Ubuntu 20.04 환경에 접속한 후 다시 Gazebo를 실행했다.

호스트에서 Multipass 인스턴스의 IP를 확인했다.

multipass list

이후 Multipass에서 사용하는 SSH 키를 이용하여 X11 forwarding으로 접속했다.

sudo ssh -X -i /var/snap/multipass/common/data/multipassd/ssh-keys/id_rsa ubuntu@10.117.200.96

접속 후 다시 ROS 환경을 불러오고 TurtleBot3 모델을 설정했다.

source /opt/ros/noetic/setup.bash
export TURTLEBOT3_MODEL=burger

그리고 Gazebo를 실행했다.

roslaunch turtlebot3_gazebo turtlebot3_world.launch

정상적으로 Gazebo가 실행되었으며, TurtleBot3 Burger 모델과 시뮬레이션 환경이 화면에 표시되는 것을 확인했다.

이를 통해 Ubuntu 20.04 + ROS 1 Noetic 환경에서 TurtleBot3 Gazebo 실행에 필요한 기본 환경이 정상적으로 구성되었음을 확인했다.


6. TurtleBot3 Snap 프로젝트 생성

ROS 1 Noetic 환경을 구성하고 TurtleBot3 Gazebo 실행까지 확인한 후, Canonical Robotics의 복잡한 로봇 소프트웨어 Snap 패키징 실습을 진행했다.

먼저 TurtleBot3 관련 Snap 프로젝트 디렉터리를 생성하고 snapcraft init을 이용하여 Snap 프로젝트의 기본 구조를 생성했다.

mkdir ~/turtlebot3c_snap
cd ~/turtlebot3c_snap
snapcraft init

생성된 프로젝트 구조를 확인했다.

ls -la

프로젝트에는 snap 디렉터리가 생성되었으며, 이후 snap/snapcraft.yaml을 수정하여 TurtleBot3c Snap 패키징 환경을 구성했다.


7. snapcraft.yaml 구성

TurtleBot3c를 ROS 1 Noetic 기반 Snap으로 패키징하기 위해 snap/snapcraft.yaml을 작성했다.

name: turtlebot3c
base: core20
version: '1'
summary: Turtlebot3c core snap
description: |
  This snap automatically spawns a roscore and the core components for the
  Turtlebot3.

confinement: strict

parts:
  workspace:
    plugin: catkin
    source: .
    build-packages: [python3-vcstool, git]
    stage-packages:
      - ros-noetic-rosbash
      - ros-noetic-roslaunch
    override-pull: |
      snapcraftctl pull
      vcs import --input turtlebot3c.rosinstall

apps:
  core:
    daemon: simple
    environment:
      TURTLEBOT3_MODEL: waffle_pi
    command: opt/ros/noetic/bin/roslaunch turtlebot3c_bringup turtlebot3c_bringup.launch
    plugs: [network, network-bind, raw-usb]
    extensions: [ros1-noetic]

core20을 기반으로 ROS 1 Noetic 환경을 사용하도록 구성했으며, catkin 플러그인을 이용하여 ROS 패키지를 빌드하도록 설정했다.

또한 ros1-noetic extension을 사용하여 ROS 1 Noetic 기반 실행 환경을 구성했다.


8. TurtleBot3 저장소 구성

TurtleBot3 관련 소스 코드를 가져오기 위해 turtlebot3.rosinstall 파일을 작성했다.

- git: {local-name: turtlebot3, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3.git', version: noetic}
- git: {local-name: turtlebot3_msgs, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git', version: noetic}
- git: {local-name: turtlebot3c, uri: 'https://github.com/ubuntu-robotics/turtlebot3c.git', version: noetic-devel}

vcs import를 이용하여 위 저장소들을 Snap 빌드 환경으로 가져오도록 구성했다.


9. Snapcraft 버전 및 빌드 환경 구성

TurtleBot3c Snap을 빌드하기 위해 Snapcraft 환경을 구성했다.

처음 설치된 Snapcraft 버전은 9.1.1이었지만, 이번 실습에서 사용하는 ROS 1 Noetic 기반 core20 환경에 맞추기 위해 Snapcraft 8.x 버전으로 변경했다.

sudo snap refresh snapcraft --channel=8.x/stable

버전을 다시 확인했다.

snapcraft --version

결과:

8.14.5

이후 Snapcraft 빌드에 사용할 LXD 환경도 확인했다.

lxd --version

LXD를 초기화했다.

sudo lxd init

초기화 과정에서 저장소와 네트워크 브리지 등을 설정한 후 LXD 환경을 구성했다.


10. Multipass 빌드 환경에서 LXD로 전환

처음 Snapcraft 빌드를 실행했을 때는 Snapcraft의 기본 빌드 환경으로 Multipass가 사용되었다.

snapcraft pack --debug

이 과정에서 Snapcraft가 별도의 Multipass 빌드 환경을 실행하려고 했지만 VM 실행 과정에서 문제가 발생했다.

Launching a VM.
launch failed: snapcraft-turtlebot3c: timed out waiting for response

이에 따라 기존 Multipass 기반 빌드 환경 대신 LXD를 Snapcraft 빌드 provider로 사용하기로 했다.

Snapcraft의 provider 설정을 LXD로 변경했다.

sudo snap set snapcraft provider=lxd

설정이 정상적으로 변경되었는지 확인했다.

sudo snap get snapcraft provider

결과:

lxd

이를 통해 이후 Snapcraft 빌드는 Multipass가 아닌 LXD 환경에서 진행하도록 변경했다.


11. ROS Noetic 패키지 확인

Snapcraft 빌드 과정에서 필요한 ros-noetic-catkin 패키지가 현재 ROS 1 Noetic 환경에서 정상적으로 제공되는지 확인했다.

apt-cache policy ros-noetic-catkin

결과:

ros-noetic-catkin:
  Installed: 0.8.12-1focal.20250426.001935
  Candidate: 0.8.12-1focal.20250426.001935

Ubuntu 20.04 환경의 ROS 저장소에서 ros-noetic-catkin 패키지를 정상적으로 확인할 수 있었다.


12. TurtleBot3 저장소 브랜치 확인

Snap 빌드 과정에서 TurtleBot3 저장소의 noetic-devel 브랜치를 찾지 못하는 오류가 발생했다.

Could not checkout ref 'noetic-devel':
fatal: invalid reference: noetic-devel

이에 따라 각 Git 저장소에서 현재 제공되는 브랜치를 직접 확인했다.

turtlebot3

git ls-remote --heads https://github.com/ROBOTIS-GIT/turtlebot3.git | grep -E 'noetic|devel'

확인 결과:

refs/heads/noetic

turtlebot3_msgs

git ls-remote --heads https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git | grep -E 'noetic|devel'

확인 결과:

refs/heads/noetic

turtlebot3c

git ls-remote --heads https://github.com/ubuntu-robotics/turtlebot3c.git | grep -E 'noetic|devel'

확인 결과:

refs/heads/melodic-devel
refs/heads/noetic-devel

따라서 현재 확인된 저장소 브랜치에 맞춰 다음과 같이 설정했다.

  • turtlebot3 → noetic
  • turtlebot3_msgs → noetic
  • turtlebot3c → noetic-devel

이에 따라 turtlebot3.rosinstall 파일을 수정했다.

- git: {local-name: turtlebot3, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3.git', version: noetic}
- git: {local-name: turtlebot3_msgs, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git', version: noetic}
- git: {local-name: turtlebot3c, uri: 'https://github.com/ubuntu-robotics/turtlebot3c.git', version: noetic-devel}

13. Snapcraft LXD 빌드 과정 확인

저장소 브랜치 설정을 수정한 후 Snapcraft 빌드를 다시 진행했다.

먼저 기존 빌드 내용을 정리했다.

snapcraft clean

이후 다시 Snap 패키징을 진행했다.

snapcraft pack --debug

빌드 과정에서 vcs import가 실행되며 TurtleBot3 관련 Git 저장소를 가져오는 과정을 확인했다.

vcs import --input turtlebot3c.rosinstall

빌드 환경에서 저장소를 가져오는 과정과 turtlebot3c.rosinstall 설정이 실제 빌드에 사용되는 것을 확인했다.

이 과정에서 빌드 환경에서 이전 turtlebot3.rosinstall 설정이 사용되는 현상이 확인되어 호스트 프로젝트의 파일 내용을 다시 확인했다.

확인 결과 이전 설정이 남아 있었기 때문에 호스트 프로젝트의 turtlebot3.rosinstall을 다시 수정했다.

현재 최종 설정은 다음과 같다.

- git: {local-name: turtlebot3, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3.git', version: noetic}
- git: {local-name: turtlebot3_msgs, uri: 'https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git', version: noetic}
- git: {local-name: turtlebot3c, uri: 'https://github.com/ubuntu-robotics/turtlebot3c.git', version: noetic-devel}

저장소 브랜치 설정을 수정한 상태이며, 아직 최종 Snap 패키지 빌드 성공까지 완료한 상태는 아니다.


14. TurtleBot3 Teleop 실행

TurtleBot3 Core 서비스가 실행된 상태에서 Teleop을 실행해 로봇을 키보드로 제어하는 실습을 진행했다.

먼저 다음 명령어를 실행했다.

snap run turtlebot3.teleop

실행하면 다음과 같이 TurtleBot3를 조작할 수 있는 키와 속도 조절 방법이 출력된다.

w, a, s, d, x 키를 이용하여 선속도와 각속도를 조절할 수 있으며, space 또는 s 키를 이용해 강제로 정지할 수 있다.

Teleop 실행과 함께 별도의 터미널에서 ROS 노드와 토픽의 상태를 확인했다.


15. Joint State 확인

TurtleBot3의 관절 상태 정보를 확인하기 위해 /joint_states 토픽을 확인했다.

rostopic echo /joint_states

실행 결과 header, name, position, velocity, effort 등의 정보가 출력되는 것을 확인했다.

header:
  seq: ...
  stamp:
    secs: ...
    nsecs: ...
  frame_id: ''
name: [...]
position: [...]
velocity: [...]
effort: [...]

Teleop 실행 상태에서 이러한 Joint State 데이터가 출력되는 것을 확인함으로써 로봇의 관절 상태 정보가 ROS 토픽을 통해 전달되는 것을 확인했다.


16. SLAM 실행

다음으로 TurtleBot3의 SLAM 실습을 진행했다.

SLAM 실행 시 turtlebot3_slam_gmapping 노드가 실행되며, gmapping을 이용해 LaserScan과 Odometry 정보를 기반으로 지도를 생성하는 구조이다.

실행 과정에서 다음과 같은 SLAM 관련 파라미터가 설정된 것을 확인했다.

/turtlebot3_slam_gmapping/map_frame: map
/turtlebot3_slam_gmapping/odom_frame: odom
/turtlebot3_slam_gmapping/base_frame: base_footprint
/turtlebot3_slam_gmapping/particles: 100
/turtlebot3_slam_gmapping/maxUrange: 3.0
/turtlebot3_slam_gmapping/xmin: -10.0
/turtlebot3_slam_gmapping/xmax: 10.0
/turtlebot3_slam_gmapping/ymin: -10.0
/turtlebot3_slam_gmapping/ymax: 10.0

특히 SLAM에서 사용되는 주요 Frame이 map, odom, base_footprint로 설정되어 있는 것을 확인할 수 있었다.

또한 SLAM 노드가 다음과 같이 실행되었다.

NODES
  /
    turtlebot3_slam_gmapping (gmapping/slam_gmapping)

실행 후 ROS 토픽을 확인했다.

rostopic list

확인 결과 다음과 같은 토픽들이 생성되어 있었다.

/joint_states
/map
/map_metadata
/rosout
/rosout_agg
/scan
/tf
/tf_static
/turtlebot3_slam_gmapping/entropy

이를 통해 SLAM 실행 시 지도 정보인 /map, /map_metadata와 센서 데이터에 해당하는 /scan, 좌표계 정보를 전달하는 /tf, /tf_static 등의 토픽이 사용되는 것을 확인했다.


17. Navigation 실행

이후 생성된 지도와 센서 데이터를 이용하여 Navigation을 실행했다.

Navigation 실행 과정에서 amcl, map_server, move_base 노드가 실행되는 것을 확인했다.

NODES
  /
    amcl (amcl/amcl)
    map_server (map_server/map_server)
    move_base (move_base/move_base)

또한 move_base의 Local Costmap 설정에서 /scan 토픽을 LaserScan 데이터로 사용하도록 설정되어 있었다.

/move_base/local_costmap/observation_sources: scan
/move_base/local_costmap/scan/data_type: LaserScan
/move_base/local_costmap/scan/marking: True
/move_base/local_costmap/scan/sensor_frame: base_scan
/move_base/local_costmap/scan/topic: scan

즉, Navigation에서는 /scan으로 전달되는 LaserScan 데이터를 이용하여 주변 장애물을 인식하고 Local Costmap을 구성하도록 설정되어 있었다.

하지만 Navigation 실행 후 다음과 같은 경고가 반복적으로 발생했다.

Timed out waiting for transform from base_footprint
to map to become available

No laser scan received
(and thus no pose updates have been published) ...

즉, Navigation 노드는 정상적으로 실행되었지만 base_footprint → map Transform을 기다리는 과정에서 timeout이 발생했고, /scan에서도 LaserScan 데이터가 전달되지 않아 위치 추정에 필요한 정보가 부족한 상태였다.

이후 별도로 /scan 토픽의 Publisher와 Subscriber를 확인하면서 문제의 원인을 확인했다.

현재 실습 환경에서는 실제 TurtleBot3 하드웨어와 LiDAR를 연결한 상태가 아니기 때문에 Navigation에 필요한 실제 센서 데이터가 전달되지 않는 상황이었다.

따라서 다음 과정에서는 테스트 환경에서 필요한 /scan 데이터를 직접 발행하여 Navigation에서 해당 데이터를 정상적으로 수신하는지 확인하고, map, odom, base_footprint 사이의 TF 관계를 추가로 확인했다.


18. 복잡한 로봇 소프트웨어를 Snap으로 패키징하는 구조

이번 실습에서 진행한 TurtleBot3 Snap 패키징은 단순히 하나의 ROS 노드를 Snap으로 만드는 것보다 조금 더 복잡한 구조를 가지고 있다.

Canonical Robotics의 복잡한 로봇 소프트웨어를 스냅으로 패키징하기 문서에서도 로봇 소프트웨어를 Snap으로 패키징할 때 ROS 패키지, 실행 환경, 하드웨어 인터페이스 등을 하나의 실행 가능한 형태로 구성하는 과정을 다루고 있다.

이번 실습에서 만든 turtlebot3c Snap도 이러한 구조를 이해하기 위한 과정이었다.

기존 ROS 환경에서는 여러 패키지가 시스템에 설치되어 있고 source /opt/ros/noetic/setup.bash와 같은 방식으로 환경을 구성한 뒤 각각의 노드를 실행한다.

반면 Snap으로 패키징하면 필요한 ROS 패키지와 실행 파일, 설정 등을 Snap 내부에 포함하고, Snap의 실행 환경에서 ROS 시스템을 구성할 수 있다.

즉, 기존 ROS 실행 방식과 Snap 실행 방식은 다음과 같이 생각할 수 있다.

기존 ROS 환경

Ubuntu
 └── ROS Noetic
      ├── TurtleBot3 패키지
      ├── ROS Master
      ├── 각종 ROS Node
      └── 설정 및 의존 패키지


Snap 기반 환경

Ubuntu
 └── turtlebot3c Snap
      ├── ROS Noetic 실행 환경
      ├── TurtleBot3 패키지
      ├── 실행 설정
      └── Snap에서 필요한 권한 및 연결

이번 프로젝트에서는 turtlebot3c.core Snap이 TurtleBot3의 핵심 ROS 구성 요소를 실행하도록 구성했고, 이후 turtlebot3c.teleop을 별도로 실행하여 키보드 제어를 확인했다.

이러한 방식으로 ROS 패키지와 실행 환경을 Snap 단위로 묶으면 특정 Ubuntu 환경에 의존하지 않고 동일한 소프트웨어 구성을 배포하기 위한 기반을 만들 수 있다.

다만 Snap은 일반적인 Ubuntu 프로그램과 달리 confinement 환경에서 실행되기 때문에 파일 경로, 네트워크, USB 등의 접근 권한을 별도로 고려해야 한다.

이번 실습에서도 이러한 Snap 환경의 차이 때문에 ROS 실행 과정에서 여러 문제가 발생했다.


19. TurtleBot3 Core와 Teleop의 관계

앞에서 실행한 명령어는 다음과 같다.

snap run turtlebot3c.core

이 명령을 실행하면 snapcraft.yaml에서 설정한 core 애플리케이션이 실행된다.

이번 Snap의 core 애플리케이션은 다음과 같이 구성되어 있었다.

apps:
  core:
    daemon: simple
    environment:
      TURTLEBOT3_MODEL: waffle_pi
    command: opt/ros/noetic/bin/roslaunch turtlebot3c_bringup turtlebot3c_bringup.launch

즉, core는 TurtleBot3의 핵심 ROS 구성 요소를 실행하는 역할을 한다.

이후 별도의 터미널에서

snap run turtlebot3c.teleop

을 실행하면 Teleop 노드가 실행되고 키보드를 이용해 TurtleBot3를 제어할 수 있다.

             turtlebot3c.core
                    │
                    ├── ROS Master
                    ├── Robot State Publisher
                    ├── Navigation 관련 Node
                    └── 기타 TurtleBot3 구성 요소
                              │
                              │ ROS Topic / TF
                              ▼
                    turtlebot3c.teleop
                              │
                         키보드 입력
                              │
                              ▼
                         로봇 제어

따라서 Teleop 화면이 정상적으로 실행된다는 것과 Navigation이 정상적으로 동작한다는 것은 서로 다른 의미이다.

Teleop은 주로 /cmd_vel 계열의 속도 명령을 전달하는 역할이고, Navigation은 지도, LaserScan, Odometry, TF 등의 여러 데이터가 함께 필요하기 때문이다.


20. Navigation에 필요한 ROS 데이터 구조 확인

Navigation이 왜 동작하지 않는지 확인하기 위해 관련된 ROS Topic과 Node를 하나씩 확인했다.

먼저 Navigation을 구성하는 주요 Node를 확인했다.

rosnode list

결과:

/amcl
/map_server
/move_base
/robot_state_publisher
/rosout
/static_transform_publisher_...

여기서 각 Node의 역할을 정리하면 다음과 같다.

Node역할
map_server저장된 지도(/map) 제공
amclLaserScan과 Odometry를 이용해 로봇의 위치 추정
move_base목표 위치까지 이동하기 위한 경로 및 속도 명령 생성
robot_state_publisher로봇의 관절 상태를 바탕으로 TF 생성

Navigation은 하나의 Node만 실행한다고 동작하는 것이 아니라 여러 데이터가 연결되어야 한다.

전체적인 구조는 다음과 같이 이해할 수 있다.

             /map
               │
               ▼
          ┌─────────┐
          │  AMCL   │
          └────┬────┘
               │
            map → odom
               │
               ▼
             odom
               │
       Odometry 정보
               │
               ▼
        base_footprint
               │
               ▼
           base_scan
               │
               ▼
             /scan
               │
               ▼
          ┌──────────┐
          │ move_base│
          └──────────┘

즉, /map과 /scan만 존재한다고 Navigation이 바로 동작하는 것은 아니다.

특히 Navigation에서는 로봇의 위치를 좌표계 사이에서 연결해주는 TF 관계가 중요하다.


21. /scan 토픽 확인

앞에서 Navigation 실행 시 다음과 같은 경고가 발생했다.

No laser scan received
(and thus no pose updates have been published)

따라서 먼저 /scan 토픽을 확인했다.

rostopic info /scan

처음 확인했을 때는 다음과 같이 Publisher가 존재하지 않았다.

Type: sensor_msgs/LaserScan

Publishers: None

Subscribers:
 * /amcl
 * /move_base

즉, amcl과 move_base는 /scan을 구독하고 있지만 실제 LaserScan 데이터를 발행하는 Node가 없는 상태였다.

이를 조금 더 쉽게 표현하면 다음과 같다.

/scan
 │
 ├── AMCL       ← 데이터를 기다림
 │
 └── move_base  ← 데이터를 기다림

하지만 Publisher 없음

이 상태에서는 Navigation이 LaserScan 데이터를 받을 수 없기 때문에 정상적인 위치 추정과 장애물 인식이 어렵다.


22. 테스트용 LaserScan 데이터 생성

실제 TurtleBot3 하드웨어와 LiDAR가 연결되어 있지 않은 환경이므로 테스트를 위해 /scan 데이터를 직접 발행하는 방법을 사용했다.

간단한 Python ROS Node를 만들어 sensor_msgs/LaserScan 메시지를 발행했다.

#!/usr/bin/env python3

import rospy
from sensor_msgs.msg import LaserScan

rospy.init_node("fake_laser")

pub = rospy.Publisher("/scan", LaserScan, queue_size=10)

rate = rospy.Rate(10)

msg = LaserScan()
msg.header.frame_id = "base_scan"

msg.angle_min = -3.14159
msg.angle_max = 3.14159
msg.angle_increment = 0.0174533

msg.time_increment = 0.0
msg.scan_time = 0.1

msg.range_min = 0.12
msg.range_max = 3.5

msg.ranges = [2.0] * 361
msg.intensities = [0.0] * 361

while not rospy.is_shutdown():
    msg.header.stamp = rospy.Time.now()
    pub.publish(msg)
    rate.sleep()

이 Node는 실제 LiDAR가 측정한 값이 아니라 테스트를 위해 모든 방향에 약 2m 거리의 장애물이 있다고 가정한 LaserScan 데이터를 계속 발행한다.

실행 후 다시 /scan을 확인했다.

rostopic info /scan

결과:

Type: sensor_msgs/LaserScan

Publishers:
 * /fake_laser

Subscribers:
 * /amcl
 * /move_base

이제 /scan의 Publisher가 정상적으로 연결된 것을 확인할 수 있었다.


23. /scan 데이터가 실제로 들어오는지 확인

Publisher가 존재하는 것만으로는 충분하지 않기 때문에 실제 메시지가 들어오는지도 확인했다.

rostopic hz /scan

결과 약 10Hz로 데이터가 들어오는 것을 확인했다.

average rate: 10.00

따라서 기존의

No laser scan received

문제에 대해서는 테스트용 LaserScan Publisher를 추가하여 /scan 데이터 자체는 정상적으로 전달되는 상태까지 확인했다.

즉 현재 상태는 다음과 같다.

/fake_laser
     │
     │ LaserScan
     ▼
   /scan
     │
     ├──────────────┐
     ▼              ▼
   /amcl        /move_base

여기까지 확인하면서 /scan 자체는 정상적으로 연결되었다.


24. TF 구조 확인

다음으로 Navigation에서 중요한 TF 구조를 확인했다.

rostopic info /tf

결과를 보면 /tf는 다음 Node들에서 발행되고 있었다.

Publishers:
 * /robot_state_publisher
 * /static_transform_publisher_...
 * /amcl
 * /static_transform_publisher_...

Subscribers:
 * /move_base
 * /amcl
 * /tf_echo_...

즉 /tf Topic 자체는 정상적으로 존재하고 있었고, move_base와 amcl도 /tf를 구독하고 있었다.


25. AMCL의 Frame 설정 확인

AMCL에서 어떤 Frame을 사용하는지도 확인했다.

rosparam get /amcl/odom_frame_id

결과:

odom

그리고

rosparam get /amcl/base_frame_id

결과:

base_footprint

따라서 AMCL은 다음과 같은 좌표계 구조를 기대하고 있었다.

map
 │
 │ AMCL이 추정
 ▼
odom
 │
 │ Odometry
 ▼
base_footprint

그런데 현재 TF Frame 목록을 확인했을 때 base_footprint, base_link, base_scan 등은 존재하지만 odom과 map Frame이 정상적으로 연결되어 있지 않은 문제가 확인되었다.

실제로 TF 확인 과정에서 다음과 같은 메시지가 반복적으로 나타났다.

Exception thrown: "odom" passed to lookupTransform
argument target_frame does not exist.

또한 Navigation 실행 과정에서는

Timed out waiting for transform from base_footprint
to map to become available

라는 경고가 발생했다.


26. robot_state_publisher 확인

robot_state_publisher가 어떤 데이터를 사용하는지도 확인했다.

rosnode info /robot_state_publisher

확인 결과:

Subscriptions:
 * /joint_states

Publications:
 * /tf
 * /tf_static

즉 robot_state_publisher는 /joint_states를 입력으로 받아 로봇의 TF 정보를 생성하는 구조이다.

이를 간단하게 표현하면 다음과 같다.

/joint_states
      │
      ▼
robot_state_publisher
      │
      ├── /tf
      └── /tf_static

그런데 /joint_states를 확인했을 때는 다음과 같이 Publisher가 존재하지 않았다.

rostopic info /joint_states

결과:

Type: sensor_msgs/JointState

Publishers: None

Subscribers:
 * /robot_state_publisher

그리고

rostopic hz /joint_states

를 실행했을 때도

no new messages

가 계속 출력되었다.

즉 현재 환경에서는 robot_state_publisher가 /joint_states를 기다리고 있지만 해당 데이터를 발행하는 Node가 없는 상태였다.


27. Navigation이 그대로 동작하지 않는 이유

지금까지 확인한 내용을 종합하면 Navigation이 실행되지 않는 이유를 조금 더 명확하게 정리할 수 있다.

처음에는 /scan 데이터가 없었다.

/scan
Publisher: None

따라서 테스트용 fake_laser를 만들어 /scan 데이터를 발행했고, 약 10Hz로 정상적으로 전달되는 것까지 확인했다.

하지만 /scan 문제를 해결한 이후에도 Navigation이 바로 정상 동작하지 않았다.

그 이유는 Navigation이 /scan 하나만 필요한 것이 아니기 때문이다.

현재 확인된 문제를 정리하면 다음과 같다.

① /scan
   └── fake_laser를 통해 데이터 발행 확인
       → 해결/확인 완료

② /joint_states
   └── Publisher 없음
       → 데이터 없음

③ odom TF
   └── 정상적으로 존재하지 않음
       → AMCL이 필요로 하는 좌표계 연결 부족

④ map → odom → base_footprint
   └── TF 연결이 완전하지 않음
       → move_base에서 transform timeout 발생

따라서 현재 상황은 단순히 "Navigation Node가 실행되지 않는다"가 아니라, Navigation에 필요한 Node들은 실행되었지만, Node 사이를 연결해주는 센서 데이터와 TF 데이터가 완전하게 구성되지 않은 상태라고 보는 것이 더 정확하다.


28. ROS Navigation 구조를 다시 정리

이번 실습을 통해 Navigation에서 각각의 데이터가 어떤 역할을 하는지 조금 더 명확하게 확인할 수 있었다.

/map

map_server가 지도 데이터를 제공한다.

map_server
    │
    ▼
  /map
    │
    ▼
 Navigation

/scan

LiDAR와 같은 거리 센서에서 주변 장애물까지의 거리를 전달한다.

이번 실습에서는 실제 LiDAR 대신 fake_laser Node를 사용했다.

fake_laser
    │
    ▼
  /scan
    │
    ├── AMCL
    └── move_base

/joint_states

로봇 관절의 상태를 전달하고 robot_state_publisher가 이를 이용하여 TF를 구성하는 데 사용된다.

/joint_states
      │
      ▼
robot_state_publisher
      │
      ▼
     /tf

TF

ROS에서 각각의 좌표계가 어디에 위치하는지를 연결해주는 정보이다.

Navigation에서는 특히 다음 관계가 중요하다.

map
 │
 ▼
odom
 │
 ▼
base_footprint
 │
 ▼
base_link
 │
 ▼
base_scan

이 연결이 끊어지면 move_base가 로봇의 현재 위치를 제대로 계산할 수 없기 때문에 Navigation이 정상적으로 동작하지 않는다.


29. Snap 환경에서 발생한 추가 문제

이번 실습에서는 ROS 자체의 문제뿐만 아니라 Snap 환경 때문에 발생하는 문제도 확인했다.

ROS 명령을 실행하는 과정에서 다음과 같은 경고가 발생하기도 했다.

[rospack] Unable to create temporary cache file

또한 rosrun tf view_frames를 실행했을 때는 다음과 같은 Python 라이브러리 오류가 발생했다.

ImportError: libblas.so.3:
cannot open shared object file:
No such file or directory

즉 Snap 내부에 포함된 Python/Numpy 환경에서 필요한 시스템 라이브러리까지 모두 정상적으로 연결되지 않은 경우 ROS 도구가 실행되지 않을 수 있다는 것도 확인했다.

특히 view_frames는 Python 기반의 ROS TF 도구이기 때문에 단순히 ROS Node가 실행되고 있다는 것만으로 모든 ROS 명령어가 정상적으로 실행되는 것은 아니었다.


30. ROS_HOME 경로 문제

Snap 환경에서는 ROS의 기본 설정 및 캐시 경로도 일반적인 Ubuntu 환경과 차이가 있었다.

처음에는 다음과 같이 설정했다.

export ROS_HOME="$HOME/snap/turtlebot3c/common/.ros"

하지만 Snap 실행 환경에서 경로가 중복되어 다음과 같은 경고가 발생했다.

/home/ubuntu/snap/turtlebot3c/x1/snap/turtlebot3c/common/.ros

이후 Snap에서 제공하는 사용자 공용 경로를 이용하여 다음과 같이 설정했다.

export ROS_HOME="$SNAP_USER_COMMON/.ros"

이후 ROS 명령을 실행하면서 Snap 환경에서의 ROS Home 경로 문제를 피할 수 있었다.

이 과정에서 일반적인 Ubuntu 환경에서 실행할 때와 Snap 내부에서 실행할 때의 환경 변수와 파일 경로가 다를 수 있다는 점을 확인했다.


마무리

이번 실습에서는 Ubuntu 20.04 환경에 ROS 1 Noetic을 구성하고, TurtleBot3 Gazebo 실행을 확인한 후 TurtleBot3c를 Snap으로 패키징하는 과정을 진행했다.

특히 단순한 ROS Node 실행을 넘어 snapcraft.yaml, catkin plugin, ros1-noetic extension, turtlebot3.rosinstall, LXD 기반 Snapcraft 빌드 환경 등을 구성하면서 ROS 소프트웨어를 Snap 형태로 패키징하는 전체적인 구조를 확인할 수 있었다.

또한 TurtleBot3 Core와 Teleop을 실행하고 /joint_states, /scan, /tf 등의 ROS Topic을 직접 확인하면서 ROS Node들이 Topic과 TF를 통해 서로 연결되는 구조도 확인했다.

Navigation 실습에서는 처음에 /scan Publisher가 존재하지 않아 No laser scan received 오류가 발생했고, 이를 해결하기 위해 테스트용 fake_laser Node를 작성하여 /scan 데이터를 직접 발행했다. 이후 /scan이 약 10Hz로 정상적으로 전달되는 것을 확인했다.

하지만 /scan만 추가한다고 Navigation이 바로 동작하는 것은 아니었다. AMCL, move_base, robot_state_publisher 등이 정상적으로 실행되고 있어도 map → odom → base_footprint로 이어지는 TF와 Odometry 데이터가 제대로 구성되어 있어야 한다는 것을 확인했다.

특히 이번 실습에서는 실제 TurtleBot3 하드웨어와 LiDAR, Odometry 환경이 없는 상태에서 Snap 내부의 ROS 구성 요소를 실행했기 때문에 실제 로봇에서 자동으로 제공되는 센서 및 Odometry 데이터가 부족했다.

따라서 이번 실습의 최종 결과는 TurtleBot3 ROS 1 환경을 Snap으로 구성하고 Core 및 Teleop 실행을 확인했으며, Navigation에 필요한 ROS Topic과 TF 구조를 직접 확인하고 발생한 문제의 원인을 단계적으로 분석한 것으로 정리할 수 있다.

현재까지의 테스트에서는 /scan 데이터는 fake_laser를 통해 정상적으로 생성되는 것을 확인했지만, joint_states 및 odom을 포함한 TF 구조가 완전히 구성되지 않아 Navigation이 정상적으로 동작하는 단계까지는 도달하지 못했다.

0개의 댓글