[Physical AI W1D2] 6/6 — ROS 2 패키지 빌드 마스터: Python·C++·colcon

2026. 6. 14. 17:15·피지컬AI

[Physical AI W1D2 · 6/6]

로봇 소프트웨어는 패키지 단위로 관리된다. Catkin과 Colcon의 차이를 이해하고, Python(ament_python)과 C++(ament_cmake) 패키지를 각각 생성·빌드·실행하며, setup.py와 CMakeLists.txt, colcon 옵션, 실무 패키지 설계까지 — ROS 2 개발의 기본기를 마무리한다.

이 글에서 마무리하는 것

  • ROS 패키지·워크스페이스란, Catkin vs Colcon
  • Python 패키지(ament_python) 생성·빌드·실행
  • C++ 패키지(ament_cmake) 생성·빌드·실행
  • setup.py vs CMakeLists.txt, colcon 빌드 옵션, 실무 패키지 설계

(Day2 ROS 2 블록의 마지막 편. 4·5편이 "코드 작성"이었다면, 이번엔 그 코드를 담는 "그릇=패키지" 자체를 다룹니다.)


1. ROS 패키지와 워크스페이스

ROS 패키지는 ROS 프로그램을 기능 단위로 묶은 폴더입니다. 이동 로봇이라면 camera_driver, lidar_driver, robot_navigation, robot_control, object_detection처럼 기능별로 나눕니다. 모든 코드를 한 폴더에 넣으면 관리·디버깅이 어렵지만, 나누면 필요한 부분만 고치고 재사용할 수 있습니다.

패키지가 보통 담는 것:

파일/폴더 설명
package.xml 패키지 이름·버전·의존성·유지보수자 정보
setup.py (Python) 실행 파일 등록·설치 설정
CMakeLists.txt (C++) 빌드 설정
패키지명/ Python 코드 폴더
src/ C++ 소스 폴더
launch/·config/ 노드 일괄 실행·파라미터 설정
msg/·srv/·action/ 사용자 정의 메시지·서비스·액션

워크스페이스는 패키지들을 모아 빌드하는 작업 공간입니다.

ros2_ws/
├── src/      ← 사용자가 만들거나 받은 패키지 (직접 만드는 유일한 폴더)
├── build/    ← 빌드 중간 파일 (colcon build가 자동 생성)
├── install/  ← 빌드 결과(실행물) 설치
└── log/      ← 빌드·오류 로그

처음엔 src만 만들고, build/install/log는 colcon build가 자동 생성합니다.


2. Catkin vs Colcon

ROS는 버전에 따라 빌드 도구가 다릅니다.

구분 Catkin (ROS 1) Colcon (ROS 2)
대표 명령 catkin_make, catkin build colcon build
워크스페이스 src, build, devel src, build, install, log
빌드 방식 Catkin 패키지 중심 여러 빌드 타입 지원
결과 반영 source devel/setup.bash source install/setup.bash

💡 왜 둘 다 알아야 하나 — 현재 ROS 2는 Colcon이 기본이지만, 기존 산업·연구 코드와 오픈소스 예제 상당수가 ROS 1(Catkin) 기반입니다. ROS 1 → ROS 2 이전 작업이 흔해서, 두 구조를 읽을 수 있어야 합니다. (참고: ROS 1은 catkin_create_pkg my_pkg roscpp rospy std_msgs → catkin_make → source devel/setup.bash)


3. 실습 준비 + 워크스페이스 생성

source /opt/ros/humble/setup.bash
echo $ROS_DISTRO     # humble  (ROS 2 환경이 잡혔는지 확인)
colcon --help        # 없으면: sudo apt install -y python3-colcon-common-extensions

# 빌드 실습용 워크스페이스
mkdir -p ~/ros2_build_ws/src
cd ~/ros2_build_ws
ls                   # src (아직 build/install/log 없음)

4. Python 패키지 (ament_python)

cd ~/ros2_build_ws/src
ros2 pkg create py_robot_pkg --build-type ament_python --dependencies rclpy std_msgs
tree ~/ros2_build_ws/src/py_robot_pkg

생성 구조:

py_robot_pkg
├── package.xml          # 패키지 정보·의존성
├── py_robot_pkg/        # 실제 Python 코드 폴더
│   └── __init__.py
├── resource/
├── setup.cfg
├── setup.py             # 실행 파일 등록
└── test/

