이전 포스팅에서는 궤적 형상 유도기법 TS (Trajectory Shaping Guidance)에 관한 논문을 리뷰하는 시간을 가졌다. 이번 포스팅에서는 2차원 평면 운동을 하는 Unicycle 모델을 적용한 초단순 무인기 시뮬레이터에 TS 유도기법을 적용한 내용을 다루어 보겠다. 코드의 전체적인 흐름은 지난번 구현한 NLPF와 매우 유사한 형태를 띈다. 하지만 시선각 각도나 목표점의 헤딩 등의 추가적인 정보를 활용하는 유도기법인 만큼 추가적은 과정이 더 필요하다. 그럼 시작해보겠다.

 

2025.09.12-[알쓸신잡 연구노트 Guidance 5편] 논문 리뷰 Trajectory Shaping Guidance Method (1/2)

 

[알쓸신잡 연구노트 Guidance 5편] 논문 리뷰 Trajectory Shaping Guidance Method (1/2)

경로 추종이란 단순히 특정 지점을 향해 가는 것과 달리 연속적인 궤적 뿐만 아니라 곡선 구간에서도 경로를 정확하고 빠르게 따르는지가 핵심이다. 이번에 리뷰할 논문은 Path Following Using Traject

piggymode.tistory.com

 

  1. 궤적 형상 유도기법
    최근에 구현한 Trajectory Shaping Guidance(TS) 모듈의 핵심은 경로상의 점과 무인기 사이의 거리가 \(R^*\)가 유지되도록 참조점을 이동시키는것인데, 이를 NLPF와 동일한 방식으로 변경하였다. 즉, 무인기와의 거리가 \(R^{*}\)거리 내에 있는 경로점 상의 점을 참조점으로 선정하는 방식으로 변경하고, 이 점을 기준으로 LOS각 \(\lambda\), 참조점의 헤딩각 \(\gamma_{t}\)를 선정하여 유도 수식에 적용하게 된다.

    -find_target함수 : 참조점 찾기
    참조점을 찾기 위한 find_target 함수에서는 NLPF에서 참조점 선정하기 위해 활용하는 방식과 동일하게 현재 위치와 경로 상 점들 사이의 거리를 계산한 뒤, Rstar 이내의 점 중 가장 선행하는 점의 인덱스를 반환한다. 루프 경로인 경우에는 인덱스를 모듈로 연산으로 처리해 경로를 끊김 없이 탐색할 수 있도록 한다. TS 논문에서는 이 과정을 무인기와 참조점 사이의 거리에 대해 아래 수식과 같이 참조점 의 속도를 조절하고, 특정 거리 \(R^*\)보다 가까이 근접한 경우 이 거리를 유지할 수 있도록 무인기의 속도와 참조점의 일치하도록 설계한다. 그러나 NLPF와 동일하게 구성해도 무방하다고 판단하였다.
    $$v_{t} = v_{a} \frac{R^*}{R}$$
    def TS_find_target(position, path, L1: float, is_loop: bool):
        P   = np.asarray(path, dtype=float)   # shape (N,2) [n,e]
        pos = np.asarray(position, dtype=float)
        N   = P.shape[0]
    
        d  = np.hypot(P[:,0] - pos[0], P[:,1] - pos[1])
        i0 = int(np.argmin(d))
    
        cand = np.flatnonzero(d <= float(L1))
        if cand.size == 0:
            return i0
    
        if not is_loop:
            fwd = cand[cand >= i0]
            target_idx = int(fwd.max()) if fwd.size else int(cand.max())
            return target_idx
    
        rel  = (cand - i0) % N
        half = N // 2
        mask = rel <= half
        if np.any(mask):
            cand2, rel2 = cand[mask], rel[mask]
        else:
            cand2, rel2 = cand, rel
    
        target_idx = int(cand2[np.argmax(rel2)])
        return target_idx


    - TS_Guidance : 궤적 형상유도기법
    TS_Guidance 함수는 UAV의 현재 상태(state)와 정해진 참조점을 입력 받아 회전각속도(ω)를 산출하는 함수이다. 무인기와의 거리가 \(R^{*}\)거리 내에 있는 참조점을 기준으로 LOS각 \(\lambda\), 참조점의 헤딩각 \(\gamma_{t}\)를 선정하여 유도 수식에 적용하게 된다. 논문에서 제시한 수식은 경로 추종을 위한 횡가속도의 수식이고 코드에서 활용되는 입력은 선회율 이므로 횡가속도와 선회율간 공식을 활용하여 계산할 수 있다.
    $$a_{a} = \frac{v_{a}}{t_{go}}(-6 \lambda + 4 \gamma_{a} + 2 \gamma_{t})$$
    $$\omega= \frac{1}{t_{go}}(-6 \lambda + 4 \gamma_{a} + 2 \gamma_{t})$$
    def TS_Guidance(state, triplet, Rstar: float, num_points: int = 600,
                     R_clip_min: float = 50.0, R_clip_max: float = 200.0):
        n, e, psi, v = map(float, state)
        psi_t, lam, R, *_ = TS_target_from_triplet(state, triplet, Rstar, num_points=num_points)
        
        R_eff = float(np.clip(R, float(R_clip_min), float(R_clip_max)))
        d_a = wrap_pi(psi - lam)   # γ_a - λ
        d_t = wrap_pi(lam - psi_t) # λ - γ_t
        
        a_lat = (v*v / R_eff) * (4.0*d_a - 2.0*d_t)
        omega = -a_lat / max(v, 1e-6)
        
        return float(omega)


이상으로 초단순 무인기 시뮬레이터와 TS 유도 법칙을 결합한 UAV 경로 추종 시뮬레이션을 구현 결과에 대해 살펴보았다. 다음 포스팅에서는 TS 유도기법의 경로 추종 시뮬레이션 결과와 유도 응답 특성에 대해 살펴보도록 하겠다. 최종 구성된 파일은 아래 깃허브 주소에 업로드하였다. 우측 상단에 초록색 code 버튼을 통해서 clone을 위한 링크를 얻거나 zip파일로 다운로드 할 수 있다. 추후에 업데이트 내용들도 해당 깃허브 레포에 업데이트할 예정이니 많은 관심을 부탁한다. 이로서 오늘의 포스팅을 마무리하겠다. 

 

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

 

 


지난 포스팅에서는 TSG 유도기법의 수식과 핵심 특징에 대해서 살펴보았다. 이번 포스팅에서는 TSG 유도기법의 선형 분석과 경로 추종시의 응답 특성에 대해 다뤄보도록 하겠다. 그럼 흥이 가라앉기 전에 서둘러 시작해보도록 하겠다.

 

2025.09.12-[알쓸신잡 연구노트 Guidance 5편] 논문 리뷰 Trajectory Shaping Guidance Method

 

[알쓸신잡 연구노트 Guidance 5편] 논문 리뷰 Trajectory Shaping Guidance Method (1/2)

경로 추종이란 단순히 특정 지점을 향해 가는 것과 달리 연속적인 궤적 뿐만 아니라 곡선 구간에서도 경로를 정확하고 빠르게 따르는지가 핵심이다. 이번에 리뷰할 논문은 Path Following Using Traject

piggymode.tistory.com

 

  1. 선형 분석
    TSG 유도기법의 응답 특성을 이해하기 위해 먼저 직선 경로 추종 상황에 대해 살펴보겠다. 차량은 가상의 참조점에 대하여 일정 거리 \(R\) 를 유지하며 추종한다. 


    참조점의 이동 속도 무인기의 속도와 같고\(v_{a} = v_{t}\), 또한 참조점은 직선 경로를 따라 움직이므로 \(\gamma_{t} = 0\)을 유지하며 무인기가 경로로부터 횡방향으로 \(d\)만큼 벗어나 있다고 하자. 이때 차량의 헤딩각을 \(\gamma_{a}\)라 하면 다음과 같이 작은 각도 근사를 활용해 단순화할 수 있다.
    $$\gamma_{t} = 0\;,\;\;\gamma_{a} \approx \frac{\dot{d}}{v_{a}}\;,\;\; \lambda \approx -\frac{d}{R^*}$$
    이때 유도법칙 수식은 다음과 같다.
    $$a_{a} = \frac{v_{a}}{t_{go}} [ -6 \lambda + 4\gamma_{a} + 2 \gamma_{t})]$$
    앞선 근시식을 대입하면
    $$-\ddot{d} \approx \frac{v^2_{a}}{R^*}(6 \frac{d}{R^*} + 4 \frac{\dot{d}}{v_a})$$
    이를 정리하면 아래와 같이 나타낼 수 있다.
    $$\ddot{d} + 4 (\frac{v_{a}}{R^*}) \dot{d} + 6 (\frac{v_a}{R^*})^2d \approx 0$$
    즉, 제안된 유도 기법은 고유진동수와 감쇠비가 아래와 같이 정의된 2차 시스템하게 작동한다.
    $$\zeta=\sqrt{\frac{2}{3}}\;,\;\;\omega_{n}=\sqrt{6} \frac{v_a}{R^*}$$
  2. 수렴 속도 - NLPF vs TSG
    제어이론에서 흔히 등장하는 표준 2차 시스템은 다음과 같이 표현할 수 있다.
    $$\ddot{x} + 2\zeta\omega_{n} \dot{x} + \omega^2_{n} x = 0$$
    여기서 \(\omega_{n}\)은 시스템의 고유진동수 (natural frequency), \(\zeta\)는 감쇠비 (damping ratio)를 의미한다. 이때 미분방정식의 해석적 일반해를 도출하면 아래와 같이 표현할 수 있다.
    $$x(t) = e^{-\zeta\omega_{n}t}[C_{1}sin(\omega_{d}t) + C_{2}cos(\omega_{d})t]\;,\;\;\omega_{d} = \omega_{n}\sqrt{1-\zeta^2}\;,\;\;0<\zeta<1$$
    이 일반해를 유심히 살펴보면 뒤에 곱해진 대괄호 내부의 수식은 계수와 삼각함수가 곱해진, 지속적인 진동을 발생시키는 부분과 앞부분에 \(e^{-\zeta\omega_{n}}\)이
    곱해진 형태로 지수감쇠(envelope)가 붙어 있는 것이 특징이다. 이 항이 바로 시간에 따라 진동의 포락선(envelope)을 줄여 결국 수렴하도록 이끌어주는 항이며, \(\zeta\omega_{n}\)이 클수록 더 빠른 감쇠(=더 빠른 수렴)가 일어나게 된다. 반대로 작으면 천천히 줄어든다. 그래서 \(\zeta\omega_{n}\)이 곧 수렴 속도를 나타내는 지표라고 말할 수 있다.
    이러러한 관점에서 살펴보게 된다면 이번 Trajectory Shaping Guidance
    논문에서 도출된 직선경로 선형 해석은 위 2차 표준형과 같은 구조를 가지며, 그때 수렴속도 지표가 다음과 같이 도출된다.
    $$\zeta\omega_{n} = 2\frac{v_{a}}{R^*}\;,\;\;( \because \omega_{n}=\sqrt{6} \frac{v_a}{R^*}\;,\;\;\zeta=\sqrt{\frac{2}{3}})$$
    반면 Pure Pursuit(혹은 NLPF로 불린 nonlinear path=following guidance)의 경우에 TSG와 동일한 2차 응답특성을 가지지만 수렴속도 지표에서 차이가 발생해 아래와 같이 나타나게 된다.
    $$\zeta\omega_{n} = \frac{v_{a}}{R^*}\;,\;\;( \because R^*=L_{1}\;,\;\; \omega_{n}=\frac{\sqrt{2}v_{a}}{L_{1}}\;,\;\;\zeta = 0.707 \approx \frac{1}{\sqrt{2}})$$
    즉, TSG는  NLPF의 지표의 두 배 큰 \(\zeta\omega_{n}\)을 가지게 된다. 이 말은 초기 횡편차나 각도오차가 같더라도 TSG가 NLPF보다 두 배 더 빠르게 에러를 줄인다는 뜻이다.

