20.3 모델 기반 방법과 최적제어

실습 · 연습문제 2개

LQR을 Riccati 반복으로 풀기 lqr-riccati

선형 시스템 xt+1=Axt+But\mathbf{x}_{t+1} = \mathbf{A}\mathbf{x}_t + \mathbf{B}\mathbf{u}_t와 비용 ∑t(xt⊤Qxt+ut⊤Rut)\sum_t (\mathbf{x}_t^\top\mathbf{Q}\mathbf{x}_t + \mathbf{u}_t^\top\mathbf{R}\mathbf{u}_t)의 최적 정책은 u=−Kx\mathbf{u} = -\mathbf{K}\mathbf{x}다. 가치함수 V(x)=x⊤PxV(\mathbf{x}) = \mathbf{x}^\top\mathbf{P}\mathbf{x}에 대한 가치 반복은 다음 두 식을 번갈아 계산하는 것이다.

K=(R+B⊤PB)−1B⊤PA,P←Q+A⊤P(A−BK).\mathbf{K} = (\mathbf{R} + \mathbf{B}^\top\mathbf{P}\mathbf{B})^{-1}\mathbf{B}^\top\mathbf{P}\mathbf{A}, \qquad \mathbf{P} \leftarrow \mathbf{Q} + \mathbf{A}^\top\mathbf{P}(\mathbf{A} - \mathbf{B}\mathbf{K}).

함수 dlqr(A, B, Q, R, tol=1e-10, max_iter=100_000)를 완성해 (K, P)를 반환한다.

  • P=Q\mathbf{P} = \mathbf{Q}에서 출발하고, 연속한 두 P\mathbf{P}의 원소별 최대 차이가 tol보다 작아지면 멈춘다.
  • K의 shape은 (m, n), P의 shape은 (n, n)이다.
  • 역행렬을 직접 만들지 말고 np.linalg.solve를 쓴다.

테스트는 스칼라 문제(퀴즈의 K≈0.618K \approx 0.618), 본문의 이중 적분기(K=[2.762, 2.508]\mathbf{K} = [2.762,\ 2.508]), scipy의 solve_discrete_are와의 일치, 폐루프 안정성을 확인한다.

생각해 볼 것: R\mathbf{R}을 10배로 키우면 K\mathbf{K}와 폐루프 고윳값이 어떻게 바뀌는가?

랜덤 슈팅 MPC random-shooting-mpc

랜덤 슈팅은 가장 단순한 샘플링 기반 MPC다. 매 step 다음을 한다.

  1. 입력 한계 [ulow,uhigh][\mathbf{u}_{\text{low}}, \mathbf{u}_{\text{high}}] 안에서 균등분포로 길이 HH의 입력열 NN개를 뽑는다.
  2. 각 입력열로 현재 상태에서 모형을 HH step 굴리며, 각 step의 비용 stage_cost(다음 상태, 입력)을 더한다.
  3. 총비용이 가장 작은 입력열의 첫 입력을 반환한다.

함수 random_shooting(x, model, stage_cost, H, N, u_low, u_high, rng)를 완성한다.

인자 뜻
x shape (n,)의 현재 상태
model model(X, U): shape (N, n), (N, m) → (N, n)의 다음 상태 (후보 N개를 한꺼번에)
stage_cost stage_cost(X_next, U): shape (N, n), (N, m) → (N,)
H, N 지평, 후보 수
u_low, u_high shape (m,)의 입력 한계
rng np.random.Generator (표집에 이것만 쓴다)

반환값은 shape (m,)의 첫 입력이다. 테스트는 한계 준수, 1 step 문제의 최적 입력, 한계가 활성화되는 경우, 지연이 있어 지평이 필요한 시스템, 첫 입력만 적용하고 다시 계획하는 폐루프를 확인한다.

힌트: 후보 입력열을 한꺼번에 rng.uniform(u_low, u_high, size=(N, H, m))으로 뽑고, 상태는 np.tile(x, (N, 1))로 시작한다.