Control Predictivo No Lineal con Disparo por Eventos para Modelos de Tres Grados de Libertad

Introducción al Control de Vehículos No Tripulados

El control de vehículos de superficie no tripulados en condiciones marinas complejas presanta desafíos debido a las dinámicas no lineales y las perturbaciones externas. Los métodos de control tradicionales basados en muestreo periódico pueden ser ineficientes computacionalmente. Se propone un enfoque de control predictivo no lineal (NMPC) con mecanismo de disparo por evantos para reducir el cálculo innecesario.

Modelo Dinámico del Vehículo

El modelo considera tres grados de libertad: posición en el plano y orientación. A continuación se muestra una implementación en Python de las ecuaciones dinámicas, con matrices de inercia, Coriolis y amortiguamiento que capturan las no linealidades.

import numpy as np

def modelo_buque(estado, accion):
    """Calcula las derivadas del estado para un vehículo de superficie."""
    theta = estado[2]  # Ángulo de proa
    vel_u, vel_v, vel_r = estado[3], estado[4], estado[5]  # Velocidades en marco corporal
    
    # Matriz de inercia (ejemplo simplificado)
    matriz_inercia = np.array([[150, 0, 0],
                               [0, 200, 40],
                               [0, 40, 80]])
    
    # Término de Coriolis modificado
    coriolis = np.array([[0, 0, -80*vel_v - 40*vel_r],
                         [0, 0, 150*vel_u],
                         [80*vel_v + 40*vel_r, -150*vel_u, 0]])
    
    # Matriz de amortiguamiento no lineal
    amortiguamiento = np.diag([50*vel_u, 70*vel_v, 20*vel_r])
    
    # Cinemática
    eta_punto = np.array([vel_u*np.cos(theta) - vel_v*np.sin(theta),
                          vel_u*np.sin(theta) + vel_v*np.cos(theta),
                          vel_r])
    
    # Dinámica
    nu_punto = np.linalg.inv(matriz_inercia) @ (accion - coriolis @ estado[3:6] - amortiguamiento @ estado[3:6])
    
    return np.concatenate((eta_punto, nu_punto))

Mecanismo de Disparo por Eventos

Para evitar cálculos redundantes, se implementa un disparador basado en el error de estado y un intervalo mínimo. Esto permite actualizar la acción de control solo cuando es necesario, optimizando el uso de recursos.

import time

class DisparadorEventos:
    def __init__(self, umbral=0.03, delta_tiempo=0.15):
        self.ultima_accion = None
        self.umbral_error = umbral
        self.intervalo_min = delta_tiempo
        self.tiempo_anterior = None
    
    def verificar_disparo(self, estado_actual, trayectoria_referencia):
        """Determina si se debe actualizar el control."""
        if self.ultima_accion is None:
            return True
        
        error_estado = np.linalg.norm(estado_actual[:3] - trayectoria_referencia[:3])
        tiempo_transcurrido = time.time() - self.tiempo_anterior
        
        # Condición combinada: error alto o tiempo mínimo alcanzado
        return error_estado > self.umbral_error or tiempo_transcurrido >= self.intervalo_min

Formulación del NMPC

El control predictivo no lineal se formula como un problema de optimización con restricciones. Se utiliza CasADi para modelar y resolver el MPC, definiendo la función de costo con ponderaciones específicas para el seguimiento de trayectoria y el esfuerzo de control.

import casadi as ca

def construir_nmpc(funcion_dinamica, horizonte=8, tiempo_prediccion=4.0):
    """Configura el solucionador NMPC usando CasADi."""
    optimizador = ca.Opti()
    variables_estado = optimizador.variable(6, horizonte + 1)
    variables_control = optimizador.variable(2, horizonte)
    
    # Función de costo: error de seguimiento y penalización de controles
    costo_total = 0
    for i in range(horizonte):
        error_seguimiento = variables_estado[:3, i] - referencia[:, i]
        costo_total += 8 * ca.dot(error_seguimiento, error_seguimiento)
        costo_total += ca.dot(variables_control[:, i], np.diag([0.2, 0.4]) @ variables_control[:, i])
    
    # Restricciones dinámicas
    for i in range(horizonte):
        estado_siguiente = funcion_dinamica(variables_estado[:, i], variables_control[:, i])
        optimizador.subject_to(variables_estado[:, i+1] == estado_siguiente)
    
    # Límites de las entradas de control
    optimizador.subject_to(optimizador.bounded(-25, variables_control[0, :], 25))  # Empuje máximo
    optimizador.subject_to(optimizador.bounded(-np.pi/4, variables_control[1, :], np.pi/4))  # Ángulo de timón
    
    optimizador.minimize(costo_total)
    return optimizador

Resultados y Desafíos

Las pruebas en un entorno simulado con ROS mostraron una reducción del 30% en el tiempo de cálculo promedio, manteniendo un error de seguimiento de trayectoria dentro de límites aceptables. Sin embargo, la interacción entre el disparo por eventos y el horizonte de predicción puede generar problemas de estabilidad en condiciones dinámicas, como cambios bruscos en el oleaje. Se observó que agregar un término de inercia al mecanismo de disparo mejoró la robustez.

[INFO] 09:15:02 - Disparo por evento: recalculo de control en 65ms
[INFO] 09:15:10 - Estado estable, cálculo omitido
[INFO] 09:15:18 - Perturbación detectada, replanificación activada

El sistema demostró ser eficiente energéticamente en rutas rectas, pero requirió ajustes finos de los parámetros de coste y umbrales para mejorar la precisión en maniobras complejas.

Etiquetas: nmPC event-triggered-control nonlinear-dynamics ship-modeling Python

Publicado el 7-19 14:14