이상으로 이번 포스팅에서는 TSG의 응답특성과 수렴속도에 대해 가지는 특성을 확인해보았다. TSG의 수식은 본래 미사일 종말 유도기법 중 충돌각 제어를 위해 최적제어 문제로 접근한 연구와 유사하며 이를 항공기 유도에 적용한 점이 특히 흥미로웠다. 추후에 기회가 된다면 이 미사일 종말유도 논문도 리뷰해보도록 하겠다. 이로써 이번 포스팅을 마무리하도록 하겠다.

 


경로 추종이란 단순히 특정 지점을 향해 가는 것과 달리 연속적인 궤적 뿐만 아니라 곡선 구간에서도 경로를 정확하고 빠르게 따르는지가 핵심이다. 이번에 리뷰할 논문은 Path Following Using Trajectory Shaping Guidance 이다. 이 논문은 이런 목표를 주로 삼아 미사일 유도 이론에서 발전한 Trajectory Shaping Guidance (TSG)를 무인기 경로 추종에 적용해본 연구이다. 그럼 포스팅을 시작해보도록 하겠다.

논문 제목 : Path Following Using Trajectory Shaping Guidance

논문 저자 : Ashwini Ratnoo, Shmuel Y. Hayoun, Asaf Granot and Tal Shima

저널 정보 : Journal of Guidance, Control, and Dynamics, Vol.38, No.1, 2015

DOI         : https://doi.org/10.2514/1.G000300

 

  1. 궤적 형상 유도기법
    이 논문에서 제시하는 핵심 아이디어는 차량이 추적해야 할 가상의 참조점을 정의하고, 무인기가 해당 목표를 tail-chase 형태로 따라가도록 하는 것이다. 여기서 tail-chase란 목표의 뒤를 따라가면서 추적하는 전술 또는 기동을 말한다. Pure Pursuit Guidance와 달리 Trajectory Shaping Guidance는 도착 시점의 헤딩각(heading angle)이 0이 되도록 직접 제어함으로써 곡선 경로에서도 구조적으로 정확한 추종을 가능하게 힌다. 수식을 살펴보면 \(\lambda\)는 LOS(Line of Sight) 각도, \(\gamma_{t}\)는 목표 헤딩, (\gamma_{a}\)는 무인기의 헤딩을 나타내고 \(v_{a}\)는 무인기의 속도, \(R^*\)는 차량과 목표 사이의 최소 거리(디자인 파라미터), \(R\)은 실제 거리를 나타낼 때 기체의 횡가속도 명령을 다음과 같이 도출한다.
    $$a_{a} = \frac{v_{a}}{t_{go}} [ -6 \lambda + 4\gamma_{a} + 2 \gamma_{t})]$$


  2. 핵심 특징
    NLPF 와 같은 순수추적 기반의 유도기법은 곡선 경로를 따라갈 때 구조적으로 헤딩 오차가 생긴다. 경로의 국부 곡률 반경을 \(R_{c}\), 차량과 경로 접선 간의 정착 각도를 \(\alpha\)라 하면, 이 관계는 아래와 같이 표현할 수 있다.
    $$\alpha = sin^{-1}(\frac{R^*}{2R_{c}})$$
    즉 경로 곡률 반경 \(R_{c}\)가 작아지거나 차량과 목표 간 설계 거리 \( R^*\)가 커질수록 \(\alpha\)가 증가해, 곡선 구간에서 필연적으로 steady-state 오차가 발생한다.

    Pure Pursuit Guidance Trajectory Shaping Guidance

    Trajectory Shaping Guidance는 이 문제를 해결하기 위해 차량의 순시적으로 생성되는 원호를 추종하도록 유도명령을 생성하는 핵심 조건을 제시한다. 즉 두가지 핵심 특징을 아래와 같이 정리할 수 있다.

    - 종말단계 헤딩각 오차 소거
    TSG의 첫 번째 핵심은 차량과 목표점사이의 헤딩각 오차가 0이 되도록 유도명령을 생성한다. 유도 명령 수식을 약간 변형하면 아래와 같이 표현할 수 있다.
    $$a_{a} = \frac{v_{a}}{t_{go}} [4(\gamma_{a} - \lambda) - 2(\lambda - \gamma_{t})]$$
    즉 Pure Pursuit 유도기법과 같이 무인기의 헤딩각\(\gamma_{a}\)과 시선각 \(\lambda\)의 오차를 0으로 만드는것 뿐만이 아닌 목표점이 향하고 있는 방향(\gamma_{t}\)까지 고려한 각도 조합이 된다. 이걸 통해 차량은 가상 표적의 진행 방향(경로 접선)에 맞게 회전하도록 횡가속도를 받는다.


    - 순시적 원호 추종 가속도 생성
    위의 전개에서 연결되는 내용이다. TSG의 두 번째 핵심은 무인기와 목표점(가상 표적)이 만들어 내는 순시적 원(curvature circle)을 추종하기 위한 가속도 명령이 자연스럽게 도출된다는 점이 되겠다. TSG의 유도 명령에서 \(t_{go}\) 는 가상 표적까지 도달하는데 소요될 시간을 의미하며 수식적으로는 아래와 같이 근사할 수 있다.
    $$t_{go} = \frac{R^*}{v_{a}}$$
    이를 유도 명령에 대입하면 아래의 수식으로 변경할 수 있다. 
    $$a_{a} = \frac{v^2_{a}}{R^*} [4(\gamma_{a} - \lambda) - 2(\lambda - \gamma_{t})]$$
    위의 그림에서 나타나듯 무인기와 목표점의 위치, 헤딩각을 고려한 순시적인 원호를 그리면  \(\gamma_{a} - \lambda\) ,  \(\lambda - \gamma_{t})\)가 \(\alpha\) 로 동일해지는것을 확인할 수 있다. 이를 수식에 대입하면 왼쪽 식과 같이 나타낼 수 있으며 기하학적 관계를 이용해 오른쪽 수식과 같이 나타낼 수 있다.
    $$a_{a} = \frac{v^2_{a}}{R^*} (2\alpha) \;,\;\;_{a} = \frac{v^2_{a}}{2 R_{c} sin(\alpha)} (2\alpha)$$
    이때 \(R^* << R_{c}\) 인 경우엔 \(sin^{-1}\) 부분이 사라지고 아래 수식만 남게 된다.
    $$a_{a} = \frac{v^2_{a}}{R_{c}}$$
    이는 반경이 \(R_{c}\) 인 원호를 따르기 위한 가속도의 크기가 된다. 즉, 이 유도기법의 횡가속도 명령은 반경이 \(R_{c}\)인 원을 따르기 위한 값으로 도출됨을 의미한다. 이것이 TSG가 무인기와 목표의 위치와 헤딩각을 고려한 원호를 따라가도록 유도명령을 만들어내는 원리가 되겠다.

이번 포스팅에서는 TSG 유도기법의 수식과 핵심 특징에 대해서 살펴보았다. 원래의 계획대로라면 직선 경로 및 원형 경로 추종시의 선형 분석도 함께 포스팅에서 다루려고 하였으나 흥미로운 점이 많은 논문인 만큼 논문의 난이도가 매우 높다,,, 또한 분량도 과해질것같아 다음 포스팅으로 나누어 기술해보도록 하겠다.


