AEGLOSLAB Arduino UNO Q 완전 가이드

제7부 미들웨어·데이터 계층

7.1 ROS2 Jazzy 설치하기 (커뮤니티 방법: 소스 빌드·Docker)

⚠️ 이 장 전체는 "커뮤니티 방법(비공식)"이다. Arduino나 ROS 진영 어느 쪽도 UNO Q용 ROS 2 패키지를 공식 배포하지 않는다. 아래 절차는 커뮤니티가 포럼·GitHub에 공개한 실제 시도를 확인해 정리한 것이며, 본인 환경에서 재현되지 않을 수 있다.

7.1.1 공식 지원과 현실 사이 — 왜 "비공식"인가

ROS 2 Jazzy Jalisco는 REP 2000(ROS 배포판별 지원 플랫폼 규약)에서 Tier 1 플랫폼을 Ubuntu 24.04 Noble(amd64/arm64)과 Windows 10으로 한정한다. packages.ros.org의 공식 apt 저장소도 이 Tier 1 플랫폼용 바이너리만 배포한다 — 즉 apt install ros-jazzy-* 계열 패키지는 Ubuntu Noble에서만 그대로 동작하도록 빌드되어 있다.

UNO Q의 MPU(QRB2210)는 Ubuntu가 아니라 Debian을 구동한다. 집필 시점(2026-08) 기준 공식 문서와 보드 자체가 보고하는 버전은 Debian Trixie(13.1), 커널 6.16.7이다(제조사 확인 필요 — Bookworm으로 표기된 구 자료도 있으니 실제 보드에서 cat /etc/os-release로 재확인할 것. 3.1 참조). 여기서 확인해야 할 두 가지 사실이 있다.

  1. Debian은 애초에 Jazzy의 1군 지원 대상이 아니다. REP 2000 표에는 배포판에 따라 Debian이 낮은 등급(소스 빌드 전제, 바이너리 없음)으로 잠깐 언급되는 시기가 있었을 뿐 안정적인 1군 대상인 적이 없다.
  2. 설사 Debian이 어느 등급으로 언급되더라도 그것은 Bookworm(12)에 관한 내용이다. Trixie(13)는 2025년 8월에 릴리스됐고 Jazzy는 2024년 5월에 릴리스됐다 — Jazzy가 세상에 나올 때 Trixie는 존재하지도 않았다. 즉 UNO Q가 실제로 탑재한 Debian 버전은 애초에 Jazzy 지원 표에 오를 수조차 없었다.

결론: packages.ros.org에 UNO Q용 ros-jazzy-* 패키지가 생길 일은 (적어도 현재 REP 2000 체계에서는) 없다고 보는 것이 맞다. 그래서 커뮤니티는 두 가지 우회 방법을 쓴다 — ① 소스에서 직접 빌드, ② Ubuntu 24.04 기반 Docker 컨테이너 안에서 실행하기. 뒤에서 다루듯 실제로 공개된 커뮤니티 결과물은 압도적으로 ②(Docker) 쪽이 많다 — Debian 호스트 위에서 순수 소스 빌드를 완주해 공개한 사례는 이 장을 쓰며 찾지 못했다(7.1.4 참조, 정직하게 표기).

7.1.2 두 경로 개관과 트레이드오프

두 방법 모두 결과적으로 "ROS 2 Jazzy 노드가 UNO Q의 MPU 위에서 실행된다"는 목표는 같지만 성격이 다르다.

항목① 소스 빌드(호스트 직접)② Docker 컨테이너
실행 환경Debian Trixie 위에 직접 컴파일·설치컨테이너 내부는 Ubuntu 24.04 — 사실상 "공식 타깃"을 그대로 재현
커뮤니티 검증 정도낮음 — 일반 원칙은 확립돼 있으나 UNO Q 전용 완주 사례 미확인높음 — GitHub 공개 저장소 2곳, 포럼 스레드 다수
빌드 시간·디스크매우 큼(수 시간, 수십 GB)상대적으로 작음(이미지 1GB 내외로도 가능)
RAM 요구큼 — 2GB 모델은 OOM 위험 높음상대적으로 완화되나 여전히 스왑 권장
GUI(RViz 등)이론상 가능하나 GPU 드라이버 문제로 비권장기본 이미지에서 완전히 제외하는 것이 커뮤니티 관행
유지보수호스트 오염(패키지 충돌) 위험컨테이너 삭제로 완전히 롤백 가능
적합한 경우ROS 2 내부를 수정해야 하거나 호스트에 상주시켜야 하는 경우대부분의 실험·프로토타이핑, 특히 처음 시도하는 경우