노드를 작성합니다.

cd ~/ros2_build_ws/src/py_robot_pkg/py_robot_pkg
nano status_publisher.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String


class StatusPublisher(Node):
    def __init__(self):
        super().__init__('status_publisher')

        self.publisher_ = self.create_publisher(String, 'robot_status', 10)
        self.count = 0
        self.timer = self.create_timer(1.0, self.publish_status)

    def publish_status(self):
        msg = String()
        msg.data = f'Robot package build test message: {self.count}'
        self.publisher_.publish(msg)
        self.get_logger().info(msg.data)
        self.count += 1


def main(args=None):
    rclpy.init(args=args)
    node = StatusPublisher()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

setup.py의 entry_points에 등록합니다.

entry_points={
    'console_scripts': [
        'status_publisher = py_robot_pkg.status_publisher:main',
    ],
},
항목 설명
packages=find_packages() Python 모듈 자동 탐색
data_files ROS 2 패키지 인덱스에 등록
entry_points ros2 run으로 실행할 명령 등록

package.xml에는 의존성이 들어 있어야 합니다(<depend>rclpy</depend>, <depend>std_msgs</depend>).

빌드·실행:

cd ~/ros2_build_ws
colcon build
ls                       # build install log src
source install/setup.bash
ros2 pkg list | grep py_robot_pkg     # py_robot_pkg
ros2 run py_robot_pkg status_publisher
# [INFO] [status_publisher]: Robot package build test message: 0 ...

5. C++ 패키지 (ament_cmake)

cd ~/ros2_build_ws/src
ros2 pkg create cpp_robot_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
tree ~/ros2_build_ws/src/cpp_robot_pkg

구조(Python과 다릅니다 — CMakeLists.txt·src/·include/):

cpp_robot_pkg
├── CMakeLists.txt
├── include/cpp_robot_pkg/
├── package.xml
└── src/

C++ 노드를 작성합니다.

cd ~/ros2_build_ws/src/cpp_robot_pkg/src
nano cpp_status_publisher.cpp
#include <chrono>
#include <memory>
#include <string>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class CppStatusPublisher : public rclcpp::Node
{
public:
  CppStatusPublisher()
  : Node("cpp_status_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("cpp_robot_status", 10);
    timer_ = this->create_wall_timer(
      1s, std::bind(&CppStatusPublisher::publish_status, this));
  }

private:
  void publish_status()
  {
    auto message = std_msgs::msg::String();
    message.data = "C++ ROS 2 package build test message: " + std::to_string(count_);
    publisher_->publish(message);
    RCLCPP_INFO(this->get_logger(), "%s", message.data.c_str());
    count_++;
  }

  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
  size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<CppStatusPublisher>());
  rclcpp::shutdown();
  return 0;
}

C++은 CMakeLists.txt에 실행 파일을 직접 등록해야 합니다. 그런데 ⚠️ ros2 pkg create가 만들어 준 CMakeLists.txt에는 실행 파일 등록 줄이 없습니다. 생성 직후의 파일은 대략 이렇습니다(핵심만).

cmake_minimum_required(VERSION 3.8)
project(cpp_robot_pkg)
# ...
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)      # --dependencies로 만들면 이미 들어있음
find_package(std_msgs REQUIRED)

if(BUILD_TESTING)
  # ... 린트 설정 ...
endif()

ament_package()                    # ← 여기 위에 실행 파일 등록이 '없다'

이대로 빌드하면 빌드는 성공해도 실행 파일이 안 생겨, 나중에 ros2 run 시 No executable found가 납니다. 그래서 ament_package() 바로 위에 다음 세 블록을 추가합니다.

# 1) 소스를 컴파일해 실행 파일 생성
add_executable(cpp_status_publisher src/cpp_status_publisher.cpp)

# 2) 실행 파일에 ROS 2 의존성(rclcpp·std_msgs) 연결
ament_target_dependencies(cpp_status_publisher rclcpp std_msgs)

# 3) ros2 run이 찾도록 lib/<패키지명>에 설치
install(TARGETS cpp_status_publisher
  DESTINATION lib/${PROJECT_NAME})
CMake 항목 의미
add_executable() 소스를 컴파일해 실행 파일 생성
ament_target_dependencies() 실행 파일에 ROS 2 의존성 연결
install(TARGETS …) ros2 run이 찾도록 설치 — 이게 빠지면 No executable found