제어 시스템을 설계할 때 우리는 입력에 대한 출력의 시간/주파수 응답이 어떻게 바뀌는가를 빠르게 판단해야 한다. 전달함수는 선형 시불변(LTI) 시스템을 다루고 분석하기 위해 널리쓰이는 도구이며, 폐루프 안정성, 과도응답, 대역폭, 감쇠비 등의 핵심 지표을 활용해 시스템의 특성을 체계적으로 분석할 수 있도록 해 준다. 본 포스팅은 새로 구현한 간단한 전달함수 모듈을 소개하고, 선형 분석의 의미와 함께 각 함수의 역할, 그리고 실제 사용 예시를 정리한다. 

 

  1. 선형 해석
    선형 해석이란 시스템의 입력과 출력의 관계를 분석하는 방법이다. 선형이라는 특성 덕분에 복잡한 물리 현상을 단순한 수학적 식으로 다룰 수 있으며, 작은 블록 단위로 나누어 결합해도 일관된 해석이 가능하다. 시간 영역에서는 주로 계단 입력이나 임펄스 입력을 적용해 시스템의 응답을 살펴본다. 이를 통해 정착 시간, 오버슈트, 정상상태 값과 같은 성능 지표를 빠르게 확인할 수 있다. 주파수 영역에서는 보드 선도나 루트로커스, 나이퀴스트 선도를 활용하여 안정성의 여유도, 잡음 억제 능력, 대역폭 등을 평가한다. 결국 선형 해석은 시스템이 얼마나 안정적인지, 얼마나 빠르고 정확하게 목표값을 따라갈 수 있는지를 예측하는 기본 도구이다. 제어 이론에서 가장 먼저 배우는 이유도 여기에 있으며, 이후 비선형 시스템이나 복잡한 제어 기법을 다룰 때에도 직관을 제공하는 출발점이 된다.

  2. 전달함수
    전달함수는 제어이론에서 선형 해석을 위해 시스템을 표현하는 가장 기본적인 도구이다. 전달함수란 선형 시불변 시스템에서 입력과 출력의 관계를 가장 간단하고 직관적으로 나타내는 표현이다. 시간 영역에서 실제 물리 시스템은 보통 시간에 따라 변하는 복잡한 미분방정식으로 설명되는데, 이런 방정식 형태는 해석이 어렵고 계산이 복잡하다.  그래서 제어공학에서는 라플라스 변환이라는 방법을 이용해 미분방정식을 보다 단순한 분수 형태로 바꾼다. 이렇게 변환된 식에서 입력과 출력의 비율을 정의한 것이 바로 전달함수이다.


    아래는 일반적인 질량-스프링-댐퍼 시스템을 나타낸다. 이 수식을 볼때 차량의 쇽옵서버를 연상하면 이해에 도움이 되는데, 질량 m, 스프링 k, 댐퍼 c가 달린 시스템에 가속방지턱이나 포트홀과 같은 어떠한 힘이 가진되었을때, 이 시스템(차량)의 변위가 어떻게 변할것인가를 표현한 수식이 되겠다.
    $$m\ddot{x}(t) + c\dot{x}(t) + kx(t)=F(t)$$
    이를 전달함수꼴로 바꾸게 된다면 아래와 같다.
    $$G(s) = \frac{X(s)}{F(s)} = \frac{1}{ms^2+cs+k}$$
    앞선 전달함수를 보며 알아챌 수 있겠지만 전달함수로 바꾸어 표현하면 가장 큰 장점은 시스템을 단순한 수학적 비율식으로 다룰 수 있다는 점이다. 시간 영역에서 미분방정식으로 직접 해석할 때는 복잡한 계산이 필요하지만, 전달함수를 이용하면 입력과 출력의 관계가 분수 꼴로 정리되어 훨씬 직관적으로 접근할 수 있다. 특히 분모 다항식에 포함된 계수들은 시스템의 극을 결정하며, 이 극의 위치는 안정성, 응답 속도, 진동 특성 등을 이해하는 데 핵심적인 역할을 한다. 따라서 전달함수는 단순히 수식을 바꿔 놓은 것이 아니라, 시스템의 본질적인 성질을 파악할 수 있게 해주는 중요한 도구라고 할 수 있다.

  3. 전달함수 유틸리티
    - tf 함수 : 전달함수 생성
    tf 함수는 분자와 분모 계수를 입력받아 이를 하나의 전달함수로 정의해주며, 이 과정을 통해 수학적으로만 존재하던 시스템을 코드 상에서 다룰 수 있는 객체로 변환한다. 
    def tf(num, den):
        num = np.atleast_1d(np.asarray(num, float))
        den = np.atleast_1d(np.asarray(den, float))
        if den[0] == 0:
            raise ValueError("Leading denominator coefficient cannot be zero.")
        # 정규화(den[0]=1)
        num = num / den[0]
        den = den / den[0]
        return signal.TransferFunction(num, den)

    - series, parallel, feedback 함수 : 전달함수의 수학적 처리
    series와 parallel 함수는 시스템 전달함수를 더하거나 곱하는 기능을 제공한다. 이는 제어 블록도를 수학적으로 단순화하는 과정과 같으며, 여러 부분 시스템을 하나의 전달함수로 합쳐 표현할 수 있게 한다. 
    feedback 함수는 제어 이론에서 가장 널리 쓰이는 폐루프 구조를 구현한다. 음의 피드백을 기본으로 하여 시스템 안정성을 높이고 원하는 동작을 얻는 형태를 코드로 쉽게 재현할 수 있다.
    def series(G, H):
        """G*H"""
        Ng, Dg = _nd(G); Nh, Dh = _nd(H)
        return tf(np.polymul(Ng, Nh), np.polymul(Dg, Dh))
    
    def parallel(G, H):
        """G+H"""
        Ng, Dg = _nd(G); Nh, Dh = _nd(H)
        num = np.polyadd(np.polymul(Ng, Dh), np.polymul(Nh, Dg))
        den = np.polymul(Dg, Dh)
        return tf(num, den)
    
    def feedback(G, H=None, sign=-1):
        """Closed loop TF """
        Ng, Dg = _nd(G)
        if H is None:
            Nh, Dh = np.array([1.0]), np.array([1.0])
        else:
            Nh, Dh = _nd(H)
        num = np.polymul(Ng, Dh)                          # Ng*Dh
        den = np.polyadd(np.polymul(Dg, Dh),              # Dg*Dh
                         sign * np.polymul(Ng, Nh))       # + sign*Ng*Nh
        return tf(num, den)

    - step, lsim 함수 : 시뮬레이션 수행
    step 함수 : 단위 계단 입력에 대한 응답을 구해, 시스템의 안정성과 응답 특성을 파악할 수 있게 한다. mag인자를 활용해 계단입력의 진폭을 설정할 수 있다.

    ramp 함수 : 시간이 지남에 따라 선형적으로 증가하는 램프 입력을 적용한다. 시스템의 정상상태 오차나 추종 특성을 분석하는 데 활용되며, slope 인자로 기울기를 조절할 수 있다. 

    parabolic 함수 : 입력이 시간의 제곱에 비례하여 증가하는 포물선 입력을 적용한다. 가속 운동과 같은 상황을 모사할 수 있으며, accel 인자를 통해 가속도의 크기를 설정할 수 있다.

    impulse 함수 : 순간적인 외란(임펄스 입력)에 대한 응답을 계산한다. 시스템의 고유진동수와 감쇠 특성을 파악하는 데 유용하며, mag 인자를 통해 임펄스의 크기를 조절할 수 있다.

    lsim 함수는 보다 사용자가 지정한 입력에 대해 시스템 응답을 계산하는 기능을 제공한다. 즉, 실제 동작 환경을 가정한 시뮬레이션이 가능하다.

    def step(G, T=None, mag=1.0):
        if T is None:
            T = np.linspace(0, 10, 1001)
        u = mag * (T*0+1)
        tout, y = signal.step(G, T=T)
        return u, tout, y
    
    def ramp(G, T=None, slope=1.0):
        if T is None:
            T = np.linspace(0, 10, 1001)
        u = slope * T
        tout, y, _ = lsim(G, u, T)
        return u, tout, y
    
    def parabolic(G, T=None, accel=1.0):
        if T is None:
            T = np.linspace(0, 10, 1001)  # 기본 시간 벡터
        u = 0.5 * accel * T**2
        tout, y, _ = lsim(G, u, T)
        return u, tout, y
    
    def impulse(G, T=None):
        if T is None:
            T = np.linspace(0, 10, 1001)
        
        dt = T[1] - T[0] if len(T) > 1 else 1.0
        u = np.zeros_like(T)
        u[0] = mag / dt
    
        tout, y = signal.impulse(G, T=T)
        return u, tout, y
    
    def lsim(G, u, t):
        sys_ss = signal.StateSpace(*signal.tf2ss(*_nd(G)))
        tout, y, x = signal.lsim(sys_ss, U=np.asarray(u, float), T=np.asarray(t, float))
        return tout, y, x

    - bode 함수 : 주파수 응답 해석
    bode 함수는 주파수 영역에서 시스템의 크기와 위상 특성을 나타내는 중요한 도구이다. 제어 시스템은 주파수 대역에 따라 다른 성능을 보이는데, 이를 시각적으로 확인할 수 있는 가장 대표적인 방법이 Bode 선도이다. 크기 응답은 이득 특성을, 위상 응답은 지연 특성을 보여주며, 안정성과 성능을 동시에 평가할 수 있다.
    def bode(G, w):
        w, mag, phase = signal.bode(G, w=w)
        return w, mag, phase


  4. 사용 예시
    아래는 간단한 2차 시스템을 구성하여 각 기능을 사용해본 결과를 나타낸다. 사용한 코드는 아래와 같다.
    if __name__ == "__main__":
        wn, zeta = 2.0, 0.7
        G = tf([wn**2], [1, 2*zeta*wn, wn**2])
        
        # Step 응답
        t = np.linspace(0,6, 1000)
        u, t, y = step(G, T=t, mag=1.0)
        plt.figure(); plt.plot(t, y); plt.plot(t, u, 'k--', label="step input")
        plt.title("Step response"); plt.xlabel("s"); plt.grid(True)
        
        # Ramp 응답
        u, t, y = ramp(G, T=t, slope=1.0)
        plt.figure(); plt.plot(t, y, label="y"); plt.plot(t, u, 'k--', label="ramp input")
        plt.title("Ramp response"); plt.xlabel("s"); plt.legend(); plt.grid(True)
        
        # Parabolic 응답
        u, t, y = parabolic(G, T=t, accel=1.0)
        plt.figure(); plt.plot(t, y, label="y"); plt.plot(t, u, 'k--', label="para input")
        plt.title("Parabolic response"); plt.xlabel("s"); plt.legend(); plt.grid(True)
        
        # Bode
        w = np.logspace(-2, 2, 500)
        w, mag, phase = bode(G, w)
        fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(6, 6))
        ax1.semilogx(w, mag); ax1.set_ylabel("Magnitude [dB]"); ax1.set_title("Bode Plot"); ax1.grid(True, which="both")
        ax2.semilogx(w, phase); ax2.set_ylabel("Phase [deg]"); ax2.set_xlabel("Frequency [rad/s]"); ax2.grid(True, which="both")
    
        # 임의 입력 (사인)
        t = np.linspace(0, 10, 2001)
        u = np.sin(2*np.pi*0.8*t)
        tout, y, _ = lsim(G, u, t)
        plt.figure(); plt.plot(t, u, label="u"); plt.plot(tout, y, label="y"); plt.legend(); plt.grid(True)
        plt.show()


    - 첫 번째 그림은 단위 계단 입력에 대한 시스템의 응답이다. 감쇠비가 0.7, 고유진동수가 2 rad/s인 2차 시스템은 비교적 빠른 상승과 함께 큰 오버슈트 없이 안정적으로 정착하는 모습을 보인다. 이는 감쇠비가 충분히 커서 진동이 심하지 않고, 동시에 응답 속도도 적당히 빠른 전형적인 안정된 2차 시스템의 특성이라 할 수 있다.
    두 번째 그림은 램프 입력에 대한 응답이다. 출력은 입력을 따라가지만 일정한 정상상태 오차가 발생하며, 입력 기울기(slope)가 커질수록 오차도 커진다.
    세 번째 그림은 포물선 입력에 대한 응답이다. 시간이 지남에 따라 입력이 가속적으로 증가하기 때문에 출력은 더 큰 정상상태 오차를 보이게 된다. 이는 시스템의 추종 한계를 보여주는 예라 할 수 있다.

    - 네 번째 그림은 주파수 영역에서의 시스템 특성을 나타낸다. 위쪽은 크기 응답으로, 저주파 대역에서는 약 0 dB 부근의 평탄한 응답을 보이다가, 고유진동수 이후에는 기울기가 점차 낮아지는 특성이 나타난다. 아래쪽은 위상 응답으로, 저주파에서는 거의 0° 부근에서 시작해 고주파로 갈수록 -180°에 가까워진다. 이는 2차 시스템의 전형적인 위상 지연 특성을 잘 보여준다.

    - 다섯 번째 그림은 0.8 Hz(약 5 rad/s)의 사인 입력에 대한 출력 응답이다. 입력과 출력의 파형을 비교해 보면, 출력은 입력과 같은 주파수를 유지하면서도 크기가 줄어들고 위상이 지연되어 있음을 확인할 수 있다. 이는 시스템의 전달 특성이 주파수에 따라 달라지며, 해당 주파수 대역에서 이득이 1보다 작고 위상 지연이 발생한다는 것을 의미한다.

