안녕하세요! 지난 포스팅에서는 [Flightmare 시리즈 1] Docker를 활용한 Flightmare 빌드 및 환경 구축을 다루었습니다. 복잡한 의존성을 Docker로 깔끔하게 해결하고, 예제 런처를 실행하는 것까지 성공했었죠.
오늘은 그 후속편으로, **"나만의 C++ 프로젝트를 만들어 Flightmare Unity와 직접 연동하는 방법"**을 정리해 보려 합니다. ROS 패키지 내부에서 개발하는 것이 정석이지만, 알고리즘 테스트나 가벼운 시뮬레이션을 위해 Standalone C++ 프로젝트에서 이미 빌드된 라이브러리(flightlib)를 가져다 쓰는 방법을 익혀두면 매우 유용합니다.
Flightmare의 핵심은 물리 엔진인 flightlib과 그래픽 엔진인 Unity가 **ZeroMQ(ZMQ)**라는 프로토콜을 통해 초고속으로 통신한다는 점입니다. 이번 실습에서는 ROS의 무거운 미들웨어 없이, 순수 C++ 코드로 이 다리를 놓아 드론을 제어해 보겠습니다. 특히 catkin_make로 빌드된 Flightlib 라이브러리를 외부 CMake 프로젝트에서 링크하는 방법과, Flightlib의 최신 API(State 처리 방식 등)를 적용하는 과정을 중점적으로 다룹니다.
0. 개발 환경 (Development Environment)
이번 프로젝트는 Docker 컨테이너 환경에서 진행되었습니다. Flightmare는 의존성이 많기 때문에 Docker를 사용하는 것이 정신건강에 이롭습니다. 지난 포스팅에서 구축한 Docker 컨테이너(Ubuntu 18.04 + CUDA) 안에서 진행됩니다.
- OS: Ubuntu 18.04 (Docker Container)
- Language: C++17 (Flightmare 권장 표준)
- Core Dependencies:
- Flightlib: 이미 Catkin Workspace에 빌드되어 있는 상태여야 합니다.
- Eigen3 & OpenCV: 수학 연산 및 이미지 처리용.
- ZeroMQ (zmq, zmqpp): Unity와의 브릿지 통신을 위한 필수 라이브러리.
1. 프로젝트 생성 (Directory Structure)
가장 먼저 작업 공간의 구조를 잡아야 합니다. Docker 컨테이너 내의 /root/workspace 경로를 기준으로, 기존 **Flightmare(ROS)**와 우리가 만들 **새 프로젝트(myprj)**를 나란히 배치하겠습니다.
추천 디렉토리 구조
/root/workspace/
├── catkin_ws/ # [기존] Flightmare ROS 워크스페이스
│ ├── src/
│ │ └── flightmare/ # (A) 헤더 파일 위치 (flightlib/include)
│ └── devel/
│ └── lib/ # (B) 라이브러리 파일 위치 (libflightlib.so)
│
└── myprj_flightmare/ # [NEW] 이번에 만들 독립 프로젝트
├── CMakeLists.txt # 빌드 설정 파일
├── build/ # 빌드 결과물이 생길 곳
└── src/
└── fmp_connection.cpp # 소스 코드
설명:
- catkin_ws: 이미 1편에서 빌드가 완료된 상태여야 합니다. 여기서 헤더 파일과 라이브러리(.so)를 빌려옵니다.
- myprj_flightmare: 이번 실습을 위해 새로 만들 폴더입니다.
터미널 명령어로 구조 생성하기:
cd /root/workspace
mkdir -p myprj_flightmare/src
cd myprj_flightmare
touch CMakeLists.txt src/fmp_connection.cpp
2. 프로젝트 설정 (CMakeLists.txt)
Flightmare는 기본적으로 ROS catkin 빌드 시스템을 따르지만, 여기서는 순수 CMake 프로젝트(myprj_flightmare)를 생성하여 이미 빌드된 flightlib를 가져다 쓰는 방식을 택했습니다.
가장 중요한 포인트는 라이브러리 링킹입니다. flightlib뿐만 아니라 통신을 담당하는 zmq, zmqpp를 반드시 링크해줘야 합니다.
파일명: /root/workspace/myprj_flightmare/CMakeLists.txt
cmake_minimum_required(VERSION 3.00)
project(myprj_flightmare)
# 1. C++ 표준 설정 (C++17 권장)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(-Ofast -march=native -fopenmp)
# 출력 디렉토리 설정
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
# 2. 필수 외부 라이브러리 찾기
find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED)
# 3. Flightlib 경로 설정 (핵심!)
# Flightlib 소스 코드의 헤더 경로
set(FLIGHTLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../flightmare/flightlib/include)
# 빌드된 라이브러리 파일 (.so) 경로 지정
# 보통 catkin workspace의 devel/lib 안에 생성됩니다.
set(FLIGHTLIB_LIB_FILE ${CMAKE_SOURCE_DIR}/../../devel/lib/libflightlib.so)
# 4. 실행 파일 생성
add_executable(fmp src/fmp_connection.cpp)
# 5. Include 및 링크 설정
target_include_directories(fmp PUBLIC
${FLIGHTLIB_INCLUDE_DIR}
${EIGEN3_INCLUDE_DIR}
${OpenCV_INCLUDE_DIRS}
)
target_link_libraries(fmp PUBLIC
${FLIGHTLIB_LIB_FILE} # Flightlib
${OpenCV_LIBS} # OpenCV
zmq # ZeroMQ (C)
zmqpp # ZeroMQ (C++) - 통신 에러 방지 필수!
pthread
stdc++fs
)
Tip: zmqpp를 링크하지 않으면 컴파일 단계에서 Unity Bridge 관련 링킹 에러가 발생할 수 있습니다.
3. 실습 예제 (Source Code)
Unity와 통신하여 드론(Quadrotor)을 생성하고, Z축으로 천천히 상승시키는 코드입니다. Flightlib 버전에 따라 API 사용법(특히 connect 함수 유무, getState 방식)이 조금씩 다르므로 주의해야 합니다.
#include <iostream>
#include <unistd.h> // for sleep
// Flightlib 헤더 포함
#include "flightlib/bridges/unity_bridge.hpp"
#include "flightlib/objects/quadrotor.hpp"
#include "flightlib/common/types.hpp"
using namespace flightlib;
int main() {
// ------------------------------------------------
// 1. Unity Bridge 초기화
// ------------------------------------------------
// 별도의 connect() 함수 호출 없이 객체 생성 시점에 통신 준비가 시작됩니다.
UnityBridge bridge;
std::cout << "Unity Bridge 초기화..." << std::endl;
// ------------------------------------------------
// 2. 쿼드로터(드론) 객체 생성 및 초기화
// ------------------------------------------------
// 설정 파일 경로 (자신의 환경에 맞게 수정 필요)
std::string config_path = "/root/workspace/catkin_ws/flightmare_ws/src/flightmare/flightlib/configs/quadrotor_env.yaml";
std::shared_ptr<Quadrotor> quad_ptr = std::make_shared<Quadrotor>(config_path);
// 초기 상태(State) 설정
// setPosition 대신 reset() 함수를 사용하여 전체 상태를 초기화합니다.
QuadState initial_state;
initial_state.setZero();
initial_state.p << 0.0, 0.0, 5.0; // 높이 5m에서 시작하도록 설정
quad_ptr->reset(initial_state);
// ------------------------------------------------
// 3. Bridge에 쿼드로터 등록
// ------------------------------------------------
bridge.addQuadrotor(quad_ptr);
std::cout << "시뮬레이션 루프 시작..." << std::endl;
// ------------------------------------------------
// 4. 시뮬레이션 루프 (Action -> Render)
// ------------------------------------------------
const int total_steps = 500;
QuadState current_state; // 상태를 저장할 변수
for (int i = 0; i < total_steps; ++i) {
// [중요] 상태 가져오기
// getState는 리턴값이 아니라 포인터 인자를 통해 값을 받아옵니다.
quad_ptr->getState(¤t_state);
// 간단한 제어: Z축(높이)을 0.01씩 증가
current_state.p[2] += 0.01;
// 변경된 상태 적용 (Kinematic Update)
quad_ptr->setState(current_state);
// Unity로 렌더링 요청 (이때 데이터가 전송됨)
bridge.getRender(true);
// 루프 속도 조절 (20ms)
usleep(20000);
}
std::cout << "시뮬레이션 종료." << std::endl;
return 0;
}
코드 주요 포인트
- reset(initial_state): 단순히 위치만 옮기는 것이 아니라, 속도/회전 등을 포함한 QuadState 전체를 초기화합니다.
- getState(¤t_state): 상태를 받아올 때 리턴 값 방식이 아닌, 포인터를 넘겨주는 방식을 사용해야 합니다.
- bridge.getRender(true): 이 함수가 호출되어야 Unity 화면상의 드론이 움직입니다.
4. 빌드 및 실행 (Build & Run)
이제 폴더 구조와 코드가 준비되었으니 컴파일하고 실행해 보겠습니다.
1) 빌드하기
myprj_flightmare 폴더 내부에서 build 폴더를 만들고 컴파일합니다.
cd /root/workspace/myprj_flightmare
mkdir build
cd build
cmake ..
make
에러 체크: 만약 FindEigen3.cmake 관련 에러가 난다면 cmake 버전 문제일 수 있으나, 제공된 Docker 환경에서는 정상 작동해야 합니다. libflightlib.so를 찾을 수 없다는 에러가 나면 CMakeLists.txt의 경로 부분을 다시 확인하세요.
2) 실행 준비 (Host PC)
Windows나 Host Ubuntu에서 **RPG_Flightmare 실행 파일(Unity)**을 먼저 켜주세요.
- 화면에 공장 내부(Industrial)나 숲 같은 씬이 떠 있어야 합니다.
3) 코드 실행 (Docker Container)
# build 폴더 안에서 실행
./fmp
5. 결과 확인
- 터미널에 Unity Bridge 초기화... 메시지가 출력됩니다.
- Unity 화면에서 드론이 높이 5m 지점에 생성됩니다.
- 드론이 아주 천천히 위쪽(Z축)으로 상승하는 것을 확인할 수 있습니다.
- 500 step 후 시뮬레이션이 종료됩니다.
마무리
이번 포스팅에서는 폴더 구조를 명확히 분리하여, ROS 워크스페이스를 건드리지 않고 독립적인 C++ 개발 환경을 구축하는 방법을 다뤘습니다.
- 핵심: catkin_ws는 라이브러리 저장소로만 사용하고, 개발은 myprj에서 진행합니다.
- 주의: CMakeLists.txt에서 flightlib의 헤더와 라이브러리 경로를 정확히 연결해주는 것이 관건입니다.
다음 포스팅에서는 이 환경 위에서 단순 상승이 아닌, PID 제어기를 직접 구현하여 드론을 목표 지점에 호버링 시키는 내용을 다뤄보겠습니다.
'엔지니어링 > 프로그래밍' 카테고리의 다른 글
| [Flightmare 시리즈 4] Docker 속 ROS 노드를 VS Code로 해부하기 (GDB 디버깅 완벽 가이드) (1) | 2026.01.18 |
|---|---|
| [Flightmare 시리즈 3] 드론 자율비행, ROS 기반 Waypoint 비행 구현 (0) | 2026.01.18 |
| [RPG 프로젝트 전격분석] UZH RPG의 Flightmare: Docker 기반 빌드 및 ROS/RL 연동 완벽 가이드 (1) | 2026.01.10 |
| GIT 명령어를 사용하여 remote에 프로젝트 업로드 (0) | 2023.12.30 |
| Github.io 블로그 만들기(3) - Jekyll에 테마 적용하기 (0) | 2023.01.26 |
댓글