Arquitectura y Funcionamiento del mpc_local_planner

Configuración del Controlador Principal

El proceso de inicialización del controlador getsiona la creación de los componentes clave para el control predictivo por modelo (MPC). Aquí se definen la dinámica del vehículo, la estructura de la malla temporal y el motor de optimización.

void ControladorMPC::inicializarComponentes() {
  // Definición del modelo cinemático (ej. Uniciclo)
  modelo_dinamico = std::make_shared<ModeloUniciclo>();

  // Configuración de la malla de discretización (Multiple Shooting)
  // Almacena estados, entradas y puntos de referencia
  malla_temporal = configurarMalla(nodo_nh);  

  // Inicialización del motor de resolución numérica
  optimizador = configurarSolver(nodo_nh);

  // Construcción del Problema de Control Óptimo (OCP)
  // Integra la dinámica, la malla y el optimizador en un grafo estructurado
  problema_estructurado = configurarOCP(nodo_nh, lista_obstaculos, modelo_robot, waypoints);
}

Definición de la Malla de Discretización

La malla permite transformar el problema continuo en uno discreto. Se pueden utilizar mallas fijas o variables según la necesidad de adaptación temporal.

corbo::DiscretizationGridInterface::Ptr ControladorMPC::configurarMalla(const rclcpp::Node::SharedPtr& nh) {
  FiniteDifferencesGridSE2::Ptr malla_se2;
  
  if (usar_malla_variable) {
    malla_se2 = std::make_shared<FiniteDifferencesVariableGridSE2>();
  } else {
    malla_se2 = std::make_shared<FiniteDifferencesGridSE2>();
  }

  // Selección del método de colocación (Diferencias hacia adelante)
  malla_se2->setFiniteDifferencesCollocationMethod(std::make_shared<ForwardDiffCollocationSE2>());

  // Regla de integración para el costo (Suma por la izquierda)
  if (metodo_integracion == "left_sum") {
    malla_se2->setCostIntegrationRule(FullDiscretizationGridBaseSE2::CostIntegrationRule::LeftSum);
  }
  return malla_se2;
}

Integración con el Optimizador IPOPT

El solver utiliza la librería IPOPT para resolver problemas de programación no lineal (NLP). Se configuran parámetros críticos como tolernacias y límites de tiempo de cómputo.

void ControladorMPC::configurarSolver() {
  // SolverIpopt encapsula IpoptWrapper (interfaz NLP) e IpoptApplication
  auto solver_ipopt = std::make_shared<corbo::SolverIpopt>();
  solver_ipopt->initialize();

  // Parámetros de ejecución
  solver_ipopt->setIterations(max_iteraciones); 
  solver_ipopt->setMaxCpuTime(tiempo_limite_cpu);

  // Configuración interna de IPOPT: estrategia de barrera y tolerancias
  // Equivale a manipular Options() en la API nativa de Ipopt
  solver_ipopt->setIpoptOptionNumeric("tol", 1e-6);
  solver_ipopt->setIpoptOptionString("mu_strategy", "adaptive");
}

Formulación del Problema de Control Óptimo (OCP)

En esta etapa se definen las funciones de costo cuadráticas y las restricciones físicas que el robot debe respetar durante la planificación.

void ControladorMPC::configurarOCP() {
  // Creación de un hipergrafo para representar el problema de optimización
  auto grafo_base = std::make_shared<corbo::HyperGraphOptimizationProblemEdgeBased>();

  // Estructura del OCP vinculando la malla y el solver
  auto ocp_instancia = std::make_shared<corbo::StructuredOptimalControlProblem>(malla_temporal, modelo_dinamico, grafo_base, optimizador);

  // Límites de las variables de control (velocidad lineal y angular)
  ocp_instancia->setControlBounds(Eigen::Vector2d(min_v, min_w), Eigen::Vector2d(max_v, max_w));

  // Función de costo de etapa (Stage Cost) y costo terminal
  ocp_instancia->setStageCost(std::make_shared<QuadraticFormCostSE2>(matriz_Q, matriz_R, forma_integral, solver_lsq));
  ocp_instancia->setFinalStageCost(std::make_shared<QuadraticFinalStateCostSE2>(matriz_Qf, solver_lsq));

  // Restricciones de desigualdad: Obstáculos y Footprint
  auto restricciones = std::make_shared<StageInequalitySE2>();
  restricciones->setObstacleVector(lista_obstaculos);
  restricciones->setRobotFootprintModel(perfil_robot);
  restricciones->setMinimumDistance(distancia_seguridad);
  
  // Límites de aceleración
  restricciones->setControlDeviationBounds(Eigen::Vector2d(dec_max, acc_w_max), Eigen::Vector2d(acc_max, acc_w_max));

  ocp_instancia->setStageInequalityConstraint(restricciones);
}

Ciclo de Ejecución (Step)

La función step se encarga de recibir el estado actual y generar la trayectoria óptima. Si el objetivo cambia significativamente, la malla se reinicia para evitar inconsistencias.

bool ControladorMPC::ejecutarPaso(const std::vector<PoseStamped>& plan_global, const Twist& vel_actual, double dt) {
  // Conversión de objetivos y estados a formatos vectoriales
  modelo_dinamico->extraerEstado(objetivo_final, x_final);
  modelo_dinamico->extraerEstado(pose_actual, x_actual);

  // Reinicio de la malla si el cambio de objetivo es brusco
  if (verificarCambioObjetivo(x_final)) {
    malla_temporal->clear();
  }

  // Generación de trayectoria inicial si la malla está vacía (interpolación lineal)
  if (malla_temporal->isEmpty()) {
    generarTrayectoriaReferencia(x_actual, x_final, plan_global);
  }

  // Llamada al motor de cómputo predictivo
  return ocp_instancia->compute(x_actual, referencia_estatica, entrada_cero, dt, tiempo_actual, seq_u, seq_x);
}

Interfaz del Optimizador y Jacobianos

El IpoptWrapper actúa como puente, mapeando los vectores de parámetros del problema de optimización a las funciones de evaluación que requiere el solver para calcular el gradiente y la curvatura.

// Evaluación de la función objetivo
void IpoptWrapper::eval_f(int n, const double* x, bool new_x, double& obj_value) {
  if (new_x) {
    Eigen::Map<const Eigen::VectorXd> vector_x(x, n);
    problema_base->setParameterVector(vector_x); // Actualiza vértices en la malla
  }
  obj_value = problema_base->computeValueObjective();
}

// Evaluación de restricciones
void IpoptWrapper::eval_g(int n, const double* x, bool new_x, int m, double* g) {
  if (new_x) {
    Eigen::Map<const Eigen::VectorXd> vector_x(x, n);
    problema_base->setParameterVector(vector_x);
  }
  Eigen::Map<Eigen::VectorXd> vector_g(g, m);
  // Separa restricciones de igualdad y desigualdad
  problema_base->computeValuesEquality(vector_g.head(dim_igualdad));
  problema_base->computeValuesInequality(vector_g.tail(dim_desigualdad));
}

Etiquetas: MPC IPOPT Robotics ROS2 Control-Theory

Publicado el 10-23 19:11