이번 포스팅에서는 전달함수를 정의하고, 이에 대한 시스템 응답 및 주파수 해석 결과를 도출하기 위한 코드를 구현해보았다. 다음 포스팅에서는 이를 활용하여 각 유도기법의 응답 특성에 대해 조금더 파고드는 시간을 가지도록 하겠다. 이것으로 이번 포스팅을 마무리하도록 하겠다.

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

 

 

 

지난번 포스팅에서는 Track Guidance(TG) 관련 논문을 소개하는 시간을 가졌다. 이를 통해 TG 유도기법의 수식을 전개해보고 핵심 특징들에 대해 알아보았다. 이번 포스팅은 지난번 다뤘던 TG 를 좀 더 깊게 깊게 들어가서 튜닝 파라미터(κ, Kp)에 따른 응답 특성이 어떻게 변하는지 그리고 직선 경로와 원형 경로에서 각각 어떤 현상이 나타나는지 살펴보겠다.

 

2025.08.24-[알쓸신잡 연구노트 Guidance 3편] 논문 리뷰 Track Guidance Method

 

[알쓸신잡 연구노트 Guidance 3편] 논문 리뷰 Track Guidance Method

무인기의 경로 추종 문제에서 안정적이고 예측 가능한 방식으로 목표 경로에 접근하도록 하는것은 매우 중요한 문제이다. 이러한 상황에서 경로 추종 응답 특성을 설계할 수 있다는 점은 매우

piggymode.tistory.com

 

  1. 직선 경로 응답 특징
    첫번째로 다룰 내용은 직선 경로를 추종하는 경우이다. 파라미터를 조종해가며 응답 특성 변화에 대해 알아보도록 하겠다.
    - κ (kappa): 유도 응답 상수
    이는 시간 영역에서의 1차 지연 시스템의 시정수(time constant)에 대응되는 개념이다. UAV가 목표 궤적에 얼마나 빠르게 수렴할지를 결정하며, κ가 작을수록 빠르게 수렴하지만 진동이나 과도한 조향이 발생할 수 있고, κ가 크면 더 완만하게 경로에 수렴한다. x = κ일 때 : 최종 목표 오프셋의 63.2%에 도달,  x = 3κ일 때 : 약 95% 수렴,  x = 4κ 이상 : 오차 2% 이하 수렴하는 1차 시스템의 특징은 다음과 같음을 상기시키며 시뮬레이션 결과를 살펴보도록 한다.
    아래 결과는 \(\kappa\)를 [100, 200, 400, 800] 으로 설정한 직선 경로 추종 시뮬레이션 결과이다, 초기 시작 위치는 직선에서 남쪽 방향으로 2000m떨어진 지점이 되겠다. 따라서 각 설정된 (\kappa\)에 따른 east 위치에 도달할때 이의 63.2%인 대략 1260, 즉 -740m의 위치에 도달하게 된다. 예를 들어 (\kappa\)가 200인 경우 east 방향으로 200m 이동하는 시점에 north의 위치가 -740m에 도달하게 된다.

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.



    - Kp: 선회율 명령에 대한 비례 이득
    κ가 원하는 공간 응답 특성을 만든다면, Kp는 이를 실제 yaw-rate(선회율) 명령으로 변환하는 비례 이득이다 너무 크면 기체가 과도하게 기동하고, 너무 작으면 추종 오차를 소거하는데 큰 지연이 발생하는 문제가 발생한다. 아래 시뮬레이션 결과는 \(\kappa\)는 400으로 설정하고 \(K_{p}\)를 [0.000005, 0.00005, 0.0005, 0.005]로 설정한 결과이다. 시뮬레이션 결과 응답 특성에 대한 변화는 크지 않으며, 발생한 오차를 빠르게 소거하느냐 느리게 소거하느냐의 차이로 나타나는것을 확인할 수 있다. 일반적인 TG는 1차 지연 시스템과 유사하게 동작하므로 Overshoot이 존재하지 않는다. 하지만 \(K_{p}\)가 특정 이상 낮게 설정되는 경우 오차를 소거하기 위한 비례 이득이 부족하여 Overshoot가 발생하는것을 확인할 수 있다.

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.




  2. 원선회 경로 응답 특성
    앞선 단원에서 설명한 것과 같이 TG는 공간 기반의 1차 지연 시스템처럼 동작하는 유도 기법이다. 직선 경로에서는 κ라는 공간 시정수에 따라 지수적으로 수렴하여 오차를 줄이는 특성이 잘 맞아떨어진다. 그러나 원형 경로에서는 곡률을 지속적으로 따라야 하므로, 적분항이 없는 단순 비례 제어 구조에서는 항상 작은 steady-state error가 발생한다. 따라서 기체가 원을 완전히 타지 못하고 약간 밀려서 추종하는 현상이 나타난다. 이를 개선하려면 단순 튜닝이 아니라 적분항이나 곡률 보상을 추가하는 방식이 필요하다.

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

이번 글에서는 Track Guidance의 구조와 그에 따른 응답 특성을 살펴보았다. 분석 결과, κ는 공간 상의 수렴 속도를 결정하는 핵심 인자이며, Kp는 실제 조향 반응을 조절하는 요소임을 확인할 수 있었다. 직선 경로에서는 지수적 수렴 특성 덕분에 비교적 깔끔한 추종이 가능하지만, 원형 경로에서는 적분항이 없어 작은 steady-state error가 남는 구조적 한계가 드러났다. 이는 단순성과 직관성을 갖춘 기법이지만, 원형이나 곡률이 큰 경로에서는 보상 메커니즘의 보완이 필요함을 의미한다. 이러한 이해를 바탕으로 상황에 맞는 유도 기법을 선택하거나 확장한다면 더욱 안정적인 경로 추종이 가능하겠다. 이로써 이번 포스팅을 마무리하도록 하겠다.

 

 

 

각종 무인기 운용에 쓰이는 유도기법들과 항로점 속성들을 추가하다보니 지난번 소개한 시뮬레이터와는 상당히 개선된 결과물이 탄생하였다. 이번 포스팅에서는 매뉴얼 작성하듯 추가/개선된 내용들을 설명하는 시간을 가져보겠다.추가된 내용은 아래와 같다.

- main 코드에서 무인기의 항로점(line, hold, racetrack)을 정의할 수 있도록 추가

- 무인기 단독/다중 비행 임무 수행할 수 있도록 무인기 대수 및 개별 유도기법 지정 기능 추가