견해: 처음 UNO Q에서 ROS 2를 만져본다면 ②(Docker)를 먼저 권한다 — 실패해도 컨테이너만 지우면 되고, 커뮤니티가 이미 겪은 시행착오(디스크 부족, GUI 패키지 문제)를 Dockerfile에 반영해 공개해 두었기 때문이다. 소스 빌드는 "ROS 2 자체를 UNO Q 네이티브 환경에 상주시켜야 하는 특수한 이유"가 있을 때만 고려한다.

7.1.3 사전 준비 — 모델 선택, 저장공간, 스왑

  • 모델: 4GB/32GB 모델을 강력히 권장한다(1.1). 2GB 모델에서 ROS 2 관련 빌드를 시도하다 디스크·메모리가 고갈되는 사례가 커뮤니티에 반복적으로 보고된다 — 특히 mjs513의 저장소는 (ROS 2가 아닌 ArduinoCore-zephyr 소스 빌드 관련이지만) "2GB 모델에서는 절대 시도하지 말 것"이라고 굵게 경고한다. 같은 이유(디스크·RAM 부족)로 ROS 2 빌드에도 그대로 적용된다고 보는 것이 안전하다.
  • 디스크 확인: df -h / — eMMC 여유공간이 최소 8~10GB는 있어야 안전하다. 부족하면 5.4(저장공간 관리) 참조.
  • 스왑 확보: 커뮤니티가 공통으로 권장하는 완화책은 스왑 파일 확장이다.
# 현재 메모리·스왑 확인
free -h

# 4GB 스왑 파일 생성(디스크 여유가 있는 경우)
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 재부팅 후에도 유지
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

⚠️ eMMC는 SSD보다 쓰기 내구성이 낮다. 스왑을 상시 크게 켜두면 eMMC 수명에 영향을 줄 수 있다(5.4·5.5 참조). 빌드가 끝나면 sudo swapoff /swapfile로 끄거나 스왑 크기를 줄이는 것을 고려한다.

7.1.4 방법 ① 소스 빌드 — ros2.repos로 직접 컴파일

이 절차는 ROS 2 공식 문서가 비-Tier-1 플랫폼에 소스 빌드를 권하는 일반 원칙(ros2/ros2 저장소의 ros2.repos 매니페스트를 vcs로 임포트 → rosdep으로 의존성 설치 → colcon으로 빌드)을 UNO Q의 Debian Trixie 호스트에 그대로 적용한 것이다. UNO Q에서 이 경로를 처음부터 끝까지 완주해 공개한 커뮤니티 사례는 이 장을 조사하며 찾지 못했다 — 아래는 "원리상 되어야 하는" 절차이지 "누군가 검증해 공개한" 절차가 아니다. 시도한다면 각 단계에서 실패를 기록해 커뮤니티에 공유할 가치가 있다.

# 1) 빌드 도구 설치
sudo apt update
sudo apt install -y python3-pip python3-vcstool python3-colcon-common-extensions \
    python3-rosdep build-essential cmake git

# 2) 워크스페이스 생성 및 매니페스트 가져오기
mkdir -p ~/ros2_jazzy/src
cd ~/ros2_jazzy
wget https://raw.githubusercontent.com/ros2/ros2/jazzy/ros2.repos
vcs import src < ros2.repos

ros2.repos에는 RViz2·rqt 계열(ros-visualization)처럼 Qt·OpenGL에 의존하는 GUI 패키지들이 포함돼 있다. UNO Q의 Adreno 702 GPU는 Debian Trixie 위에서 데스크톱용 Mesa/Qt 그래픽 스택이 정식으로 검증돼 있지 않아 — 특히 App Lab이 자체 사용하는 GPU 드라이버 스택과 충돌할 소지가 있다 — 이 패키지들을 넣은 채 빌드를 시도하면 실패하거나 매우 오래 걸릴 가능성이 높다. 사전에 제외하는 편이 안전하다.

# 3) GUI 계열 저장소를 매니페스트에서 제거(수동 편집 또는 아래처럼 디렉터리 삭제)
rm -rf src/ros-visualization
rm -rf src/ros2/rviz
# rqt 계열도 필요 없다면 함께 제거
find src -maxdepth 1 -iname "*rqt*" -exec rm -rf {} +

# 4) 의존성 설치
sudo rosdep init   # 이미 초기화돼 있다면 생략
rosdep update
rosdep install --from-paths src --ignore-src -y \
    --skip-keys "rviz2 rviz_ogre_vendor rqt_gui"

# 5) 빌드 — 메모리가 부족하므로 병렬도를 낮춘다
colcon build --symlink-install --parallel-workers 1 \
    --cmake-args -DCMAKE_BUILD_TYPE=Release

