146 lines
4.7 KiB
Python
146 lines
4.7 KiB
Python
# Simulador interativo de MPC com separação de visitados (execução vs simulação)
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
import json
|
|
|
|
# Carrega o mapa a partir de pontos.json
|
|
with open("pontos.json", "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
traj_gps = data["pontos"]
|
|
|
|
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
|
|
|
|
def calcular_orientacao(p1, p2):
|
|
dx = p2[0] - p1[0]
|
|
dy = p2[1] - p1[1]
|
|
return np.arctan2(dy, dx)
|
|
|
|
def calcular_omega(v, angulo_rad, tipo):
|
|
tan_delta = np.tan(angulo_rad)
|
|
if tipo == "rodasFrontais":
|
|
return (v * tan_delta) / distancia_entre_eixos
|
|
elif tipo == "movimentoArco":
|
|
return (2 * v * tan_delta) / distancia_entre_eixos
|
|
return 0.0
|
|
|
|
lat0, lon0 = traj_gps[0]
|
|
traj_xy = np.array([latlon_to_xy(lat, lon, lat0, lon0) for lat, lon in traj_gps])
|
|
|
|
# Parâmetros físicos
|
|
distancia_entre_eixos = 0.92
|
|
angulo_max_graus = 30.0
|
|
angulo_max_rad = np.radians(angulo_max_graus)
|
|
dt = 0.2
|
|
velocidade = 1.0
|
|
limiar_entrada = 0.7
|
|
horizonte = 20
|
|
|
|
# Estado atual do robô
|
|
x, y = traj_xy[0][0] - 0.5, traj_xy[0][1] - 0.5
|
|
theta = 0.0
|
|
trajetoria_percorrida = [(x, y)]
|
|
visitados_execucao = [False] * len(traj_xy)
|
|
|
|
def proximo_nao_visitado(visitados):
|
|
for i, v in enumerate(visitados):
|
|
if not v:
|
|
return i
|
|
return len(visitados) - 1
|
|
|
|
def calcular_melhor_candidato(x, y, theta, visitados):
|
|
visitados_sim = visitados.copy()
|
|
idx_alvo = proximo_nao_visitado(visitados_sim)
|
|
if idx_alvo >= len(traj_xy):
|
|
return 0.0, "rodasFrontais"
|
|
|
|
ponto_alvo = traj_xy[idx_alvo]
|
|
if np.linalg.norm([x - ponto_alvo[0], y - ponto_alvo[1]]) < limiar_entrada:
|
|
visitados_sim[idx_alvo] = True
|
|
idx_alvo = proximo_nao_visitado(visitados_sim)
|
|
if idx_alvo >= len(traj_xy):
|
|
return 0.0, "rodasFrontais"
|
|
ponto_alvo = traj_xy[idx_alvo]
|
|
|
|
orient = calcular_orientacao((x, y), ponto_alvo)
|
|
delta_theta = (orient - theta + np.pi) % (2 * np.pi) - np.pi
|
|
angulo = np.clip(delta_theta, -angulo_max_rad, angulo_max_rad)
|
|
|
|
melhor_custo = float('inf')
|
|
melhor_tipo = "rodasFrontais"
|
|
melhor_angulo = angulo
|
|
|
|
for tipo in ["rodasFrontais", "movimentoArco"]:
|
|
omega = calcular_omega(velocidade, angulo, tipo)
|
|
x_sim = x + velocidade * np.cos(theta) * dt
|
|
y_sim = y + velocidade * np.sin(theta) * dt
|
|
theta_sim = theta + omega * dt
|
|
erro = np.linalg.norm([x_sim - ponto_alvo[0], y_sim - ponto_alvo[1]])
|
|
orient_sim = calcular_orientacao((x_sim, y_sim), ponto_alvo)
|
|
erro_ori = np.abs((orient_sim - theta_sim + np.pi) % (2 * np.pi) - np.pi)
|
|
custo = erro * 2.0 + erro_ori * 1.5
|
|
|
|
if custo < melhor_custo:
|
|
melhor_custo = custo
|
|
melhor_tipo = tipo
|
|
melhor_angulo = angulo
|
|
|
|
return melhor_angulo, melhor_tipo
|
|
|
|
def simular_dinamico(x, y, theta, visitados):
|
|
sim = [(x, y)]
|
|
visitados_sim = visitados.copy()
|
|
for _ in range(horizonte):
|
|
angulo_mpc, tipo_mpc = calcular_melhor_candidato(x, y, theta, visitados_sim)
|
|
omega = calcular_omega(velocidade, angulo_mpc, tipo_mpc)
|
|
x += velocidade * np.cos(theta) * dt
|
|
y += velocidade * np.sin(theta) * dt
|
|
theta += omega * dt
|
|
sim.append((x, y))
|
|
return np.array(sim)
|
|
|
|
fig, ax = plt.subplots()
|
|
plt.ion()
|
|
|
|
while True:
|
|
angulo_mpc, tipo_mpc = calcular_melhor_candidato(x, y, theta, visitados_execucao)
|
|
caminho = simular_dinamico(x, y, theta, visitados_execucao)
|
|
|
|
ax.clear()
|
|
ax.plot(traj_xy[:,0], traj_xy[:,1], 'ro--', label='Trajetória alvo')
|
|
ax.plot(*zip(*trajetoria_percorrida), 'b-', label='Percurso real')
|
|
ax.plot(caminho[:,0], caminho[:,1], 'g:', label='Previsão MPC')
|
|
ax.set_title("MPC passo a passo (dinâmico por passo)")
|
|
ax.set_xlabel("X (m)")
|
|
ax.set_ylabel("Y (m)")
|
|
ax.axis("equal")
|
|
ax.legend()
|
|
plt.pause(0.01)
|
|
|
|
print(f"\n>>> Comando MPC: tipo = {tipo_mpc}, ângulo = {np.degrees(angulo_mpc):.2f}°")
|
|
input("Pressione Enter para o próximo passo...")
|
|
|
|
idx_real = proximo_nao_visitado(visitados_execucao)
|
|
if idx_real < len(traj_xy):
|
|
ponto_real = traj_xy[idx_real]
|
|
if np.linalg.norm([x - ponto_real[0], y - ponto_real[1]]) < limiar_entrada:
|
|
visitados_execucao[idx_real] = True
|
|
|
|
omega = calcular_omega(velocidade, angulo_mpc, tipo_mpc)
|
|
x += velocidade * np.cos(theta) * dt
|
|
y += velocidade * np.sin(theta) * dt
|
|
theta += omega * dt
|
|
trajetoria_percorrida.append((x, y))
|
|
|
|
if proximo_nao_visitado(visitados_execucao) >= len(traj_xy):
|
|
print("Trajetória completa!")
|
|
break
|
|
|
|
plt.ioff()
|
|
plt.show() |