- 애니메이션 모듈 개선하여 지정된 항로점 및 무인기 위치/트레일을 나타내는 기능 추가 

 

  1. 시뮬레이터가 수행하는 일
    단독 혹은 다수 무인기의 초기 상태, 각자의 미션(직선, 원·장주 선회)을 시간 흐름에 맞춰 생성한다. 이후 사용자가 지정하는 유도기법(Nonlinear Path-Following or Track Guidance)을 적용한 유니사이클 모델로 시뮬레이션을 수행한다. 추후에 시뮬레이션을 통해 얻은 상태 정보를 활용해 애니메이션 혹은 그래프로 가시화하는것이 이 코드의 핵심 기능이다. 이를 통해 다양한 유도기법을 구현해보고 이를 통해 각 유도기법의 각기 다른 응답 특성을 음미하는것이 이 코드의 목적이다.

  2. 시뮬레이터의 개요
    시뮬레이터의 메인이 되는 UAV_Sim_main_v2p00.py를 살펴보면 주석을 활용하여 코드를 5가지의 구간으로 나눠놓은것을 확인할 수 있을것이다. 각 구간은 각기 다른 기능을 가지며 사용자의 목적에 맞는 시뮬레이션을 수행하기 위하여 수정해야할 부분이 존재한다.
    - Initial State
    시뮬레이션에 참여할 무인기 대수와 각 기체의 시작 상태(북/동 위치, 초기 헤딩, 속도)를 정한다.
    - Mission Planning
    HOLD/LINE/RACETRACK 같이 튜플 형태의 항로점을 정의하고, 시뮬레이션 중 경로를 순차 생성·연결한다.
    - Guidance Method
    지정된 무인기별로 TG 또는 NLPF 유도기법을 선택한다.
    - Simulation
    선택한 유도기로 조향 명령을 만들며 등속 유니사이클 모델에 대한 오일러법 혹은 룬게쿠타법 수치적분을 수행하여 시뮬레이션을 수행한다.
    - Visualize
    결과를 애니메이션 혹은 정지 그래프로 시각화하며 필요에 따라 비디오 파일로 저장한다.

  3. 시뮬레이션 설정 방법
    1) 초기값 지정(Initial State)
    초기값 지정 구간에서는 시뮬레이션에 참여할 무인기의 대수를 지정하고 각 무인기의 초기 상태를 정의한다. 무인기의 부호 규약 및 좌표계는 아래와 같으며 상태는 [N, E, \(\psi\), \(V_{g}\)] 로 정의하도록 한다.
      - 좌표계는 NED좌표계를 사용하며 단의는 [m] 이다.
      - 헤딩은 북쪽 (y축 +방향)을 기준 시계방향으로 +부호를 가진다. 단위는 [rad]이다.
      - 속도 V는 [m/s]로 정의한다.
        # =========================
        # Initial State
        # =========================
        num_uav = 4
        x0_list = [
            np.array([-800.0, -400.0, np.deg2rad( 20.0), 40.0]),    # UAV0
            np.array([-500.0,  200.0, np.deg2rad(-10.0), 40.0]),    # UAV1
            np.array([ 300.0, -700.0, np.deg2rad( 45.0), 40.0]),    # UAV2
            np.array([ 300.0, -700.0, np.deg2rad( 45.0), 40.0]),    # UAV3
        ]


    2) 미션 기획(항로점/경로 설정, Mission Planning)
    다음은 무인기가 비행할 항로점을 정의하기 위한 구간이다. 항로점은 튜플 형태로 3가지의 항로점 속성 중에서 선택하고 이를 유지할 시간(duration)을 지정하여 정의하도록 되며 비행 임무는 이 항로점 튜플들의 시퀀스로 구성한다. 즉, 각 항로점의 속성을 참고하여 중괄호 안에 나열하는 식으로 항로점의 종류와 순서를 지정할 수 있다. 시뮬레이션을 시작하면 원과 장주선회 항로점 속성에는 duration 속성, 직선 항로점 속성에는 끝점과의 거리를 활용하여 항로점 임무 수행이 완료되었는지를 판단하고, 이를 기반으로 mission_planner_step함수에서 매 프레임마다 현재 미션의 prev / cur / next 항로점을 계산하여 Guidance부로 넘겨준다. 이렇게 prev / cur / next  항로점의 3점을 지정하는 이유는 무인기가 비행해야하는 기하를 정의하기 위함인데 prev와 cur 항로점을 활용하여 현재 경로의 기하를 정의할 수 있다. 즉, 두 점이 만드는 진행축이 곧 목표 헤딩(\(\psi_{d}\))과 크로스 트랙 오차(\(e_{y}\))의 기준이 된다. 또한 next 항로점을 추가하게 되면 코너에서 cur 항로점에서 next 항로점으로 넘어거는 코너에서 호를 따는 등 스무딩을 붙여 방향·곡률의 연속성을 확보하는데에 사용할 수 있다.

    - HOLD: ("HOLD", np, ep radius, direction, duration_s)
        중심 (np, ep)에서 반지름 radius로 선회
        direction: +1=시계, -1=반시계
        duration 초 동안 선회 비행 유지
    - LINE: ("LINE", np, ep, mode)
        직선 선회 비행의 끝점, 시작점은 prev 항로점의 위치로 지정
        직선 비행 모드, FLY_OVER(직선의 끝점을 찍고 다음 항로점 진행)/FLY_BY(직선의 끝점 도달하기 전에 다음 항로점 진행)
    - RACETRACK: ("RACETRACK", np, ep length, width, bearing_deg, direction, laps)
        중심 (np, ep)을 기준으로 장주(달걀형) 선회
        긴축 length, 짧은축 width, 축 방향 bearing_deg(도)
        direction(±1), 수행 laps 회
        # =========================
        # Mission Planning
        # =========================
        # Description
        # HOLD      (n, e, radius, direction(+1/-1), duration[s])
        # LINE      (n, e, mode="FLY_OVER"/"FLY_BY")
        # RACETRACK (n, e, length, width, bearing[deg], direction(+1/-1), laps)
        m0 = [("HOLD", 0.0, 0.0, 400.0, -1, 30.0),
              ("LINE", 1200.0, 800.0, "FLY_OVER"),
              ("HOLD", 800.0, -600.0, 300.0, +1, 30.0),
              ("RACETRACK", 1500.0, -500.0, 250.0, 1800.0, -20.0, -1, 3),
              ("LINE", 400.0, -600.0, "FLY_BY")]
        m1 = [("RACETRACK", 1500.0, -500.0, 250.0, 1800.0, -20.0, +1, 3),
              ("HOLD", 0.0, 0.0, 400.0, -1, 60.0),
              ("LINE", 1200.0, 800.0, "FLY_OVER"),
              ("HOLD", 400.0, -600.0, 300.0, +1, 120.0)]
        m2 = [("HOLD", 800.0, -600.0, 300.0, +1, 120.0),
              ("LINE", 00.0, 00.0, "FLY_BY"),
              ("HOLD", 0.0, 0.0, 400.0, -1, 60.0),
              ("RACETRACK", 1500.0, -500.0, 250.0, 1800.0, -20.0, -1, 3),
              ("LINE", 1200.0, 800.0, "FLY_OVER"),]
        m3 = [("HOLD", 0.0, 0.0, 400.0, -1, 60.0),
              ("RACETRACK", 1500.0, -500.0, 250.0, 1800.0, -20.0, +1, 3),
              ("LINE", 1200.0, 800.0, "FLY_OVER"),
              ("RACETRACK", 1500.0, -500.0, 250.0, 1800.0, -20.0, +1, 3),
              ("LINE", 400.0, -600.0, "FLY_BY"),
              ("HOLD", 400.0, -600.0, 300.0, +1, 120.0)]
        
        missions = [m0, m1, m2, m3]
        mission_states = [None for _ in range(num_uav)] 
        guidance_times = [0.0 for _ in range(num_uav)]
        WP_save = [None for _ in range(num_uav)] 
        LEG_HIST     = [[] for _ in range(num_uav)]   # 프레임별 현재 레그 기록
        PATH_BY_LEG  = [{} for _ in range(num_uav)]


    3) 유도기법 설정과 튜닝(Guidance Method)
    이 부분에서 우리가 구현할 유도거법을 적용하고 시험하기 위해 조절해야하는 부분이 되겠다. 현재는 NLPF 그리고 TG 유도기법이 설계되어 있으며 추후에 유도기법의 구현이 완료되면서 추가될 예정이다. 지정된 유도기법에 앞서 지정된 항로점 3점을 정의하게 되면 각 유도기법에서 필요한 작업을 수행하게 된다. 또한 지정된 3 항로점을 저장하여 추후 가시화 작업에서 목표 경로를 도식하는데 활용한다.

    - NLPF
    > 유도기법 튜닝 파라미터 : \(L_{1}\) (크면 부드럽고 이탈 적음, 너무 크면 둔해짐)
    > 항로점 3점을 활용하여 북-동 좌표(N, E)의 촘촘한 점의 경로점으로 재구성한다. 이 작업이 필요한 이유는 NLPF 유도가 경로상의 점들과 무인기의 위치에서 \(L_{1}\) 에 있는 점을 선행 목표점으로 지정한다. 이후 이 선행 목표점과 속도벡터의 상대 각도를 구한 후 유도명령 \(\omega\)를 도출한다.
        

    - TG
    > 유도기법 :튜닝
     파라미터: \(K_{p}\) ,  \(\kappa\)
    > 항로점 3점을 활용하여 기하를 판정한 뒤, 그 기하 위에서 추종 목표점과 목표 헤딩(\(\psi_{d}\))을 정하고, 그에 따른 크로스 트랙 오차(\(e_{y}\))를 계산해유도명령 \(\omega\)를 도출한다.
        # =========================
        # Guidance Method
        # =========================
        def Guidance_Method_0(state, idx=0):
            (triplet, meta), mission_states[idx] = mission_planner_step(t=guidance_times[idx],pos_NE=state[:2],psi=state[2],mission_state=mission_states[idx],mission_spec=missions[idx])
            omega = TG_Guidance(state, triplet, Kp=0.0002, K=100)
            guidance_times[idx] += ctrl_dt
            leg = int(meta["leg_idx"]); LEG_HIST[idx].append(leg)
            if leg not in PATH_BY_LEG[idx]:
                P, _ = build_viz_path_from_triplet(triplet, state=state, num_points=600)
                PATH_BY_LEG[idx][leg] = P
            return float(np.clip(omega, -1.0, 1.0))
    
        def Guidance_Method_1(state, idx=1):
            (triplet, meta), mission_states[idx] = mission_planner_step(t=guidance_times[idx],pos_NE=state[:2],psi=state[2],mission_state=mission_states[idx],mission_spec=missions[idx])
            omega = NLPF_Guidance(state, triplet, L1=200.0, num_points=600)
            guidance_times[idx] += ctrl_dt
            leg = int(meta["leg_idx"]); LEG_HIST[idx].append(leg)
            if leg not in PATH_BY_LEG[idx]:
                P, _ = build_viz_path_from_triplet(triplet, state=state, num_points=600)
                PATH_BY_LEG[idx][leg] = P
            return float(np.clip(omega, -1.0, 1.0))
    
        def Guidance_Method_2(state, idx=2):
            (triplet, meta), mission_states[idx] = mission_planner_step(t=guidance_times[idx],pos_NE=state[:2],psi=state[2],mission_state=mission_states[idx],mission_spec=missions[idx])
            omega = TG_Guidance(state, triplet, Kp=0.0002, K=100)
            guidance_times[idx] += ctrl_dt
            leg = int(meta["leg_idx"]); LEG_HIST[idx].append(leg)
            if leg not in PATH_BY_LEG[idx]:
                P, _ = build_viz_path_from_triplet(triplet, state=state, num_points=600)
                PATH_BY_LEG[idx][leg] = P
            return float(np.clip(omega, -1.0, 1.0))
    
        def Guidance_Method_3(state, idx=3):
            (triplet, meta), mission_states[idx] = mission_planner_step(t=guidance_times[idx],pos_NE=state[:2],psi=state[2],mission_state=mission_states[idx],mission_spec=missions[idx])
            omega = NLPF_Guidance(state, triplet, L1=200.0, num_points=600)
            guidance_times[idx] += ctrl_dt
            leg = int(meta["leg_idx"]); LEG_HIST[idx].append(leg)
            if leg not in PATH_BY_LEG[idx]:
                P, _ = build_viz_path_from_triplet(triplet, state=state, num_points=600)
                PATH_BY_LEG[idx][leg] = P
            return float(np.clip(omega, -1.0, 1.0))


    4) 시뮬레이션 수행 및 가시화 (Simulation & Visualize) 
    시뮬레이션 & 가시화 구간은 시뮬레이션을 돌리고 가시화해 보여주는 파이프라인을 담당한다. 각 무인기는 지정된 유도기법을 ctrl_dt 주기로 호출해 선회율 유도명령을 만든다. 모델은 sim_dt 간격으로 수치적분하여 시뮬레이션을 수행한다. 그 결과 시간 T, 상태 X, 입력 U를 누적할 수 있다.
    적분이 끝나면 각 무인기의 상태에 대한 시계열 정보와 각 시점에서 지정된 항로점 3점의 기록을 넘겨 애니메이션을 그린다. 경로는 점선으로, 기체는 헤딩을 향한 삼각형으로 표현한다. 꼬리는 tail_length_m 만큼 최근 이동 거리만 강조한다.
    기본적으로 알고리즘을 수정할 필요가 없고, 표시 옵션만 조정한다.
    성능·가독성은 아래의 옵션들을 활용해 조절하며 비디오 파일을 얻고 싶다면 상단에 save_video=True로 두면 영상 저장이 가능하도록 구현하였다.
    • interval_ms: 프레임 간 지연(밀리초)이다. 값이 작을수록 재생 FPS가 높아 부드럽고, 크면 느리다. 일반 40~80 ms (≈ 12 – 25 FPS), 발표용은 30~50 ms.
    • stride: 무인기 상태 시계열 정보을 stride 간격으로 건너뛰어 재생하는 샘플링 간격이다. 값이 클수록 프레임 수가 줄어 재생이 가벼워지고 진행이 빨라 보이지만 움직임이 덜 부드럽다. 긴 시뮬은 30~80, 디버그 활용시에는 1~10.
    • blit: Matplotlib의 “바뀐 요소만 다시 그리는” 가속 모드이다. True면 CPU 사용이 크게 줄어 부드럽다. 특정 환경에서 깜빡임이 보이면 False로 끈다. 팁: 기본 True, 축 한계/배경이 자주 변하면 False.
    • uav_scale: 화면에 그리는 기체 삼각형의 시각적 크기 배율이다. 값이 크면 멀리서도 기체가 눈에 잘 띄고, 작으면 경로가 깔끔하다. 물리 의미는 없다. 팁: 지도가 크면 2~4, 줌인 뷰면 1~2.
    • tail_length_m: 꼬리 선의 길이(미터) 이다. 최근 궤적만 보여주므로 현재 동작이 또렷하고, 너무 길면 화면이 지저분해진다. 0이면 꼬리 없음. 팁: 패턴 확인은 500~1500 m, 최소 표시면 100~300 m.

        # =========================
        # Simulation
        # =========================
        T_steps = int(total_time / sim_dt)
        ctrl_chk = ctrl_dt
        t = 0.0
        
        X = [np.zeros((T_steps + 1, 4)) for _ in range(num_uav)]
        U = [np.zeros((T_steps + 1, 2)) for _ in range(num_uav)]
        for i in range(num_uav):
            X[i][0] = np.asarray(x0_list[i], dtype=float)
            U[i][0] = np.array([0.0, 0.0], float)
            
        for k in range(T_steps):
            if t > ctrl_chk - 1e-12:
                omega0 = Guidance_Method_0(X[0][k])  # UAV1
                U[0][k] = [np.clip(omega0, -1.0, 1.0), 0.0]
                omega1 = Guidance_Method_1(X[1][k])  # UAV2
                U[1][k] = [np.clip(omega1, -1.0, 1.0), 0.0]
                omega2 = Guidance_Method_2(X[2][k])  # UAV3
                U[2][k] = [np.clip(omega2, -1.0, 1.0), 0.0]
                omega3 = Guidance_Method_3(X[3][k])  # UAV4
                U[3][k] = [np.clip(omega3, -1.0, 1.0), 0.0]
                ctrl_chk += ctrl_dt
            for i in range(num_uav):
                X[i][k+1] = integrate_step(unicycle_dynamics, X[i][k], U[i][k], sim_dt, method="rk4", t=t)
                U[i][k+1] = U[i][k]
            t += sim_dt
                    
        # =========================
        # Visualize
        # =========================
        ani, fig = animate_simulation(
        histories=X,
        leg_index_histories=[np.asarray(h, dtype=int) for h in LEG_HIST],
        path_by_leg_list=PATH_BY_LEG,
        interval_ms=50, stride=60, tail_length_m=800.0,
        uav_scale=3, blit=True, show=True)
        if save_video == True:
            ani.save("uav_simulation.mp4", writer=animation.FFMpegWriter(fps=180))
        
        plt.show(block=False)
        plt.close(fig)


