AEGLOSLAB 제어공학 종합 핸드북

부록 부록

A.3 부록 C — python-control·NumPy·Matplotlib 치트시트

부록 · 참조. 본문 실습에 쓴 도구를 한 장에. ✅ 아래 control API는 control 0.10.2(컨테이너 검증, 목차 2)에서 전부 존재 확인. 버전이 다르면 일부 인자가 다를 수 있다.

A.3.1 시스템 생성·변환

import control as ct, numpy as np
G = ct.tf([1], [1, 2, 4])              # 전달함수 1/(s²+2s+4)
H = ct.ss(A, B, C, D)                  # 상태공간
Gz = ct.tf([1], [1, -0.9], 0.1)        # 이산 전달함수 (dt=0.1)
ct.ss2tf(H);  ct.tf2ss(G)              # 상호 변환 (4.1)
ct.minreal(G)                          # 최소 실현(숨은 모드 제거, 4.2)
ct.c2d(G, 0.1, method='zoh')           # 연속→이산 ('zoh'|'tustin'|'euler'|...) (3.2)

A.3.2 해석 (극·영점·응답)

ct.poles(G);  ct.zeros(G)              # 극·영점 (1.2)
ct.dcgain(G)                           # DC 이득 G(0)
np.linalg.eigvals(A)                   # = 극 (4.1.6)
t, y = ct.step_response(G)             # 계단응답 (1.3)
ct.step_info(G)                        # 오버슈트·정착·상승·피크시간 dict
ct.bode_plot(G, dB=True)               # Bode (2.3)
gm, pm, wpc, wgc = ct.margin(G)        # 이득·위상 여유 + 교차주파수 (2.4)
ct.nyquist_plot(G)                     # Nyquist (2.4)
ct.root_locus(G, grid=True)            # 근궤적 + ζ·ωₙ 격자 (2.2)
ct.pzmap(G)                            # 극·영점 지도

A.3.3 설계 (피드백·극배치·LQR)

T = ct.feedback(G, H)                  # 폐루프 G/(1+GH) (1.2.5)
ct.series(G, H);  ct.parallel(G, H)    # 직렬(곱)·병렬(합)
K = ct.place(A, B, [-2+2j, -2-2j])     # 극배치 (4.3)
L = ct.place(A.T, C.T, poles).T        # 관측기(쌍대 극배치) (4.4)
K, S, E = ct.lqr(A, B, Q, R)           # LQR: 이득K·리카티S·폐루프극E (4.5)
Kd, Sd, Ed = ct.dlqr(Ad, Bd, Q, R)     # 이산 LQR: 이산 리카티S·폐루프극E (4.5, 6.1 MPC 종단비용)
Cc = ct.ctrb(A, B);  Co = ct.obsv(A, C)# 가제어·가관측 행렬 (4.2)
np.linalg.matrix_rank(Cc)              # = n 이면 가제어

A.3.4 NumPy/SciPy (수치)

np.roots([1, 3, 2, 6])                 # 다항식 근 (안정성, 2.1)
np.linalg.eigvals(A); np.linalg.inv(A)
np.linalg.eigvalsh(P)                  # 대칭/에르미트 전용 고유값(더 빠름·실수 보장, 리아푸노프 P, 6.2)
np.linalg.cholesky(P)                  # √P (UKF, 5.3)
from scipy.linalg import solve_continuous_are, solve_lyapunov, expm
solve_continuous_are(A, B, Q, R)       # 리카티 (LQR 직접)
solve_lyapunov(A.T, -Q)                # 리아푸노프 (6.2)
from scipy.optimize import curve_fit   # 시스템 식별 (FOPDT, 1.4)
from scipy.signal import cont2discrete # 이산화 대안
rng = np.random.default_rng(0)
rng.multivariate_normal(mu, Sig, 1000) # 가우시안 샘플 (5.1)
np.cov(X.T)                            # 공분산 추정

A.3.5 Matplotlib (그래프)

import matplotlib
matplotlib.use('Agg')                  # headless(컨테이너·서버)면 Agg, show() 대신 savefig
import matplotlib.pyplot as plt
plt.plot(t, y); plt.grid(True)
plt.xlabel('t [s]'); plt.ylabel('y'); plt.legend()
plt.savefig('out.png')                 # headless
# plt.show()                           # GUI 있을 때

A.3.6 MPC (cvxpy)

import cvxpy as cp                      # (6.1, 7.6)
x = cp.Variable((n, N+1)); u = cp.Variable((m, N))
cost = sum(cp.quad_form(x[:,i], Q) + cp.quad_form(u[:,i], R) for i in range(N))
con = [x[:,0]==x0] + [x[:,i+1]==A@x[:,i]+B@u[:,i] for i in range(N)] \
      + [cp.abs(u[:,i])<=u_max for i in range(N)]
cp.Problem(cp.Minimize(cost), con).solve(solver=cp.OSQP)
u0 = u[:,0].value                       # 첫 입력만 적용

A.3.7 설치 (0.3 요약)

python3 -m venv ~/ctrl-venv && source ~/ctrl-venv/bin/activate
pip install numpy scipy matplotlib sympy control cvxpy
# slycot(일부 고급 함수): sudo apt install gfortran libopenblas-dev && pip install slycot
#   또는 conda install -c conda-forge control slycot

본문에서 각 함수의 의미와 함정을 다룬다 — 이 표는 호출 형태 참조용. 검증 환경·전체 예제는 저장소 verify/(목차 2).