💡 find_package(rclcpp REQUIRED)·find_package(std_msgs REQUIRED)는 --dependencies rclcpp std_msgs로 패키지를 만들었다면 이미 있습니다. if(BUILD_TESTING) 블록은 지우지 말고 그대로 두세요(테스트용).

빌드·실행:

cd ~/ros2_build_ws
colcon build                              # 전체
# colcon build --packages-select cpp_robot_pkg   # 특정 패키지만
source install/setup.bash
ros2 run cpp_robot_pkg cpp_status_publisher

⚠️ 흔한 함정 — No executable found (빌드는 성공했는데 실행이 안 됨)

colcon build가 Finished <<< cpp_robot_pkg로 끝났는데 ros2 run이 다음을 뱉으면:

~/ros2_build_ws# ros2 run cpp_robot_pkg cpp_status_publisher
No executable found

원인: CMakeLists.txt에 위 3블록(add_executable·ament_target_dependencies·install)을 추가하지 않았거나, 추가 후 다시 빌드/source 하지 않은 것입니다. C++ 빌드는 실행 파일 등록이 없어도 Finished로 끝나기 때문에 놓치기 쉽습니다(가장 흔한 실수).

해결 — 3블록을 넣었는지 확인하고 깨끗이 다시 빌드:

cd ~/ros2_build_ws
rm -rf build/ install/ log/          # 이전 빌드 잔재 제거(권장)
colcon build --packages-select cpp_robot_pkg
source install/setup.bash
ros2 run cpp_robot_pkg cpp_status_publisher

install/을 지웠다가 다시 빌드하는 과정에서 source install/setup.bash 시 AMENT_PREFIX_PATH ... doesn't exist 같은 경고가 잠깐 보일 수 있는데, 빌드가 끝나면 사라지는 무해한 경고입니다. (확인: ls ~/ros2_build_ws/src/cpp_robot_pkg/CMakeLists.txt에서 add_executable·install 줄이 있는지 cat으로 점검)

⚠️ 흔한 함정 — ModuleNotFoundError: No module named 'cmake' (C++ 빌드 실패)

Colab 등 일부 환경에서 C++ 패키지를 colcon build 할 때 다음 오류가 납니다.

--- stderr: cpp_robot_pkg
Traceback (most recent call last):
  File "/usr/local/bin/cmake", line 4, in <module>
    from cmake import cmake
ModuleNotFoundError: No module named 'cmake'
---
Failed   <<< cpp_robot_pkg [0.06s, exited with code 1]

원인: /usr/local/bin/cmake가 사실은 pip로 설치된 cmake의 래퍼 스크립트인데, 정작 그 cmake 파이썬 모듈이 깨져 있어서입니다. 이 래퍼가 PATH에서 정상 apt cmake(/usr/bin/cmake, 3.22)를 가립니다. 그래서 apt install cmake를 해도 "already newest version"으로 무시되어 안 고쳐집니다(엉뚱한 cmake를 보고 있는 것).

해결 (검증됨):

# 1) pip 자체가 깨져 있을 수 있으니 먼저 복구
sudo apt update
sudo apt install -y python3-pip

# 2) pip cmake 패키지 재설치 → /usr/local/bin/cmake 래퍼 정상화
pip3 install cmake

# 3) 다시 빌드
cd ~/ros2_build_ws
colcon build --packages-select cpp_robot_pkg

성공하면 Finished <<< cpp_robot_pkg가 뜹니다. 이때 함께 보이는

CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.10 will be removed from a future version of CMake.

경고는 무해합니다(빌드는 성공). 최신 cmake가 아주 낮은 최소 버전을 권장하지 않는다는 안내일 뿐입니다. 거슬리면 CMakeLists.txt 첫 줄을 cmake_minimum_required(VERSION 3.10) 이상으로 올리면 사라집니다.

💡 대안 — 깨진 래퍼를 치우기: pip 래퍼 대신 apt cmake를 쓰고 싶다면 pip3 uninstall -y cmake && hash -r 후 which cmake가 /usr/bin/cmake로 잡히는지 확인하세요(단, pip가 깨졌다면 위 1)을 먼저 실행). 핵심은 "colcon이 어떤 cmake를 보고 있는가" 입니다 — which cmake로 확인하는 습관이 좋습니다.


6. Colcon 빌드 옵션 & 환경 설정