--parallel-workers 1은 빌드 시간을 늘리는 대신 동시에 여러 컴파일 유닛이 메모리를 과도하게 점유하지 않게 한다. 그래도 OOM(Out-Of-Memory)으로 컴파일러가 강제 종료되는 경우, 7.1.3에서 만든 스왑이 완충 역할을 한다 — 다만 이는 ROS 2 공식 절차가 아니라 저사양 aarch64 보드에서 흔히 쓰는 일반적 완화책을 그대로 가져온 것이며 UNO Q 전용 검증 자료는 없다는 점을 다시 강조한다.

빌드가 끝나면:

source ~/ros2_jazzy/install/setup.bash
ros2 doctor   # 기본 동작 확인

7.1.5 방법 ② Docker — 커뮤니티가 공개한 두 저장소

Docker 방식의 핵심은 컨테이너 내부 rootfs를 Ubuntu 24.04로 삼아, UNO Q가 무엇을 호스트 OS로 쓰든 ROS 2 입장에서는 "정확히 공식 타깃 위에서" 동작한다는 점이다. 호스트가 Debian이라는 사실 자체를 우회해 버리므로, 소스 빌드보다 실제로 동작을 보고한 사례가 많다.

mjs513/Arduino-Uno-Q-and-ROS2-Implementation

github.com/mjs513/Arduino-Uno-Q-and-ROS2-Implementation(MIT 라이선스)는 Dockerfile·트러블슈팅 노트를 모아둔 저장소다. 핵심은 Docker Jazzy Base/Dockerfile.ros2_jazzy_base_nonano:

FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive

# 로케일·인증서·기본 도구
RUN apt update && apt install -y --no-install-recommends \
    locales curl gnupg2 lsb-release git ca-certificates \
    && update-ca-certificates \
    && locale-gen en_US en_US.UTF-8

# ROS 2 apt 저장소 등록
RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
    | gpg --dearmor -o /usr/share/keyrings/ros-archive-keyring.gpg
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
    http://packages.ros.org/ros2/ubuntu noble main" \
    > /etc/apt/sources.list.d/ros2.list

# ros-base(데스크톱 아님)만 설치
RUN apt update && apt install -y --no-install-recommends \
    ros-jazzy-ros-base python3-colcon-common-extensions \
    python3-rosdep python3-vcstool build-essential tmux

저장소의 README(Other 절)는 처음엔 ros-jazzy-desktop을 설치하려 했으나 이미지 빌드 중 디스크 공간이 부족해 실패했고, GUI 관련 x11-apps·mesa-utils·libgl1-mesa-glx 설치 줄 전체를 주석 처리하고 ros-jazzy-base로 낮춰 해결했다고 명시한다. 즉 실제로 보고된 원인은 "GPU 드라이버 충돌"이라기보다 컨테이너 이미지 용량 초과였다 — 다만 결과적으로 "GUI 스택은 UNO Q에서 제외하는 것이 최선"이라는 결론은 소스 빌드(7.1.4)의 GPU 이슈 우려와 같은 방향을 가리킨다. 두 원인 모두 실재하며, 어느 경로를 택하든 RViz·rqt 같은 Qt/OpenGL 도구는 보드에서 직접 실행하지 않는 것이 커뮤니티의 공통 관행이다.

# 이미지 빌드
docker build --no-cache -t ros2-jazzy .

# 워크스페이스를 호스트와 공유하며 컨테이너 실행
docker run -it -v ~/my_ws:/opt/ros_ws ros2-jazzy

# 컨테이너 안에서
cd /opt/ros_ws
colcon build --symlink-install

RViz2 등 시각화가 필요하면 저장소는 보드가 아니라 별도 PC(WSL2)에서 ros-jazzy-desktop을 설치해 원격으로 접속하는 방법을 안내한다(Installing and Running Rviz in WSL2/Install RViz2.md). PC와 UNO Q가 같은 네트워크에 있어야 하고, 두 쪽의 RMW 구현과 도메인을 맞춘다.

# UNO Q(컨테이너)와 PC(WSL2) 양쪽에서 동일하게
export ROS_DOMAIN_ID=0
export ROS_LOCALHOST_ONLY=0
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp   # 기본 DDS로 맞춰 통신

이 저장소에는 이 밖에도 Ros2_LED_node(RPC로 LED 제어하는 최소 예제), Ros2 Jazzy Slam Toolbox(초음파 소나 기반 SLAM 실험, 작업 중 표기)가 들어 있다 — 후자는 저장소 스스로 "work in process"라고 밝히므로 그대로 재현 가능하다고 단정하지 않는다.

miguelgonrod/ros_arduino_uno_Q

github.com/miguelgonrod/ros_arduino_uno_Q(BSD-3-Clause 라이선스)는 더 작고 구체적인 데모다 — ROS 2 노드가 /led_status(std_msgs/Bool) 토픽을 구독하다가, RPC를 통해 STM32 MCU의 set_led_state() 함수를 원격 호출해 보드 LED를 켜고 끄는 엔드투엔드 예제.

