MAVSDK-Python 프로그래밍 완벽 가이드 [제10편]: 목표 도달 효율을 높이는 피드-포워드(Feed-Forward) 동시 제어
안녕하세요! 자율비행 로봇과 드론 제어 알고리즘 연구에 매진하고 계신 대학생 및 연구원 여러분. MAVSDK-Python 연재 블로그의 대망의 열 번째 시간에 오신 것을 환영합니다.
지난 **[제8편]**과 **[제9편]**에 걸쳐 우리는 특정 좌표로 기체를 이동시키는 **위치 제어(Position Control)**와, 기체의 진행 방향 및 속도를 지정하는 **속도 제어(Velocity Control)**에 대해 깊이 있게 학습했습니다. 이 두 가지 제어 방식만으로도 훌륭한 자율비행 임무를 수행할 수 있지만, 드론 레이싱이나 초정밀 3D 궤적 비행처럼 ‘동적 환경’에서 극강의 퍼포먼스를 내기에는 아쉬운 점이 존재합니다.
단순히 “어디로 가라(위치)” 혹은 “얼마의 속도로 가라(속도)”라고 개별적으로 명령하면, 제어기 내부에서는 오차를 줄이기 위한 시간이 소요되어 움직임이 굼뜨거나 목표 지점을 지나쳐버리는 오버슈트(Overshoot) 현상이 발생하기 쉽습니다.
이를 완벽하게 해결하고 목표 도달 효율을 극대화하기 위해 고급 제어 공학에서 사용하는 기법이 바로 **’피드-포워드(Feed-Forward)’**입니다.
이번 **[제10편: 목표 도달 효율을 높이는 피드-포워드(Feed-Forward) 동시 제어]**에서는 PX4 비행 제어기에서 피드-포워드가 어떻게 작동하는지 이론적 배경을 확실히 다지고, MAVSDK를 통해 위치와 속도를 동시에 제어하는 강력한 예제 코드를 한 줄씩 친절하게 뜯어보겠습니다.
1. 제어 공학의 꽃: 피드-포워드(Feed-Forward)란 무엇인가요?
학부 제어공학 수업 시간이나 논문에서 ‘피드백(Feedback)’과 ‘피드포워드(Feed-Forward)’라는 용어를 많이 접해보셨을 것입니다.
일반적인 드론의 위치 제어는 **’피드백 제어’**에 기반합니다. 즉, 목표 위치와 현재 드론의 위치를 끊임없이 비교하여 그 ‘오차(Error)’를 계산하고, 이 오차를 줄이는 방향으로 제어 신호를 만들어냅니다. 이 방식의 치명적인 단점은 **”오차가 발생해야만 비로소 기체가 반응을 시작한다”**는 것입니다. 따라서 출발할 때 가속이 느리고, 목표 지점에 다다를 때쯤 급격히 감속하느라 시간이 지연됩니다.
반면, **’피드-포워드(Feed-Forward)’**는 예상되는 시스템의 동작이나 외부에서 주어지는 명령을 미리 고려하여 제어 신호에 선제적으로 반영하는 기법입니다. 미래의 목표 상태를 미리 제어기에 알려주는 것이죠.
예를 들어, 북쪽으로 50m 떨어진 곳으로 기체를 이동시켜야 할 때, 단순히 “50m 위치로 가라(위치 제어)”라고만 하는 것이 아니라, **”50m 위치로 가되, 그 방향으로 1.0m/s의 가속/속도를 미리 내고 있어라!”**라고 명령하는 것입니다.
이렇게 피드-포워드 값을 함께 주면 드론은 오차가 커지기를 기다리지 않고, 명령에 따른 가속도를 제어 신호에 즉시 더해 더 부드럽고 신속하게 목표 속도와 위치에 도달하게 됩니다. 이는 동적 환경에서의 응답성과 정밀도를 비약적으로 향상시킵니다.
2. MAVSDK에서의 동시 제어: set_position_velocity_ned
MAVSDK에서는 이 피드-포워드 개념을 구현하기 위해 **set_position_velocity_ned()**라는 매우 직관적이고 강력한 메서드를 제공합니다.
이름에서 알 수 있듯이, 이 함수는 지난 연재에서 배웠던 두 개의 클래스, 즉 목표 위치를 나타내는 **PositionNedYaw**와 목표 속도(피드-포워드 값)를 나타내는 **VelocityNedYaw**를 매개변수로 동시에 받습니다.
코드를 통해 어떻게 구현되는지 핵심 로직을 분석해 보겠습니다.
① EKF Origin (원점) 초기화
오프보드 모드 진입 전 가장 중요한 안전장치입니다. set_position_ned 메서드를 이용하여 초기 설정점(Setpoint)을 0, 0, 0으로 잡아줍니다.
print("-- Setting initial setpoint")
await drone.offboard.set_position_ned(PositionNedYaw(0.0, 0.0, 0.0, 0.0))
② 비동기 실시간 고도 및 속도 모니터링
피드-포워드 제어가 얼마나 정밀하게 들어가는지 확인하기 위해, 텔레메트리를 이용해 기체의 현재 위치와 속도를 실시간으로 터미널에 출력하는 백그라운드 태스크를 만듭니다.
async def print_z_velocity(drone):
async for odom in drone.telemetry.position_velocity_ned():
print(f"북쪽 속도: {odom.velocity.north_m_s:.2f} m/s, 하강 속도: {odom.velocity.down_m_s:.2f} m/s")
# 파이썬 asyncio의 ensure_future를 통해 백그라운드 병렬 작업으로 실행
asyncio.ensure_future(print_z_velocity(drone))
💡 주의: 저사양 PC나 VM 환경에서는 비동기 출력이 너무 빈번하게 발생하면 부하(에러)가 생길 수 있으므로 상황에 맞게 조절하시기 바랍니다.
③ 이륙 (고도 제어 + 상승 속도 피드포워드)
이제 기체를 10m 상공으로 띄워보겠습니다. Z축 기준(Down)이므로 마이너스(-) 값을 적용해야 한다는 점 잊지 않으셨죠?
print("-- Go 0m North, 0m East, -10m Down (피드포워드 속도 적용)")
await drone.offboard.set_position_velocity_ned(
PositionNedYaw(0.0, 0.0, -10.0, 0.0), # 목표 위치: 10m 상공
VelocityNedYaw(0.0, 0.0, -1.0, 0.0) # 목표 속도(피드포워드): 위로 1.0m/s
)
await asyncio.sleep(10)
위 코드를 실행하면, 드론은 10m 고도(-10.0)라는 위치 명령과 함께, 상승 속도를 1.0m/s(-1.0)로 미리 내라는 피드포워드 명령을 동시에 받습니다. 덕분에 훨씬 기민하게 지면을 박차고 날아오릅니다.
④ 전진 비행 (위치 이동 + 전진 속도 피드포워드)
print("-- Go 50m North, 0m East, -10m Down (피드포워드 속도 적용)")
await drone.offboard.set_position_velocity_ned(
PositionNedYaw(50.0, 0.0, -10.0, 0.0), # 목표 위치: 북쪽 50m
VelocityNedYaw(1.0, 0.0, 0.0, 0.0) # 목표 속도(피드포워드): 북쪽 1.0m/s
)
await asyncio.sleep(20)
10m 고도를 유지한 채 북쪽으로 50m를 날아갑니다. 이때 북쪽 방향으로 1.0m/s의 피드-포워드 속도(VelocityNedYaw)가 더해지므로 기체는 머뭇거림 없이 목표를 향해 부드럽게 가속하며 나아갑니다.
3. 전체 실전 예제 코드 (offboard_position_velocity_ned.py)
지금까지 설명한 내용이 모두 담긴 전체 코드입니다. 여러분의 우분투 개발 환경에서 스크립트를 생성하여 직접 그 차이를 느껴보시기 바랍니다.
#!/usr/bin/env python3
import asyncio
from mavsdk import System
from mavsdk.offboard import (PositionNedYaw, VelocityNedYaw, OffboardError)
async def run():
drone = System()
await drone.connect(system_address="udp://:14540")
print("드론 연결을 기다리는 중...")
async for state in drone.core.connection_state():
if state.is_connected:
print("-- 드론 연결 성공!")
break
print("글로벌 위치 정보(GPS)가 안정화되기를 기다립니다...")
async for health in drone.telemetry.health():
if health.is_global_position_ok and health.is_home_position_ok:
print("-- 위치 신호 정상 확보 완료")
break
print("-- 모터 무장 (Arming)")
await drone.action.arm()
print("-- EKF origin 초기화 (Setting initial setpoint)")
await drone.offboard.set_position_ned(PositionNedYaw(0.0, 0.0, 0.0, 0.0))
print("-- Offboard 모드 시작")
try:
await drone.offboard.start()
except OffboardError as error:
print(f"Offboard 모드 진입 실패: {error._result.result}")
print("-- 모터 무장 해제 (Disarming)")
await drone.action.disarm()
return
# 속도 텔레메트리 출력 비동기 함수 정의
async def print_z_velocity(drone):
async for odom in drone.telemetry.position_velocity_ned():
print(f"북향 속도: {odom.velocity.north_m_s:.2f} | 하강 속도: {odom.velocity.down_m_s:.2f}")
# 백그라운드 태스크로 속도 모니터링 시작
asyncio.ensure_future(print_z_velocity(drone))
# 1. 고도 10m 상승 (피드포워드 -1.0m/s 적용)
print("-- 북 0m, 동 0m, 상공 10m(-10m Down)로 상승 (피드포워드 적용)")
await drone.offboard.set_position_velocity_ned(
PositionNedYaw(0.0, 0.0, -10.0, 0.0),
VelocityNedYaw(0.0, 0.0, -1.0, 0.0))
await asyncio.sleep(10)
# 2. 북쪽 50m 전진 (피드포워드 1.0m/s 적용)
print("-- 북 50m, 동 0m, 상공 10m 위치로 이동 (피드포워드 적용)")
await drone.offboard.set_position_velocity_ned(
PositionNedYaw(50.0, 0.0, -10.0, 0.0),
VelocityNedYaw(1.0, 0.0, 0.0, 0.0))
await asyncio.sleep(20)
print("-- 착륙 (Landing)")
await drone.action.land()
print("-- Offboard 모드 종료")
try:
await drone.offboard.stop()
except OffboardError as error:
print(f"Offboard 모드 종료 실패: {error._result.result}")
if __name__ == "__main__":
# 비동기 루프 실행
asyncio.run(run())

