104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
# Simulação MPC com recalculagem do delta_theta a cada passo (versão mais próxima do real)
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
import json
|
|
|
|
# Carregar mapa
|
|
with open("CidadeJardimTerreno2_Corrigido.json", "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
traj_gps = []
|
|
for feature in data["features"]:
|
|
coords = feature["geometry"]["coordinates"]
|
|
for lon, lat in coords:
|
|
traj_gps.append((lat, lon))
|
|
|
|
def latlon_to_xy(lat, lon, lat0, lon0):
|
|
R = 6371000
|
|
dlat = np.radians(lat - lat0)
|
|
dlon = np.radians(lon - lon0)
|
|
x = R * dlon * np.cos(np.radians((lat + lat0) / 2))
|
|
y = R * dlat
|
|
return x, y
|
|
|
|
lat0, lon0 = traj_gps[0]
|
|
traj_xy = np.array([latlon_to_xy(lat, lon, lat0, lon0) for lat, lon in traj_gps])
|
|
|
|
# Parâmetros do robô
|
|
distancia_entre_eixos = 0.92
|
|
angulo_max_graus = 30.0
|
|
angulo_max_rad = np.radians(angulo_max_graus)
|
|
dt = 0.2
|
|
horizonte = 10
|
|
|
|
def calcular_orientacao(p1, p2):
|
|
dx = p2[0] - p1[0]
|
|
dy = p2[1] - p1[1]
|
|
return np.arctan2(dy, dx)
|
|
|
|
def calcular_omega_fisico(v, angulo_rad, tipo_movimento):
|
|
L = distancia_entre_eixos
|
|
tan_delta = np.tan(angulo_rad)
|
|
if tipo_movimento == "rodasFrontais":
|
|
return (v * tan_delta) / L
|
|
elif tipo_movimento == "movimentoArco":
|
|
return (2 * v * tan_delta) / L
|
|
return 0.0
|
|
|
|
def simular_trajetoria_recalculando(x0, y0, theta0, velocidade, tipo_movimento, traj_xy):
|
|
x, y, theta = x0, y0, theta0
|
|
pontos = [(x, y)]
|
|
custo_total = 0.0
|
|
|
|
for step in range(horizonte):
|
|
idx = min(step, len(traj_xy)-1)
|
|
ponto_alvo = traj_xy[idx]
|
|
erro_dist = np.linalg.norm([x - ponto_alvo[0], y - ponto_alvo[1]])
|
|
orient_alvo = calcular_orientacao((x, y), ponto_alvo)
|
|
delta_theta = (orient_alvo - theta + np.pi) % (2 * np.pi) - np.pi
|
|
|
|
# Limitando o ângulo de direção
|
|
angulo_direcional = np.clip(delta_theta, -angulo_max_rad, angulo_max_rad)
|
|
omega = calcular_omega_fisico(velocidade, angulo_direcional, tipo_movimento)
|
|
|
|
# Atualização de estado
|
|
x += velocidade * np.cos(theta) * dt
|
|
y += velocidade * np.sin(theta) * dt
|
|
theta += omega * dt
|
|
|
|
pontos.append((x, y))
|
|
erro_orient = np.abs((orient_alvo - theta + np.pi) % (2 * np.pi) - np.pi)
|
|
custo_total += erro_dist * 2.0 + erro_orient * 1.5
|
|
|
|
return np.array(pontos), custo_total
|
|
|
|
# Estado inicial
|
|
x0, y0, theta0 = traj_xy[0][0] - 0.5, traj_xy[0][1] - 0.5, 0.0
|
|
trajetorias_testadas = []
|
|
custos = []
|
|
|
|
# Candidatos com recalculagem a cada passo
|
|
for vel in [0.6, 1.0, 1.4]:
|
|
for tipo in ["rodasFrontais", "movimentoArco"]:
|
|
traj, custo = simular_trajetoria_recalculando(x0, y0, theta0, vel, tipo, traj_xy)
|
|
trajetorias_testadas.append((traj, vel, tipo))
|
|
custos.append(custo)
|
|
|
|
# Melhor resultado
|
|
melhor_idx = int(np.argmin(custos))
|
|
traj_melhor, vel_melhor, tipo_melhor = trajetorias_testadas[melhor_idx]
|
|
|
|
# Plot
|
|
for traj, vel, tipo in trajetorias_testadas:
|
|
plt.plot(traj[:,0], traj[:,1], alpha=0.2, label=f"{vel} m/s | {tipo}")
|
|
|
|
plt.plot(traj_melhor[:,0], traj_melhor[:,1], 'b-', linewidth=2.5, label=f"Melhor: {vel_melhor} m/s | {tipo_melhor}")
|
|
plt.plot(traj_xy[:,0], traj_xy[:,1], 'ro--', label='Trajetória alvo')
|
|
plt.xlabel("X (m)")
|
|
plt.ylabel("Y (m)")
|
|
plt.title("MPC com recalculagem por passo (delta_theta dinâmico)")
|
|
plt.legend(loc="best", fontsize="small")
|
|
plt.grid(True)
|
|
plt.axis("equal")
|
|
plt.show()
|