안녕하세요! 지난 포스팅(Part 2)에서는 ROS 없이 순수 C++ 프로젝트(Standalone)에서 Flightlib 라이브러리를 링크하여 드론을 단순 상승시키는 방법을 다루었습니다. 가볍게 테스트하기엔 좋았지만, 실제 자율비행 시스템을 개발하려면 센서 데이터 처리와 노드 간 통신이 필수적입니다.
오늘 Part 3에서는 다시 ROS(Robot Operating System)의 정석 환경으로 돌아와, **"C++ 코드로 드론을 특정 좌표(Waypoint)로 이동시키는 노드"**를 구현해 보겠습니다.
이 과정은 단순한 이동처럼 보이지만, Unity Bridge 연결 → 객체 생성 → 상태(State) 업데이트 → 렌더링으로 이어지는 Flightmare 제어 루프의 핵심을 이해하는 중요한 단계입니다.
0. 개발 목표 (Goal)
- 환경: Docker Container (ROS Melodic)
- 목표:
- ROS 패키지 내에 새로운 노드(fmp_waypoint) 생성
- Flightmare Bridge를 통해 Unity와 통신 연결
- 드론을 초기 위치 (0, 0, 5)에서 생성 후, 목표 지점 (5, 5, 5)으로 이동
1. 프로젝트 생성 (Directory Structure)
가장 먼저 작업 공간의 구조를 잡아야 합니다. Docker 컨테이너 내의 /root/workspace 경로를 기준으로, 기존 **Flightmare(ROS)**와 우리가 만들 **새 프로젝트(myprj)**를 나란히 배치하겠습니다.
추천 디렉토리 구조
/root/workspace/
├── catkin_ws/ # [기존] Flightmare ROS 워크스페이스
│ ├── src/
│ │ └── flightmare/ # (A) 헤더 파일 위치 (flightlib/include)
│ └── build/ # 빌드 결과물
│ └── devel/
│ └── lib/ # (B) 라이브러리 파일 위치 (libflightlib.so)
│
└── myprj_flightmare_2/ # [NEW] 이번에 만들 독립 프로젝트
├── CMakeLists.txt # 빌드 설정 파일
├── package.xml # ROS 패키지 정의
└── src/
└── fmp_waypoint.cpp # 소스 코드
설명:
- catkin_ws: 이미 1편에서 빌드가 완료된 상태여야 합니다. 여기서 헤더 파일과 라이브러리(.so)를 빌려옵니다.
- myprj_flightmare_2: 이번 실습을 위해 새로 만들 폴더입니다.
프로젝트내 build 폴더가 없습니다. 이번에는 ROS 빌드 시스템인 catkin을 사용해서 빌드하므로 build의 결과물들은 catkin_ws의 build 폴더에 생성됩니다.
터미널 명령어로 구조 생성하기:
cd /root/workspace
mkdir -p myprj_flightmare/src
cd myprj_flightmare
touch CMakeLists.txt src/fmp_connection.cpp
2. 노드 코드 작성 (Source Code)
이번에는 catkin_ws 내부의 패키지(myprj_flightmare_2)에서 작업합니다. Flightlib의 UnityBridge를 사용하여 시뮬레이터와 통신하는 핵심 코드를 살펴보겠습니다.
파일명: src/fmp_waypoint.cpp
#include <ros/ros.h>
#include <flightlib/bridges/unity_bridge.hpp>
#include <flightlib/common/quad_state.hpp>
#include <flightlib/objects/quadrotor.hpp>
int main(int argc, char *argv[]) {
// ------------------------------------------------
// 1. ROS 노드 및 Bridge 초기화
// ------------------------------------------------
ros::init(argc, argv, "fmp_waypoint");
// Unity와 통신을 담당하는 Bridge 객체 생성
flightlib::UnityBridge bridge;
bridge.initializeConnections();
// ------------------------------------------------
// 2. 드론(Quadrotor) 객체 생성 및 초기화
// ------------------------------------------------
flightlib::Quadrotor quad;
quad.setName("my_drone");
// 초기 위치 설정: (0, 0, 5) 미터 상공
// Reset 함수는 위치뿐만 아니라 속도, 가속도 등을 0으로 초기화합니다.
flightlib::QuadState initial_state;
initial_state.setZero();
initial_state.p = Eigen::Vector3f(0.0, 0.0, 5.0);
quad.reset(initial_state);
// ------------------------------------------------
// 3. Unity에 드론 등록 및 연결 대기
// ------------------------------------------------
// Bridge에게 "이 드론을 시뮬레이션에 추가해줘"라고 요청
bridge.addQuadrotor(&quad);
// Unity가 실행될 때까지 대기 (핸드셰이크)
bool unity_ready = bridge.connectUnity(quad.getID());
if (!unity_ready) {
ROS_ERROR("Unity 연결 실패! 시뮬레이터를 확인하세요.");
return -1;
}
ROS_INFO("Start Simulation... Moving to Waypoint (5, 5, 5)");
// ------------------------------------------------
// 4. 비행 루프 (100Hz)
// ------------------------------------------------
ros::Rate loop_rate(100);
// 목표 좌표 (Waypoint)
Eigen::Vector3f target_position(5.0, 5.0, 5.0);
while (ros::ok()) {
// [Step 1] 현재 드론의 상태(State) 가져오기
flightlib::QuadState current_state = quad.getState();
// [Step 2] 위치 업데이트 (Kinematic Control)
// 물리 엔진을 거치지 않고 강제로 위치를 지정하는 방식입니다.
// 실제 제어에서는 PID 제어기 등을 통해 추력(Thrust)을 계산해야 합니다.
current_state.p = target_position;
// [Step 3] 업데이트된 상태 적용
quad.setState(current_state);
// [Step 4] 시각화 요청 (Render)
// 이 함수가 호출되어야 Unity 화면상의 드론이 실제로 움직입니다.
bridge.getRender(0);
bridge.handleOutput();
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
위 코드에서 눈여겨봐야 할 부분은 Flightmare의 통신 구조입니다.
- bridge.addQuadrotor(&quad):
- C++ 코드 상의 드론 객체를 Bridge 리스트에 등록합니다. 하지만 아직 Unity 화면에는 나타나지 않습니다.
- bridge.connectUnity(quad.getID()):
- 이 시점에서 Unity와 실제 TCP 핸드셰이크가 일어납니다. Unity가 실행 중이지 않다면 여기서 타임아웃이 발생하므로, 반드시 시뮬레이터를 먼저 켜야 합니다.
- Kinematic Update:
- 위 예제는 current_state.p = target_position을 통해 좌표를 순간이동 시킵니다.
- Flightmare는 물리 연산 없이 위치만 업데이트하는 모드(Kinematic)와, 모터의 힘을 계산하는 모드(Dynamics)를 모두 지원합니다. 초기 개발 단계에서는 이처럼 Kinematic 방식으로 로직을 검증하는 것이 좋습니다.
3. 빌드 설정 (CMakeLists.txt)
ROS 패키지 방식이므로 CMakeLists.txt에 실행 파일 설정을 추가해야 합니다.
# (기존 설정 하단에 추가)
add_executable(fmp_waypoint src/fmp_waypoint.cpp)
target_link_libraries(fmp_waypoint
${catkin_LIBRARIES}
flightlib # Flightlib 라이브러리 링크 필수
)
터미널에서 빌드를 수행합니다:
cd ~/workspace/catkin_ws/flightmare_ws
catkin build myprj_flightmare_2
source devel/setup.bash
4. 실행 및 결과 (Run)
Flightmare 시뮬레이션을 실행하려면 3개의 터미널이 필요합니다.
- Terminal 1 (ROS Master): roscore
- Terminal 2 (Simulator): ./RPG_Flightmare.x86_64 (호스트 또는 컨테이너)
- Terminal 3 (Node): rosrun myprj_flightmare_2 fmp_waypoint
결과 화면: 명령어를 입력하자마자 Unity 화면 중앙에 드론이 소환되고, 우리가 지정한 (5, 5, 5) 좌표로 이동해 떠 있는 것을 확인할 수 있습니다.
5. 마치며: 하지만 문제가 생겼다?
성공적으로 드론을 띄웠지만, 코드를 수정하고 복잡한 로직을 추가하다 보니 치명적인 문제에 봉착했습니다.
Segmentation fault (core dumped)
프로그램이 실행되자마자 죽어버리거나, 알 수 없는 이유로 멈추는 현상이 발생했습니다. printf로 로그를 찍어가며 원인을 찾는 건 너무 비효율적이었습니다.
"VS Code에서 브레이크 포인트(Breakpoint)를 걸고 변수를 확인하고 싶다!"
하지만 우리는 지금 Docker 컨테이너 안에 있습니다. 호스트의 VS Code로 격리된 컨테이너 내부의 프로세스를 디버깅하는 것은 생각보다 까다로운 설정이 필요합니다.
다음 Part 4에서는 수많은 삽질 끝에 완성한 [Docker 환경에서 VS Code GDB 디버거 연동하기] 꿀팁을 대방출하겠습니다. 개발 생산성을 10배 높여주는 디버깅 환경 구축법, 기대해 주세요!
'엔지니어링 > 프로그래밍' 카테고리의 다른 글
| [Flightmare 시리즈 4] Docker 속 ROS 노드를 VS Code로 해부하기 (GDB 디버깅 완벽 가이드) (1) | 2026.01.18 |
|---|---|
| [Flightmare 시리즈 2] Docker 환경에서 C++ 프로젝트와 Unity 연동하기 (Standalone Build) (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 |
댓글