이제 준비는 끝났다. 초기값과 미션, 유도기법만 손보고 시뮬레이션을 돌리면 되겠다. 이로써 업데이트 된 시뮬레이터의 기능을 알아보았다. 이를 통해 유도기법에 대한 분석이 좀더 체계적으로 개선될것이라 기대해본다. 이로서 오늘의 포스팅을 마치도록 하겠다. 최종 구성된 파일은 아래 깃허브 주소에 업로드하였다. 우측 상단에 초록색 code 버튼을 통해서 clone을 위한 링크를 얻거나 zip파일로 다운로드 할 수 있다. 추후에 업데이트 내용들도 해당 깃허브 레포에 업데이트할 예정이니 많은 관심을 부탁한다. 이로서 오늘의 포스팅을 마무리하겠다. 

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

 

무인기의 경로 추종 문제에서 안정적이고 예측 가능한 방식으로 목표 경로에 접근하도록 하는것은 매우 중요한 문제이다. 이러한 상황에서 경로 추종 응답 특성을 설계할 수 있다는 점은 매우 큰 장점으로 작용한다. 이번 포스팅에서 소개할 Formation flight of unmanned aerial vehicles using track guidance에서는 튜닝 파라미터를 조절하여 응답특성을 성형할 수 있는 유도기법을 제안한다. 이후 해당 논문에서는 이를 확장하여 편대비행 임무를 수행하는 내용까지 다루지만 본 포스팅에서는 경로 추종 유도 법칙에 집중해서 기술해보도록 하겠다.
 
논문 제목 : Formation flight of unmanned aerial vehicles using track guidance
논문 저자 : Dongwoo Lee, Seungkeun Kim and Jinyoung Suk
저널 정보 : Aerospace Science and Technology, Vol.76, 2018
DOI         : https://doi.org/10.1016/j.ast.2018.01.026

  1. 트랙 유도기법
    본 논문에서 제안된 Track Guidance(TG)  알고리즘은 일차 동특성(first-order dynamics)에 기반한 매우 예측 가능한 응답 특성을 지니도록 설계된 유도기법이다. 알고리즘의 핵심은 가상의 목표선(track line)을 기준으로 전방 \(\kappa\) [m] 거리만큼 떨어진 점을 현재 UAV가 추종하도록 만드는 것이다. UAV의 위치를 \((x, y)\), 속도를 \((\dot{x}, \dot{y})\)라 정의하며, 목표 직선과 y축 방향으로의 오차 (cross-track error)는 \(y_{d}\) 이라고 정의한다. 이때, UAV가 해당 선에 수렴하도록 비례 관계를 설정하면 다음과 같은 제어 입력이 정의된다.
    $$\dot{\psi}_{cmd}=K_{p}(\kappa \dot{y} - y_{d} \dot{x})$$


  2. 핵심 특징
    - 1차 시스템 응답 특성
    트랙 유도기법의 수식을 유도하는 과정은 기하학적 관계를 설정하며 시작한다. 무인기가 목표 경로(직선)를 따라가도록 하기 위해, 현재 위치 \((x, y)\)에서 전방 \(\kappa\) [m] 거리만큼 떨어진 가상의 목표점을 잡게 된다면 기하학적으로 다음의 관계가 성립한다.
    $$\frac{y_{f}-y}{\kappa} = \frac{\dot{y}}{\dot{x}}\;,\;\;(y_{f}-y=y_{d})$$
    이를 정리하여 아래 첫번째 식 처럼 나타낸 후 체인룰을 활용해 전개하면 아래 두번째 식과 같이 표현할 수 있다.
    $$\kappa\frac{dy}{dt} - \frac{dx}{dt}(y_{f}-y) = 0 $$
    $$\frac{dy}{dx} + \frac{y}{\kappa} = \frac{y_{f}}{\kappa} $$
    위 식은 일차 미분방정식의 형태를 가지며 미분방정식의 해를 구하면 아래의 식과 같이 공간축 상의 1차 시스템 응답 형태로 나타나게 되는것을 확인할 수 있다.
    $$y(x)=y_{f}(1-e^{-x/\kappa})$$
    즉 이는 \(x\)에 대한 \(y\)의 응답이 1차 시스템 응답의 형태로 경로 오차가 공간 좌표를 기준으로 overshoot없이 단조 감소하며 목표 직선으로 수렴하게 됨을 의미한다.

    - 튜닝 파라미터\(\kappa\)의 역할
    트랙 유도기법의 경로 추종 응답이 1차 시스템의 응답 특성을 가지게 됨을 확인할 수 있다.
    $$y(x)=y_{f}(1-e^{-x/\kappa})$$
    이를 주파수 영역의 전달함수꼴로 나타내면 아래와 같이 표현할 수 있다.
    $$G(s) = \frac{y(s)}{x(s)} = \frac{y_{f}}{\kappa s + 1}$$
    이를 좀더 깊게 살펴보면 \(x=\kappa\)일 때 목표치의 63.2%에 도달하고, \(x=3\kappa\), \(x=4\kappa\), \(x=5\kappa\)에서 각각 95%, 98.2%, 99.3%에 수렴한다. 따라서 작은 κ는 경로로 더 빠르게 붙는 공격적인 응답을 만들고, 큰 κ는 완만하게 접근하는 보수적인 응답을 형성한다. 즉, κ는 경로 추종의 “시간상수”와 같은 역할을 하며, 설계자가 원하는 수렴 특성을 직접적으로 성형할 수 있게 한다.

    - 튜닝 파라미터\(K_{p}\)의 역할
    트랙 유도기법의 최종  유도 명령 수식은 아래와 같다.
    $$\dot{\psi}_{cmd}=K_{p}(\kappa \dot{y} - y_{d} \dot{x})$$

    이때 \(K_{p}\)와 곱해지는 항은 기하학적인 특성을 만족시키기 위해 0 으로 만들어줘야하는 잔차 항이 된다. 즉 잔차가 발생하는 경우에 \(K_{p}\)가 크면 작은 오차에도 큰 선회율 명령을 발생시켜 기체가 민첩하게 반응하지만, 과도하게 크면 기체의 롤각 제한이나 제어 한계를 넘어 오히려 불안정한 동작을 유발할 수 있다. 반대로 \(K_{p}\)가 너무 작으면 기체가 천천히 반응해 수렴은 하더라도 느슨한 궤적을 보이게 된다. 따라서 Kp는 시스템 안정성과 실제 기체 제약을 고려해 조율해야 하는 파라미터가 되겠다.


