제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 참조). 여기서 확인해야 할 두 가지 사실이 있다.
- Debian은 애초에 Jazzy의 1군 지원 대상이 아니다. REP 2000 표에는 배포판에 따라 Debian이 낮은 등급(소스 빌드 전제, 바이너리 없음)으로 잠깐 언급되는 시기가 있었을 뿐 안정적인 1군 대상인 적이 없다.
- 설사 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를 켜고 끄는 엔드투엔드 예제.
실행 절차(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)을 참조한다.