4. 연구원을 위한 꿀팁 및 주의사항
피드-포워드 동시 제어는 마법의 지팡이 같아 보이지만, 연구원 여러분이 반드시 명심해야 할 주의사항이 있습니다.
“시스템의 동적 특성을 철저히 이해해야 합니다.” 피드-포워드 제어를 효과적으로 사용하려면 여러분이 제어하는 드론 기체의 동적 특성(무게, 추력 한계, 관성 등)을 잘 파악하고 있어야 합니다. 만약 목표 위치까지의 거리는 짧은데 터무니없이 높은 피드포워드 속도를 입력하면, 기체는 브레이크를 제때 잡지 못하고 목표를 크게 이탈(Overshoot)하는 등 오히려 성능 저하를 초래할 수 있습니다.
따라서, 피드-포워드 값을 적용하실 때는 논문이나 연구 설계 단계에서 시스템의 동적 모델을 바탕으로 게인(Gain) 값을 신중하게 조정하는 과정이 반드시 수반되어야 합니다. 잘 조율된 피드-포워드 제어는 여러분의 자율비행 알고리즘을 한 차원 높은 수준으로 끌어올려 줄 것입니다.
마치며
이번 제10편에서는 자율비행 제어의 꽃이자, 응답성과 정밀도를 극대화하는 피드-포워드(Feed-Forward) 동시 제어 기법에 대해 상세히 알아보았습니다. 오프보드 모드 연재를 통해 위치 제어, 속도 제어, 그리고 이 둘을 융합한 고급 제어까지 모두 섭렵하신 여러분의 노고에 큰 박수를 보냅니다.
자, 이제 우리는 코드로 드론을 원하는 대로 자유자재로 움직일 수 있는 완벽한 이론을 갖추었습니다! 다음 **[제11편]**에서는 그동안 배운 지식을 총동원하여, 가장 흥미로운 실전 프로젝트인 ‘키보드 입력을 이용한 수동 기체 제어 (Manual Control)’ 프로그램을 직접 파이썬으로 개발해 보는 시간을 갖겠습니다.
마치 PC 게임을 하듯 방향키와 WASD 키보드 입력으로 드론을 직접 조종하는 짜릿한 실전 코딩이 기다리고 있으니 많은 기대 부탁드립니다. 코드를 실행하시다가 궁금한 점이나 에러가 발생한다면 언제든지 편하게 댓글을 남겨주세요. 여러분의 성공적인 연구와 완벽한 비행을 쿼드 드론연구소가 응원합니다! 감사합니다.

Author: maponarooo, CEO of QUAD Drone Lab
Date: March 5, 2026