이번 포스팅에서는 트랙 유도기법에 대해 알아보았다. 트랙 유도기법은 공간 상수 κ와 비례 이득 Kp라는 두 개의 파라미터만으로 경로 추종의 응답을 원하는 대로 성형할 수 있고, 이 과정이 수학적으로는 전형적인 1차 시스템 응답과 동일하게 해석된다는 점에서 이론적 명확성과 실용성을 동시에 확보한다는 장점이 있다. 이로써 트랙 유도기법과 관련된 논문 리뷰 포스팅을 마치도록 하겠다.

 

 

 

지난번 포스팅에서는 Nonlinear Path Following Guidance(NLPF) 관련 논문을 소개하는 시간을 가졌다. 이를 통해 NLPF 유도기법의 핵심 내용과 주요 특징에 대해 알아보았다. 이번 포스팅은 지난번 다뤘던 NLPF를 좀 더 깊게 분석해보는 내용을 다뤄보겠다. NLPF 유도기법은 유일한 튜닝 파라미터 \(L\)이 있는데 이를 어떻게 설계하느냐에 따라 유도 응답에 어떤 식으로 작용하는지, 그리고 직선 경로와 원형 경로에서 각각 어떤 현상이 나타나는지 살펴보겠다.

2025.08.12-[알쓸신잡 연구노트 Guidance 1편] 논문 리뷰 Nonlinear Path-Following Guidance Method

 

[알쓸신잡 연구노트 Guidance 1편] 논문 리뷰 Nonlinear Path-Following Guidance Method

비행체가 정해진 경로를 안정적이고 정확하게 따라간다는 것은 항공기, 드론, 미사일 등 다양한 분야에서 필수적인 요구사항이다. 하지만 경로가 직선이 아니라 곡선이거나 원형일 경우, 그리

piggymode.tistory.com

 

 

  1. 직선 경로 응답 특성
    첫번째로 다룰 내용은 직선 경로를 추종하는 경우이다. 앞서 설명한 \(L\)은 look ahead 거리 , 즉 앞으로 \(L\)만큼 떨어진 지점을 따라가도록 정의된 가상의 목표점이다. 따라서 \(L\)의 크기에 따라 기체가 얼마나 빠르고 민감하게 경로에 수렴할지가 결정된다.

    지난번 포스팅에서 다뤘듯 수학적으로 정리하면, 경로와 기체의 상대적인 오차 방정식이 이차 미분방정식 형태로 나타난다. 이때 고유진동수(즉 시스템의 응답 속도)는 대략 \( \omega_{n} \propto V/L \)에 비례한다. 따라서 속도가 고정인 상태에서 \(L\)이 작을수록 응답이 빠르며, 오차가 신속하게 줄어든다. 반대로 \(L\)이 커질수록 응답이 느려지고 매끄러워지지만, 선회 구간에서 뒤늦게 따라가는 현상이 심해질 수 있다. 또한 진동이 얼마나 빠르게 사라지는가를 의미하는 감쇠비는 0.707로 고정된 응답 특성을 가진다. 즉 진동의 속도는 \(L\)에 따라 변하지만 진동이 감쇠하는 특성은 약간의 overshoot가 발생하는 under damped로 고정됨을 의미한다.  결국 직선 경로에서 \(L\)응답 속도를 결정하는 파라미터라고 할 수 있다.

    다음의 영상을 살펴보자. 다음 영상은 각 다른 \(L\) look ahead 거리를 사용한 시뮬레이션 결과이다. 총 4기의 무인기가 등장하며 파란색, 빨간색, 초록색, 보라색 순으로 [\(L=100, 200, 400, 800\)] 인 경우를 나타낸다. 직선 경로는 wave length 가 무한인 경우라 결국 수렴하긴 하지만 \(L\)에 따라 다른 응답 특성을 가지는것을 확인할 수 있다.

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.




  2. 원선회 경로 응답 특성
    직선과 달리 원형 경로에서는 곡률이 존재하기 때문에 추가적인 제약이 등장한다. 원 궤도를 따라가기 위해 기체는 일정한 회전률을 유지해야 한다. 그런데 NLPF 유도 법칙은 최대 선회율을 \(2V/L\), 동일한 의미로 최대 황가속도 명령은 \(2V^2/L\), 까지 낼 수 있다. 따라서 원 반지름이 너무 작은 경우, 즉 필요한 선회율이 \(2V/L\)보다 큰 경우라면 추종이 불가능하거나 크게 지연된다.

    이를 조건식으로 표현하면 추종 경로의 반경이 대략\(R \geqslant \frac{L}{2}\)이상이 되어야 선회 경로를 무리 없이 돌 수 있다. 즉, \(L\)이 커질수록 더 큰 반경의 선회 경로만 안정적으로 추종할 수 있고, 반대로 \(L\)이 작으면 더 작은 반경의 선회 경로도 따라갈 수 있지만 응답이 거칠어질 수 있다. 실제 비행에서는 \(L\)이 원 반지름의 절반 이하 정도로 유지되는 것이 안정적이다.

    이와 같은 설명은 기하학적으로 기체가 목표점을 향해 회전할 수 있는가 없는가라는 단순한 조건에 기반한 것이다. 그러나 원형 경로를 입력에 대한 시스템 응답 특성의 관점에서 바라보면 또 다른 해석이 가능하다. 원은 반지름 \(R\)을 갖는 곡선이며, 원 둘레는 \(2 \pi R\)이므로, 이를 경로의 wave length라 정의할 수 있다. 다시 말해 원은 \(L_{p}=2\pi R\)을 가지는 입력신호라고 볼 수 있다. 이는 지난번 포스팅에서도 다룬 내용으로 입력 주파수에서의 위상 지연과 동시에 참조점 선행에 의한 위상 선행 서로 상쇄되기 위해서는 아래의 조건을 가지는 경우 이상적이다.
    $$ L_{p}\approx 4.4L\;,\;\;L\approx\frac{2\pi R}{4.4}\approx1.4 R $$

    다음의 영상을 확인해보자. 우선 800m 반경을 가지는 원선회 경로를 생성한다. 이상적인 \(L\)은 이의 1.4배인 대략 1100m가 되겠다. 결과를 확인해보면 총 4기의 무인기가 등장하며 파란색, 빨간색, 초록색, 보라색 순으로 [\(L=840, 970, 1100, 1220\)] 인 경우를 나타낸다. (\(L\)이 작아질수록 응답속도가 빨라지고 경로에 빨리 다가간다). 이보다 더 큰 \(L\)을 선정하게 되면 오히려 기체가 원 바깥으로 다시 벗어나게 된다. 이를 통해 앞선 설명에서 이상적이라는 표현이 경로를 빨리 추종한다라기보다는 "lookahead로 인해 발생하는 위상선행과 2차 전달함수로 나타나는 유도 기법의 위상 지연이 딱 맞아 떨어져서 위상적으로 가장 자연스럽게 원을 따라가는 지점" 임을 확인할 수 있다. 

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.


이번 글에서는 NL 유도법칙의 구조와 그에 따른 응답 특성을 살펴보았다. 분석 결과, look ahead 거리 \(L\)은 시스템의 응답 속도를 결정하는 핵심 인자이며, 감쇠비는 구조적으로 일정하게 유지되어 응답의 형태가 안정적으로 보장된다는 점을 확인할 수 있었다. 이는 특정 기체나 특정 경로에 국한되지 않고, 다양한 상황에서 일관된 추종 성능을 기대할 수 있음을 의미한다. 이러한 특성을 이해하고 활용하면 직선, 원형, 복잡한 곡선 등 어떤 경로에서도 보다 예측 가능하고 안정적인 제어가 가능하겠다. 이로써 이번 포스팅을 마무리하도록 하겠다.

 

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

 