/led_status std_msgs/Bool RPC 호출 set_led_state(bool) ROS 2 노드 (컨테이너, Ubuntu 24.04) RPC 브리지 클라이언트 .ino 스케치 (Zephyr Arduino Core) STM32U585 GPIO → LED

실행 절차(README 원문 기준):

# 1) UNO Q 펌웨어를 최신으로 갱신 (Arduino 공식 절차)
# 2) Debian 13을 SBC 모드로 초기화 (Arduino 공식 절차)

# 3) ArduinoApps 폴더에 클론
cd ~/ArduinoApps
git clone https://github.com/miguelgonrod/ros_arduino_uno_Q.git
cd ros_arduino_uno_Q

# 4) .ino 펌웨어를 App Lab으로 업로드 + Docker 컨테이너 빌드·기동
cd python
chmod +x run.sh
./run.sh

# 5) tmux로 다중 창 운용
tmux

# 6) (첫 번째 pane) ROS 2 구독 노드 실행
ros2 run ros_led led

# 7) (두 번째 pane, Ctrl+B 후 C) LED 제어
ros2 topic pub /led_status std_msgs/Bool "data: true" --once
ros2 topic pub /led_status std_msgs/Bool "data: false" --once

run.sh가 .ino 파일을 App Lab으로 로드해 MCU에 플래시하는 부분과, Docker로 ROS 2 Jazzy 환경을 기동하는 부분을 한 번에 처리한다. 컨테이너 안에는 이 RPC 브리지 클라이언트가 미리 설치돼 있다.

7.1.6 MCU와의 연동은 결국 Bridge를 거친다

두 저장소 모두 공통된 구조를 드러낸다 — ROS 2 노드는 STM32 MCU와 직접 통신하지 않는다. ROS 2는 MPU(Linux) 쪽 컨테이너 안에서만 존재하고, MCU 쪽은 순수 Zephyr 기반 Arduino 스케치(3.2)로 남아 있으며, 둘 사이는 UNO Q 고유의 Bridge(공식 명칭. 배후 데몬 arduino-router + MCU 측 라이브러리 Arduino_RouterBridge — App Lab의 MPU-MCU 통신 계층. 내부 구조는 3.4에서 다룬다)를 거친다. 두 커뮤니티 저장소가 쓰는 "RPC bridge"라는 표현은 이 공식 Bridge를 가리키는 것으로, Bridge.provide("set_led_state", ...)처럼 3.4.4에서 다루는 API를 그대로 쓴다.

즉 "STM32에 micro-ROS 노드를 탑재해 ROS 2 네트워크에 직접 참여시킨다"는 접근은 이번에 조사한 커뮤니티 자료에서 확인되지 않았다 — 대신 MCU 쪽 함수를 Bridge로 노출하고, MPU 쪽 ROS 2 노드가 그 함수를 호출하는 간접 구조다. 실시간성이 중요한 제어 루프(PID 모터 제어 등)를 ROS 2 토픽 경유로 설계하려 한다면 이 Bridge의 지연 특성을 먼저 이해해야 한다 — 8.3(MPU-MCU 통신 지연 분석)와 8.4(PID 모터 제어 루프 실전)를 함께 참조.

7.1.7 트러블슈팅 체크리스트

  • colcon build 도중 컴파일러가 강제 종료된다(Killed) → OOM 가능성이 크다. free -h로 스왑 여유 확인, --parallel-workers 1로 낮추거나 Docker 이미지 빌드로 전환.
  • docker build가 디스크 부족으로 실패한다 → df -h로 eMMC 여유 확인. docker system prune -a --volumes로 캐시 정리(5.4). ros-jazzy-desktop 대신 ros-jazzy-base로 낮춘다.
  • RViz2가 보드에서 검은 화면/크래시 → 애초에 보드에서 직접 실행하지 않는다. PC/WSL2에서 원격 연결(7.1.5 RViz 절 참조), ROS_DOMAIN_ID·RMW_IMPLEMENTATION이 양쪽에서 일치하는지 확인.
  • 컨테이너 안 ROS 2 노드가 MCU RPC를 못 찾는다 → .ino 업로드가 먼저 성공했는지, App Lab의 Bridge 서비스가 실행 중인지 확인(3.4). 호스트 재부팅 후 재시도.
  • rosdep install이 특정 키를 못 찾는다 → GUI 관련 패키지를 제거하지 않고 남겨뒀을 가능성. --skip-keys로 건너뛰거나 해당 저장소를 src에서 삭제.

7.1.8 다음 장으로

ROS 2 설치가 끝났다면, 다음 단계는 미들웨어를 DDS에서 Zenoh로 바꾸거나 병행하는 것이다 — 7.2(Zenoh 연동)에서 이어간다. 데이터를 영속화하려면 7.3(PostgreSQL)·7.4(SQLite)을 참조한다.

7.1.9 참고자료