명령 설명
colcon build 워크스페이스 전체 빌드
colcon build --packages-select 패키지명 특정 패키지만(시간 절약)
colcon build --symlink-install Python 소스(.py) 수정 시 재빌드 부담 감소 (단, setup.py의 entry_points 변경이나 새 파일 추가 시엔 재빌드 필요)
colcon list 패키지 목록
colcon test 테스트 실행

빌드 뒤에는 항상 환경을 반영해야 합니다.

# 일반적인 순서(매 터미널)
source /opt/ros/humble/setup.bash     # ROS 2 기본
cd ~/ros2_build_ws
source install/setup.bash             # 방금 빌드한 내 워크스페이스

⚠️ .bashrc에 특정 워크스페이스를 고정하면 여러 워크스페이스를 오갈 때 혼동이 생깁니다. 실습 중엔 직접 source 하며 "지금 어느 워크스페이스인지" 의식하는 습관이 좋습니다.


7. Python vs C++ 패키지

구분 Python (ament_python) C++ (ament_cmake)
주요 파일 setup.py CMakeLists.txt
ROS 클라이언트 rclpy rclcpp
실행 등록 entry_points add_executable + install
장점 작성이 쉽고 빠름 실행 속도·성능
주 사용 실습·프로토타입·데이터 처리 제어·센서 드라이버·실시간 노드

→ 실제 로봇 시스템에선 Python과 C++ 패키지가 함께 쓰입니다(빠른 개발은 Python, 성능이 중요한 제어·드라이버는 C++).


8. ROS 2 패키지 개발 흐름

워크스페이스 생성
→ src에 패키지 생성(ros2 pkg create)
→ package.xml 의존성 확인
→ 코드 작성(Python 또는 C++)
→ 실행 파일 등록(setup.py / CMakeLists.txt)
→ colcon build
→ source install/setup.bash
→ ros2 run으로 실행
→ ros2 node / ros2 topic으로 확인

이 흐름은 이후 모든 ROS 2 실습에서 반복됩니다 — 센서 처리, 제어, Gazebo 연동, 강화학습 통합까지 전부 이 패키지·빌드 구조 위에서 진행됩니다.


9. 흔한 빌드 오류

오류 원인 / 해결
colcon: command not found sudo apt install -y python3-colcon-common-extensions
Package not found 빌드/소스 누락 → colcon build → source install/setup.bash
No executable found Python: setup.py의 entry_points 누락 / C++: CMakeLists.txt에 add_executable·install(TARGETS) 누락 (빌드는 성공해도 실행 파일이 없음 → 위 5절 함정)
ModuleNotFoundError: No module named 'py_robot_pkg.xxx' 파일명·setup.py 등록 오타, 또는 빌드 후 source 미실행
rclcpp/rclcpp.hpp: No such file find_package(rclcpp REQUIRED) 누락
ModuleNotFoundError: No module named 'cmake' (C++ 빌드) pip의 깨진 cmake 래퍼(/usr/local/bin/cmake)가 apt cmake를 가림 → sudo apt install -y python3-pip && pip3 install cmake 후 재빌드 (위 5절 함정 참고)
수정했는데 결과 안 바뀜 재빌드 안 함 → colcon build(Python은 --symlink-install 편리)

10. 실무 관점 — 패키지를 어떻게 나누나

실제 로봇 시스템은 기능과 책임으로 패키지를 나눕니다. 예: 물류 로봇.

패키지 역할
warehouse_robot_description URDF·XACRO·mesh(로봇 모델)
warehouse_robot_bringup 전체 노드 실행 launch
warehouse_robot_navigation 경로 계획·이동
warehouse_robot_perception 카메라·LiDAR 인식
warehouse_robot_control 바퀴·모터·그리퍼 제어
warehouse_robot_interfaces 사용자 정의 msg·srv·action
warehouse_robot_simulation Gazebo 시뮬레이션
warehouse_robot_rl 강화학습 환경·정책

설계 기준: 기능 분리(한 패키지 = 한 책임), 재사용성, 의존성 최소화, 인터페이스 분리(msg·srv·action은 별도 패키지). 프로젝트가 커져도 관리가 쉬워집니다.


Day2 마무리