항공기의 비행 경로 설계에서 기본적이며 자주 사용되는 패턴 중 하나는 지난번 포스팅에서 다룬 장주선회 경로, 또 다른 하나는 바로 원형 Hold 경로이다. 원형 hold는 특정 지점 상공에서 항공기가 일정 반경을 유지하며 계속해서 선회하는 경로를 의미한다. 실제 항공 교통 관리에서는 항로 혼잡이나 착륙 대기 상황에서 항공기가 지상 관제 지시에 따라 일정 지점 상공에서 원형으로 기다리는 경우가 흔하다. 또한 무인기가 특정 지역을 감시하거나 합류 대기를 위해 원형 hold 경로가 자주 활용된다. 

이는 고정익 항공기가 구조적으로 후진이 불가능하기 때문이다. 또한 비행 중에는 선회 반경(turning radius)이라는 물리적 제약이 따라붙는다. 이는 항공기의 속도와 뱅크각(bank angle)에 의해 결정되며, 너무 작은 반경으로는 선회할 수 없다는 뜻이다.

이번 포스팅에서는 이러한 원선회 경로를 구현한 내용을 다룬다.

 

  1. 원선회 경로 생성 함수
    - 원 중심과 반경 활용
    사용자가 원하는 hold 지점의 중심 좌표를 입력한다. \(P_{center} = (n_{c}, e_{c})\) 과 같이 지정한 후 항공기의 성능과 임무 요구 조건에 따라 반지름을 결정한다. 마지막으로 회전 방향을 지정한다. 
    $$[P_{circle} = P_{center} + R [cos(\theta) , sin(\theta)]] \;,\;\;(0 \leq \theta \leq 2 \pi)$$
    여기서 매개변수 \(\theta\)는 0부터 \(2\pi\) 의 값으로 지정된 원의 중심을 기준으로 반경만큼 한바퀴 회전한 점을 정의한다.

    import numpy as np
    import matplotlib.pyplot as plt
    
    def generate_hold(WP_center, radius, num_points, direction):
        waypoints = []
        is_loop=True
    
        for i in range(num_points):
            angle = 2 * np.pi * (i / num_points)
            x = radius * np.sin(angle); y = radius * np.cos(angle)
            waypoints.append([x, y])
    
        waypoints = np.array(waypoints)
    
        # CW/CCW
        if direction == -1:
            waypoints = waypoints[::-1]
    
        Path_Hold = waypoints + WP_center
    
        return Path_Hold, is_loop
    
    if __name__ == "__main__":
        WP_center = [2000, -500]
        WP_radius = 600
        WP_num_points = 400
        WP_direction = -1
        WP_gen, is_loop = generate_hold(WP_center, WP_radius, WP_num_points, WP_direction)
        
        
        fig, ax = plt.subplots()
        ax.set_xlim(-1500, 500)
        ax.set_ylim(1000, 3000)
        ax.set_aspect('equal', adjustable='box')
        ax.grid(True, linestyle=':', alpha=0.3)
        ax.plot(WP_gen[:, 1], WP_gen[:, 0], 'k:', lw=1.2, label="Path")
        
        plt.show()


  2.  원선회 경로 생성 결과
    아래 그림은 원선회 경로 중심점 [1000, 1500], 반지름 600m 를 입력하여 도출된 경로점을 가시화한 결과이다. 경로에 진입하며 Overshoot가 발생하는듯 하지만 이내 수렴하고 경로를 잘 추종한다. 장주선회와 마찬가지로 유의할 점은 항공기의 최소 선회 반경을 항공기의 속도와 최대 뱅크각도를 활용하여 아래의 수식과 같이 정의할 수 있는데 이때 원선회 반경이 이를 넘지 않도록 설계하면 안정적인 장주선회 비행이 가능하다. 어떻게 보면 장주선회보다 더욱 신경서야 하는것이 장주선회는 반원 형태 경로를 비행한 후 직선 경로를 따라 뱅크각이 완화되는 구간이 존재하지만 원선회는 선회를 지속하기 때문에 항공기의 최소 선회 반경보다 작은 반지름을 선정하는 경우 완만한 경로추종이 불가능하다.

    $$ R_{min} = \frac{V^2}{g tan(\phi_{max})}$$


    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 

원형 hold 경로는 무인기 비행 임무 중에서 매우 기본적이면서도 중요한 경로 유형이다. 이로인하여 많은 논문이네 보고서에서 다양한 시뮬레이션 사례로 등장하곤 한다. 이번 포스팅에서는 원형 hold 경로의 정의, 수학적 모델링, 생성 절차, 그리고 시뮬레이션 활용 예시를 다뤄보았다. 이로써 이번 포스팅을 마치도록 하겠다.

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

 

직선 경로는 단순하면서도 가장 효율적인 이동 방법을 제공하며, 경로 계획과 유도 알고리즘의 기본 단위로 사용된다. 또한 직선은 다양한 경로 생성 기법의 기본 빌딩 블록이 된다. 다각형 경로나 꺾이는 경로는 직선 구간들의 집합으로 구성되며, 원형이나 타원 같은 곡선 경로 역시 직선 접선과의 관계 속에서 이해된다. 즉, 직선을 이해하고 정의하는 것은 복잡한 경로를 다루기 전에 반드시 거쳐야 할 출발점이다. 이번 포스팅에서는 항법의 기본이 되는 직선 경로를 구현한 내용을 다룬다. 

 

  1. 직선 경로 생성 함수
    - 시작점, 끝점 활용
    직선 경로는 시작점과 끝점을 지정하는 것만으로도 정의된다. 두 점 \(P_{start} = (n_{s}, e_{s})\) ,  \(P_{end} = (n_{e}, e_{e})\)가 주어지면, 이들을 잇는 직선 위의 점은 다음과 같이 나타낼 수 있다.

    $$P(t) = (1-t) P_{start} + t P_{end} \;,\;\;(0 \leq t \leq 1)$$
    여기서 매개변수 \(t\)가 0일 때는 시작점, 1일 때는 끝점이 되고, 그 사이의 값은 모두 직선 위의 점을 의미한다. 이 단순한 식은 경로점을 일정 간격으로 생성할 때 매우 유용하다. 예를 들어 \(N\)개의 점을 얻고 싶다면 \(t\)를 \(\frac{0}{N-1},\frac{1}{N-1},...,\frac{N-1}{N-1}\) 로 구성하면 된다. 이 방식으로 경로점 집합을 만들면, 무인기가 따라야 할 기준선을 손쉽게 정의할 수 있다.



    - 시작점, 방위각, 길이 활용
    직선의 또 다른 정의 방법은 시작점과 방위각(bearing), 그리고 길이로 설정하는 것이다. 항공기의 현재 위치가 \(P_{start} = (n_{s}, e_{s})\) 이동해야 할 거리 \(L\), 비행해야 할 방위각 \(\psi\)가 주어지면 끝점은 다음과 같이 계산된다.

    $$P_{end} = P_{start} + L [cos(\psi), sin(\psi)]$$
    이때 방위각은 북위 기준 시계방향으로 (+)인 각도를 의미한다. 이 방식은 경로점이 미리 주어지지 않은 상황에서 특히 유용하다. 예를 들어 “현재 위치에서 북동쪽 30도 방향으로 2km 직진하라”는 명령을 단순한 벡터 연산으로 표현할 수 있기 때문이다.


    import numpy as np
    import matplotlib.pyplot as plt
    
    def generate_straight_bearing(start, bearing, length, num_points):
        start = np.array(start)
        psi = np.deg2rad(bearing)
        
        end = start + length * np.array([np.cos(psi), np.sin(psi)])
        Path_Straight, is_loop = generate_straight_p2p(start, end, num_points)
        
        return Path_Straight, is_loop
    
    def generate_straight_p2p(start, end, num_points):
        is_loop=False
    
        start = np.array(start)
        end = np.array(end)
        t = np.linspace(0, 1, num_points)
        
        # Interpolate start to end
        Path_Straight = np.outer(1 - t, start) + np.outer(t, end)
        
        return Path_Straight, is_loop
    
    if __name__ == "__main__":
        #P_start = [0, 0]
        #P_end = [2000, 1000]
        #WP_num_points = 200
        #
        #WP_gen, is_loop = generate_straight_p2p(P_start, P_end, WP_num_points)
        
        #or
        
        P_start = [0, 0]
        bearing = 240
        length = 3000
        WP_num_points = 200
        
        WP_gen, is_loop = generate_straight_bearing(P_start, bearing, length, WP_num_points)
        
        # 시각화
        fig, ax = plt.subplots()
        ax.set_aspect('equal', adjustable='box')
        ax.grid(True, linestyle=':', alpha=0.3)
        ax.scatter([WP_gen[0,1], WP_gen[-1,1]], [WP_gen[0,0], WP_gen[-1,0]], c='r', marker='o', label="Start/End")
        ax.plot(WP_gen[:,1], WP_gen[:,0], 'k:', linewidth=2, label="Path")
        
        plt.show()


  2. 직선 경로 생성 결과
    아래 그림은 직선 경로 시작점 [0, 0], 끝점 [3000, 4500] 을 입력하여 도출된 경로점을 가시화한 결과이다. 지난번 포스팅한 장주선회 경로와는 다르게 loop형태의 끝점에서 시작점으로 연결되는 형태의 경로가 아님으로 마지막 경로점을 지나친 이후의 추가적인 처리를 해줘야 한다. 일반적으로는 hold 선회 명령으로 마무리하는것이 일반적이다.

    동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 



직선 경로는 가장 단순한 형태이지만, 항공기 시뮬레이션과 제어 알고리즘 설계에서는 반드시 거쳐야 하는 기본기다. 시작점과 끝점을 잇는 직선은 효율적인 이동 수단일 뿐 아니라, 다양한 알고리즘 성능을 검증하는 기준선이 된다. 더 나아가 직선 구간을 조합하면 다각형이나 복잡한 임무 경로도 만들 수 있다. 최종 구성된 파일은 아래 깃허브 주소에 업로드하였다. 추후의 업데이트 내용들도 해당 깃허브 레포에 업데이트할 예정이다. 이로서 포스팅을 마무리하겠다.

https://github.com/Piggymode/UAV_2D_Simulation.git

 

GitHub - Piggymode/UAV_2D_Simulation: 2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control

2D 3-dof UAV simulation framework for experimenting with guidance, navigation, and control algorithms. Includes modular components for testing aircraft dynamics, control strategies, and mission sce...

github.com

 

+ Recent posts