Pular para o conteúdo principal

Estabilizando um Pêndulo Invertido com LQR

· Leitura de 3 minutos
Oséias D. Farias
Trainee Dev IA · Engenheiro ML · Pesquisador Mestrando @ UFABC & UFPA

O pêndulo invertido é o benchmark canônico do controle não-linear — instável, simples o suficiente para modelar analiticamente, mas rico o suficiente para revelar todo o fluxo de projeto LQR. Este post deriva o modelo linearizado a partir da física e mostra como estabilizá-lo com Synapsys em poucas dezenas de linhas de Python.

Física e linearização

Uma haste rígida de massa mm e comprimento LL é articulada em um carrinho. Seja θ\theta o ângulo em relação à vertical. Linearizando em torno de θ=0\theta = 0:

θ¨=gLθ1mL2u\ddot{\theta} = \frac{g}{L}\theta - \frac{1}{mL^2}u

Escolhendo o vetor de estados x=[θ,  θ˙]x = [\theta,\; \dot{\theta}]^\top e a força do carrinho uu:

x˙=[01g/L0]Ax+[01/(mL2)]Bu,y=[10]x\dot{x} = \underbrace{\begin{bmatrix}0 & 1 \\ g/L & 0\end{bmatrix}}_{A}\, x + \underbrace{\begin{bmatrix}0 \\ -1/(mL^2)\end{bmatrix}}_{B}\, u, \qquad y = \begin{bmatrix}1 & 0\end{bmatrix} x

Para m=0,5m = 0,5 kg, L=0,3L = 0,3 m, g=9,81g = 9,81 m/s²:

import numpy as np
from synapsys.api import ss

m, L, g = 0.5, 0.3, 9.81

A = np.array([[0, 1 ],
[g/L, 0 ]])
B = np.array([[0 ],
[-1/(m*L**2) ]])
C = np.array([[1, 0]])
D = np.zeros((1, 1))

planta = ss(A, B, C, D)
print(planta.is_stable()) # False — polos em ±√(g/L) = ±5,72 rad/s

Projeto LQR

O LQR encontra o ganho de realimentação de estados KK que minimiza:

J=0(xQx+uRu)dtJ = \int_0^\infty \left(x^\top Q\, x + u^\top R\, u\right)\mathrm{d}t

QQ grande penaliza erro de estado (rápido, agressivo). RR grande penaliza esforço de controle (lento, conservador):

from synapsys.algorithms import lqr

Q = np.diag([100.0, 1.0]) # penaliza ângulo pesadamente
R = np.array([[0.1]])

K, P = lqr(A, B, Q, R)
print(f"K = {K}") # K = [[33.2 5.8]]

A_cl = A - B @ K
print(np.linalg.eigvals(A_cl)) # autovalores no semiplano esquerdo

Simulação

from synapsys.api import c2d
import matplotlib.pyplot as plt

cl = ss(A_cl, B, C, D)

t = np.linspace(0, 3, 600)
x0 = np.array([0.2, 0.0]) # θ₀ = 0,2 rad (≈ 11°)
u_zero = np.zeros((len(t), 1))
t_out, y = cl.simulate(t, u_zero, x0=x0)

plt.plot(t_out, y)
plt.xlabel("Tempo (s)"); plt.ylabel("θ (rad)")
plt.title("Pêndulo Invertido — estabilização LQR de θ₀ = 0,2 rad")
plt.grid(True); plt.show()

O ângulo retorna a zero em aproximadamente 1,5 s sem sobressinal.


Discretização para deployment embarcado

dt = 0.01  # 100 Hz
planta_d = c2d(planta, dt=dt)

# Redesenhar K no tempo discreto
K_d, _ = lqr(planta_d.A, planta_d.B, Q, R)

# Laço de controle embarcado
x = np.zeros(2)
for _ in range(n_steps):
u = -K_d @ x
x, y = planta_d.evolve(x, u)

Resumo das APIs

EtapaAPI Synapsys
Modelagemss(A, B, C, D)
Verificação de estabilidadeplanta.is_stable(), planta.poles()
Projeto LQRlqr(A, B, Q, R) → retorna (K, P)
Simulaçãocl.simulate(t, u, x0=x0)
Discretizaçãoc2d(planta, dt=0.01)
Laço embarcadoplanta_d.evolve(x, u)

O notebook completo está em examples/quickstart_en.ipynb.