이번 6편(Day2)에서 우리는:

  • 리눅스 명령줄(1·2편)으로 기초 체력을 다지고,
  • ROS 2 통신 4종(3편 개념 → 4편 토픽 → 5편 서비스·액션)을 직접 구현하고,
  • 패키지·빌드 구조(6편)로 그 코드를 담는 그릇을 익혔습니다.

워크스페이스 → 패키지 → 코드 → 등록 → colcon build → source → ros2 run

이 흐름이 ROS 2 로봇 개발의 기본기입니다. 앞으로의 센서 처리, 로봇 제어, Gazebo 연동, 강화학습 통합 실습은 모두 이 토대 위에서 확장됩니다.

📚 Week1 Day2 전체 목차 (총 6편)

  • 1/6 리눅스 기초 — 쉘·파일시스템·핵심 명령어
  • 2/6 리눅스 실전 8 시나리오
  • 3/6 ROS 2 통신 4종 개념 — 노드·토픽·서비스·액션
  • 4/6 ROS 2 토픽 Pub/Sub 직접 만들기
  • 5/6 ROS 2 서비스·액션 구현
  • 6/6 ROS 2 패키지 빌드 — Python·C++·colcon — 이번 글
저작자표시 (새창열림)

'피지컬AI' 카테고리의 다른 글

[Physical AI W2D1] 2/6 — RViz 개념 ②: TF·좌표계·URDF·센서 Display·디버깅  (0) 2026.06.20
[Physical AI W2D1] 1/6 — RViz로 로봇을 눈으로 보다: 시각화 흐름·Fixed Frame·Display  (0) 2026.06.20
[Physical AI W1D2] 5/6 — ROS 2 서비스·액션 구현: 요청/응답과 목표/피드백  (0) 2026.06.14
[Physical AI W1D2] 4/6 — ROS 2 토픽 직접 만들기: Publisher·Subscriber  (0) 2026.06.14
[Physical AI W1D2] 3/6 — ROS 2 통신의 4가지 길: 노드·토픽·서비스·액션  (0) 2026.06.14
'피지컬AI' 카테고리의 다른 글
  • [Physical AI W2D1] 2/6 — RViz 개념 ②: TF·좌표계·URDF·센서 Display·디버깅
  • [Physical AI W2D1] 1/6 — RViz로 로봇을 눈으로 보다: 시각화 흐름·Fixed Frame·Display
  • [Physical AI W1D2] 5/6 — ROS 2 서비스·액션 구현: 요청/응답과 목표/피드백
  • [Physical AI W1D2] 4/6 — ROS 2 토픽 직접 만들기: Publisher·Subscriber
hyeseong-dev
hyeseong-dev
안녕하세요. 백엔드 개발자 이혜성입니다.
  • hyeseong-dev
    어제 오늘 그리고 내일
    hyeseong-dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (342) N
      • 여러가지 (11) N
        • 알고리즘 & 자료구조 (73)
        • 오류 (4)
        • 이것저것 (29)
        • 일기 (1)
      • 프레임워크 (39)
        • 자바 스프링 (39)
        • React Native (0)
      • 프로그래밍 언어 (39)
        • 파이썬 (31)
        • 자바 (3)
        • 스프링부트 (5)
      • 컴퓨터 구조와 운영체제 (3)
      • DB (17)
        • SQL (0)
        • Redis (17)
      • 클라우드 컴퓨팅 (21)
        • 도커 (2)
        • AWS (19)
      • 스케쥴 (65)
        • 세미나 (0)
        • 수료 (0)
        • 스터디 (24)
        • 시험 (41)
      • 트러블슈팅 (1)
      • 자격증 (2) N
        • 정보처리기사 (0)
        • 정보보안기사 (1)
        • 네트워크관리사 (1) N
      • 재태크 (0)
        • 암호화폐 (0)
        • 기타 (0)
      • 피지컬AI (26)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    로봇팔
    Redis
    동차변환행렬
    ROS2
    moveit
    운동학
    FastAPI
    역운동학
    그리디
    TF
    피지컬ai
    java
    자바
    프로그래머스
    AWS
    Spring WebFlux
    항해99
    WebFlux
    Spring Boot
    완전탐색
    AWS네트워크계층으로읽기
    celery
    docker
    Python
    네트워크
    취업리부트
    EC2
    rclpy
    SAA
    클라우드
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
hyeseong-dev
[Physical AI W1D2] 6/6 — ROS 2 패키지 빌드 마스터: Python·C++·colcon
상단으로

티스토리툴바