二次规划问题与OSQP原生求解器

二次规划问题与OSQP原生求解器

二次规划(QP)问题根据其约束条件的性质可以分为线性二次规划非线性二次规划两大类,它们在数学形式、求解难度和应用场景等方面存在显著差异。

比较维度 线性二次规划 非线性二次规划
约束条件 全部线性 至少一个非线性约束
目标函数 必须凸(Q半正定) 可凸可非凸(Q不定)
可行域 凸多面体 可能非凸
最优解性质 全局最优解唯一(严格凸时) 可能有多个局部最优解
求解难度 多项式时间可解 通常NP难
求解方法 内点法、活动集法等专用算法 需要通用非线性优化方法
应用领域 投资组合优化、控制理论等 化学工程、复杂物理系统建模等

线性QP问题的定义

线性二次规划(Linear Quadratic Programming)是一类特殊的非线性规划问题,其目标函数是二次函数,约束条件是线性函数。标准形式的QP问题可以表示为:
m i n    1 2 x T Q x + x T C 约束条件 : A x ≤ b E x = d x ≥ 0 ( 可选 ) min \space \space \frac{1}{2} xᵀQx + xᵀ C \\ \begin{aligned} 约束条件:Ax &≤ b \\ Ex &= d \\ x &≥ 0 (可选) \\ \end{aligned} min  21xTQx+xTC约束条件:AxExxb=d0(可选)
其中:
x ∈ R n x ∈ ℝⁿ xRn是决策变量
Q ∈ R n x n Q ∈ ℝⁿˣⁿ QRnxn是对称矩阵(通常要求半正定)
C ∈ R n C ∈ ℝⁿ CRn是线性项系数向量
A ∈ R m x n 和 b ∈ R m A ∈ ℝᵐˣⁿ 和 b ∈ ℝᵐ ARmxnbRm 定义线性不等式约束
E ∈ R p x n 和 d ∈ R p E ∈ ℝᵖˣⁿ 和 d ∈ ℝᵖ ERpxndRp 定义线性等式约束

QP问题的凹凸性

根据矩阵Q的性质,QP问题可分为:

  • 凸QP问题:当Q是半正定矩阵时,问题是凸的,有全局最优解
  • 非凸QP问题:当Q不定时,问题可能有多个局部最优解
  • 线性规划(LP):当Q=0时,QP退化为线性规划

凸问题(Convex Problem)说明

凸问题是数学优化中一类特殊且重要的问题,因其优良的数学性质在实际应用中备受青睐。下面我将从多个角度全面解释凸问题的概念。

基本定义

凸问题是指满足以下两个条件的优化问题:

  • 凸目标函数:需要最小化的目标函数是凸函数(或需要最大化的目标函数是凹函数)
  • 凸可行域:约束条件定义的可行域是凸集

标准形式的凸优化问题可表示为:
最小化 f ( x ) 约束条件 : g i ( x ) ≤ 0 , i = 1 , . . . , m h j ( x ) = 0 , j = 1 , . . . , p 最小化 f(x) \\ \begin{aligned} 约束条件:gᵢ(x) ≤ 0, i = 1,...,m \\ hⱼ(x) = 0, j = 1,...,p \\ \end{aligned} 最小化f(x)约束条件:gi(x)0,i=1,...,mhj(x)=0,j=1,...,p
其中:
f ( x ) f(x) f(x) 是凸函数
g i ( x ) gᵢ(x) gi(x) 是凸函数(不等式约束)
h j ( x ) hⱼ(x) hj(x) 是仿射函数(等式约束)

凸问题的核心要素

凸集(Convex Set)

集合 C ⊆ R n 称为凸集,如果对于任意 x , y ∈ C 和任意 θ ∈ [ 0 , 1 ] ,有: θ x + ( 1 − θ ) y ∈ C 即集合中任意两点的连线仍完全包含在集合内。 集合C ⊆ ℝⁿ称为凸集,如果对于任意x,y ∈ C和任意θ ∈ [0,1],有:\\ θx + (1-θ)y ∈ C \\ 即集合中任意两点的连线仍完全包含在集合内。 集合CRn称为凸集,如果对于任意x,yC和任意θ[0,1],有:θx+(1θ)yC即集合中任意两点的连线仍完全包含在集合内。

凸函数(Convex Function)

函数 f : R n → R 称为凸函数,如果其定义域是凸集,且对于任意 x , y ∈ d o m ( f ) 和任意 θ ∈ [ 0 , 1 ] ,有 : f ( θ x + ( 1 − θ ) y ) ≤ θ f ( x ) + ( 1 − θ ) f ( y ) 几何解释:函数图像上任意两点间的线段位于函数图像上方。 函数f: ℝⁿ → ℝ称为凸函数,如果其定义域是凸集,且对于任意x,y ∈ dom(f)和任意θ ∈ [0,1],有:\\ f(θx + (1-θ)y) ≤ θf(x) + (1-θ)f(y) \\ 几何解释:函数图像上任意两点间的线段位于函数图像上方。 函数f:RnR称为凸函数,如果其定义域是凸集,且对于任意x,ydom(f)和任意θ[0,1],有:f(θx+(1θ)y)θf(x)+(1θ)f(y)几何解释:函数图像上任意两点间的线段位于函数图像上方。

凸问题的重要性质

  • 全局最优性:任何局部最优解都是全局最优解
  • 最优性条件:对于可微凸函数,∇f(x*) = 0是全局最优的充要条件
  • 求解效率:凸问题通常可以高效求解(多项式时间)
  • 对偶间隙为零:凸问题通常满足强对偶性

凸问题的判别方法

目标函数凸性验证
  • 二次函数: f ( x ) = x T Q x + c T x 是凸的 ⇔ Q 是半正定矩阵 二次函数:f(x) = xᵀQx + cᵀx是凸的 ⇔ Q是半正定矩阵 二次函数:f(x)=xTQx+cTx是凸的Q是半正定矩阵

  • 一般函数:可通过 H e s s i a n 矩阵判断若 ∇ 2 f ( x ) ≽ 0 (半正定)对所有 x ∈ d o m ( f ) 成立,则 f 是凸函数 一般函数:可通过Hessian矩阵判断 若∇²f(x) ≽ 0(半正定)对所有x ∈ dom(f)成立,则f是凸函数 一般函数:可通过Hessian矩阵判断若2f(x)0(半正定)对所有xdom(f)成立,则f是凸函数

约束凸性验证
  • 不等式约束 g i ( x ) ≤ 0 :要求 g i ( x ) 是凸函数 不等式约束gᵢ(x) ≤ 0:要求gᵢ(x)是凸函数 不等式约束gi(x)0:要求gi(x)是凸函数

  • 等式约束 h j ( x ) = 0 :必须是仿射函数(形如 A x = b ) 等式约束hⱼ(x) = 0:必须是仿射函数(形如Ax = b) 等式约束hj(x)=0:必须是仿射函数(形如Ax=b

常见的凸问题类型

  • 线性规划(LP):
    $
    最小化 cᵀx \
    \begin{aligned}
    约束条件 Ax &≤ b \
    Cx &= d \
    \end{aligned}
    $

  • 线性二次规划(QP)(当Q半正定时):
    $
    最小化 \frac{1}{2}xᵀQx + cᵀx \
    \begin{aligned} \
    约束条件 Ax ≤ b
    \end{aligned}
    $

凸问题的求解方法

  • 无约束凸优化:梯度下降法、牛顿法、共轭梯度法

  • 约束凸优化:内点法(特别是路径跟踪法)、有效集法(适用于QP)、交替方向乘子法(ADMM)

凸问题与非凸问题的对比

特性 凸问题 非凸问题
最优解 局部最优=全局最优 可能有多个局部最优
求解难度 通常可高效求解 通常NP难
算法保证 有收敛性和最优性保证 通常只能得到局部最优
对偶间隙 通常为零 通常存在
应用范围 受限于凸性要求 建模更灵活

重要结论

  • 凸二次规划⊂二次规划⊂非线性规划
  • 计算优势:当问题被确认为凸QP时,应优先使用专用凸优化求解器
  • 建模建议:尽量将问题表述为凸QP形式以获得全局最优解
  • 对非凸问题,考虑凸近似或全局优化方法
    通过理解二者的关系,可以在实际问题中更有效地选择建模方法和求解工具。对于凸二次规划问题,推荐使用专用的QP求解器(如OSQP);对于非凸情况,可能需要结合启发式算法或凸松弛技术。

C++实践(Eigen+OSQP):线性二次规划问题的构造与求解

问题数学建模

设投资比例向量为:
X = [ w 1 , w 2 , w 3 ] T X = [w₁, w₂, w₃]ᵀ X=[w1,w2,w3]T
目标函数(组合方差):
m i n    J = 1 2 ∗ ( 0.04 w 1 2 + 0.0225 w 2 2 + 0.0025 w 3 2 + 2 × 0.018 w 1 w 2 + 2 × ( − 0.002 ) w 1 w 3 + 2 × 0.00075 w 2 w 3 ) min \space \space J= \frac{1}{2}*(0.04w₁² + 0.0225w₂² + 0.0025w₃² + 2×0.018w₁w₂ + 2×(-0.002)w₁w₃ + 2×0.00075w₂w₃) min  J=21(0.04w12+0.0225w22+0.0025w32+2×0.018w1w2+2×(0.002)w1w3+2×0.00075w2w3)
约束条件:
预期收益约束: 0.12 w 1 + 0.08 w 2 + 0.04 w 3 ≥ 0.07 预期收益约束:0.12w₁ + 0.08w₂ + 0.04w₃ ≥ 0.07 预期收益约束:0.12w1+0.08w2+0.04w30.07
预算约束: w 1 + w 2 + w 3 = 1 预算约束:w₁ + w₂ + w₃ = 1 预算约束:w1+w2+w3=1
单一资产上限: w 1 ≤ 0.5 , w 2 ≤ 0.5 , w 3 ≤ 0.5 单一资产上限:w₁ ≤ 0.5, w₂ ≤ 0.5, w₃ ≤ 0.5 单一资产上限:w10.5,w20.5,w30.5
非负约束: w 1 ≥ 0 , w 2 ≥ 0 , w 3 ≥ 0 非负约束:w₁ ≥ 0, w₂ ≥ 0, w₃ ≥ 0 非负约束:w10,w20,w30

转化为标准的QP问题形式

m i n J = 1 2 X T P X + X T q s . t .    l o w e r b o u n d ≤ A X ≤ u p p e r b o u n d min J= \frac{1}{2} X^T P X + X^T q \\ s.t. \space \space lowerbound\leq AX \leq upperbound minJ=21XTPX+XTqs.t.  lowerboundAXupperbound
其中:
P = [ 0.04 0.018 − 0.002 0.018 0.0225 0.00075 − 0.002 0.00075 0.0025 ] P= \left[ \begin{matrix} 0.04& 0.018& -0.002 \\ 0.018&0.0225&0.00075 \\ -0.002& 0.00075& 0.0025 \end{matrix} \right] P= 0.040.0180.0020.0180.02250.000750.0020.000750.0025

q = [ 0 0 0 ] q= \left[ \begin{matrix} 0 \\ 0 \\ 0 \end{matrix} \right] q= 000

A = [ 0.12 0.08 0.04 1 1 1 1 0 0 0 1 0 0 0 1 ] A= \left[ \begin{matrix} 0.12 & 0.08 & 0.04 \\ 1& 1& 1 \\ 1& 0& 0 \\ 0& 1& 0 \\ 0& 0& 1 \end{matrix} \right] A= 0.1211000.0810100.041001

l o w e r b o u n d = [ 0.07 1 0 0 0 ] lowerbound= \left[ \begin{matrix} 0.07 \\ 1 \\ 0 \\ 0 \\ 0 \end{matrix} \right] lowerbound= 0.071000

u p p e r b o u n d = [ i n f 1 i n f i n f i n f ] upperbound= \left[ \begin{matrix} inf \\ 1 \\ inf \\ inf \\ inf \end{matrix} \right] upperbound= inf1infinfinf

C++编程求解(Eigen+OSQP)

OSQP 是一个用于求解 凸二次规划(QP) 问题的高效数值优化库,基于 ADMM(交替方向乘子法) 算法。
特点:

  • 支持稀疏矩阵输入(CSC 格式)。
  • 提供 C/C++、Python、MATLAB 等接口。
  • 开源(Apache 2.0 许可证)。

OSQP求解器使用的主要数据结构

Data structure

typedef struct {
  c_int    n; ///< number of variables n
  c_int    m; ///< number of constraints m
  csc     *P; ///< the upper triangular part of the quadratic cost matrix P in csc format (size n x n).必须上三角矩阵,且为OSQP使用的CSC格式
  csc     *A; ///< linear constraints matrix A in csc format (size m x n)OSQP使用的CSC格式
  c_float *q; ///< dense array for linear part of cost function (size n)
  c_float *l; ///< dense array for lower bound (size m)
  c_float *u; ///< dense array for upper bound (size m)
} OSQPData;

Settings struct

typedef struct {
  c_float rho;                    ///< ADMM step rho
  c_float sigma;                  ///< ADMM step sigma
  c_int   scaling;                ///< heuristic data scaling iterations; if 0, then disabled.

# if EMBEDDED != 1
  c_int   adaptive_rho;           ///< boolean, is rho step size adaptive?
  c_int   adaptive_rho_interval;  ///< number of iterations between rho adaptations; if 0, then it is automatic
  c_float adaptive_rho_tolerance; ///< tolerance X for adapting rho. The new rho has to be X times larger or 1/X times smaller than the current one to trigger a new factorization.
#  ifdef PROFILING
  c_float adaptive_rho_fraction;  ///< interval for adapting rho (fraction of the setup time)
#  endif // Profiling
# endif // EMBEDDED != 1

  c_int                   max_iter;      ///< maximum number of iterations
  c_float                 eps_abs;       ///< absolute convergence tolerance
  c_float                 eps_rel;       ///< relative convergence tolerance
  c_float                 eps_prim_inf;  ///< primal infeasibility tolerance
  c_float                 eps_dual_inf;  ///< dual infeasibility tolerance
  c_float                 alpha;         ///< relaxation parameter
  enum linsys_solver_type linsys_solver; ///< linear system solver to use

# ifndef EMBEDDED
  c_float delta;                         ///< regularization parameter for polishing
  c_int   polish;                        ///< boolean, polish ADMM solution
  c_int   polish_refine_iter;            ///< number of iterative refinement steps in polishing

  c_int verbose;                         ///< boolean, write out progress
# endif // ifndef EMBEDDED

  c_int scaled_termination;              ///< boolean, use scaled termination criteria
  c_int check_termination;               ///< integer, check termination interval; if 0, then termination checking is disabled
  c_int warm_start;                      ///< boolean, warm start

# ifdef PROFILING
  c_float time_limit;                    ///< maximum number of seconds allowed to solve the problem; if 0, then disabled
# endif // ifdef PROFILING
} OSQPSettings;
typedef struct {
  /// Problem data to work on (possibly scaled)
  OSQPData *data;

  OSQPSettings *settings; ///< problem settings
  OSQPScaling  *scaling;  ///< scaling vectors
  OSQPSolution *solution; ///< problem solution
  OSQPInfo     *info;     ///< solver information
  ...

} OSQPWorkspace;

使用OSQP求解器求解

代码如下:

#include 
#include 
#include 

// 类型转换辅助函数 - 创建独立的内存
csc eigen_to_csc(const Eigen::SparseMatrix<double> &mat)
{
    csc result;
    result.nzmax = mat.nonZeros();
    result.m = mat.rows();
    result.n = mat.cols();

    // Allocate new memory and copy data
    result.p = (c_int *)malloc((mat.outerSize() + 1) * sizeof(c_int));
    std::copy(mat.outerIndexPtr(), mat.outerIndexPtr() + mat.outerSize() + 1, result.p);

    result.i = (c_int *)malloc(mat.nonZeros() * sizeof(c_int));
    std::copy(mat.innerIndexPtr(), mat.innerIndexPtr() + mat.nonZeros(), result.i);

    result.x = (c_float *)malloc(mat.nonZeros() * sizeof(c_float));
    std::copy(mat.valuePtr(), mat.valuePtr() + mat.nonZeros(), result.x);

    result.nz = -1; // 使用压缩格式

    return result;
}

void free_csc(csc *matrix)
{
    if (matrix)
    {
        if (matrix->p)
            free(matrix->p);
        if (matrix->i)
            free(matrix->i);
        if (matrix->x)
            free(matrix->x);
    }
}

int main()
{
    // 1. 构造标准QP问题
    Eigen::Matrix3d Sigma;
    Sigma << 0.04, 0.018, -0.002,
        0.018, 0.0225, 0.00075,
        -0.002, 0.00075, 0.0025;

    // 2. 提取上三角部分并转为稀疏矩阵,并压缩
    Eigen::Matrix3d P_upper = Sigma.triangularView<Eigen::Upper>();
    Eigen::SparseMatrix<double> P = P_upper.sparseView();//将稠密矩阵转换为稀疏矩阵
    P.makeCompressed();//压缩稀疏矩阵存储,将稀疏矩阵转换为 压缩列存储(CSC)格式,这是 OSQP 所需的格式

    // 3. 梯度矩阵无要求
    Eigen::VectorXd q = Eigen::VectorXd::Zero(3);

    // 4. 约束矩阵转为稀疏矩阵,并压缩
    Eigen::MatrixXd A_dense(5, 3);
    A_dense << 0.12, 0.08, 0.04,
        1, 1, 1,
        1, 0, 0,
        0, 1, 0,
        0, 0, 1;
    Eigen::SparseMatrix<double> A = A_dense.sparseView();//将稠密矩阵转换为稀疏矩阵
    A.makeCompressed();//压缩稀疏矩阵存储,将稀疏矩阵转换为 压缩列存储(CSC)格式,这是 OSQP 所需的格式

    // 5.设置约束上下限(等式约束和不等式约束)
    Eigen::VectorXd lowerbound(5), upperbound(5);
    lowerbound << 0.07, 1, 0, 0, 0;
    upperbound << OSQP_INFTY, 1, OSQP_INFTY, OSQP_INFTY, OSQP_INFTY;

    // 6.将压缩的稀疏矩阵转换为 OSQP 的 CSC 格式
    // 第二种
    csc P_csc = eigen_to_csc(P);
    csc A_csc = eigen_to_csc(A);

    // // 第一种
    // csc P_csc;
    // P_csc.nzmax = P.nonZeros();
    // P_csc.m = P.rows();
    // P_csc.n = P.cols();
    // P_csc.p = (c_int *)P.outerIndexPtr();
    // P_csc.i = (c_int *)P.innerIndexPtr();
    // P_csc.x = (c_float *)P.valuePtr();
    // P_csc.nz = -1; // 标记为压缩格式

    // csc A_csc;
    // A_csc.nzmax = A.nonZeros();
    // A_csc.m = A.rows();
    // A_csc.n = A.cols();
    // A_csc.p = (c_int *)A.outerIndexPtr();
    // A_csc.i = (c_int *)A.innerIndexPtr();
    // A_csc.x = (c_float *)A.valuePtr();
    // A_csc.nz = -1; // 标记为压缩格式

    // 7.填充OSQPData
    OSQPData data;
    data.n = P.rows();
    data.m = A.rows();
    data.P = &P_csc; // 必须是上三角矩阵
    data.q = q.data();
    data.A = &A_csc;
    data.l = lowerbound.data();
    data.u = upperbound.data();

    // 8. 设置参数
    OSQPSettings settings;
    osqp_set_default_settings(&settings);
    settings.verbose = true;//输出求解日志

    // 9. 创建工作空间
    OSQPWorkspace *work = nullptr;
    osqp_setup(&work, &data, &settings);
    if (!work)
    {
        std::cerr << "Failed to setup OSQP workspace" << std::endl;
        free_csc(&P_csc);
        free_csc(&A_csc);
        return 1;
    }

    // 10. 求解QP问题
    osqp_solve(work);

    // 11. 输出结果
    if (work->info->status_val == OSQP_SOLVED)
    {
        std::cout << "A: " << work->solution->x[0] * 100 << "%\n";
        std::cout << "B: " << work->solution->x[1] * 100 << "%\n";
        std::cout << "C: " << work->solution->x[2] * 100 << "%\n";
    }
    else
    {
        std::cerr << "error: " << work->info->status << std::endl;
    }

    // 12. 清理资源
    osqp_cleanup(work);
    // free(P_csc.p);
    // free(P_csc.i);
    // free(P_csc.x);

    free_csc(&P_csc);
    free_csc(&A_csc);
    free(&settings);

    return 0;
}

结果分析说明

-----------------------------------------------------------------
           OSQP v0.6.3  -  Operator Splitting QP Solver
              (c) Bartolomeo Stellato,  Goran Banjac
        University of Oxford  -  Stanford University 2021
-----------------------------------------------------------------
problem:  variables n = 3, constraints m = 5
          nnz(P) + nnz(A) = 15
settings: linear system solver = qdldl,
          eps_abs = 1.0e-003, eps_rel = 1.0e-003,
          eps_prim_inf = 1.0e-004, eps_dual_inf = 1.0e-004,
          rho = 1.00e-001 (adaptive),
          sigma = 1.00e-006, alpha = 1.60, max_iter = 4000
          check_termination: on (interval 25),
          scaling: on, scaled_termination: off
          warm start: on, polish: off, time_limit: off

iter  objective    pri res    dua res    rho        time
   1  0.0000e+000  1.00e+000  1.00e+002  1.00e-001  2.75e-004s
  25  2.8538e-003  2.64e-004  4.26e-004  1.00e-001  5.50e-004s

status:               solved
number of iterations: 25
optimal objective:    0.0029
run time:             1.01e-003s
optimal rho estimate: 1.77e-002

problem:

variables n = 3
优化问题的变量数(即投资组合中的资产数量,此处为3:股票A、股票B、债券C)。

constraints m = 5
约束条件的数量(包括收益率约束、权重和为1、单个资产权重上限等)。

nnz(P) + nnz(A) = 15
矩阵 P(二次项)和 A(约束矩阵)中非零元素的总数。nnz 表示 "number of non-zeros"。

settings

linear system solver = qdldl
使用的线性系统求解器(QDLLDL,一种针对稀疏对称正定矩阵的高效求解器)。

收敛容忍度:
eps_abs/eps_rel:绝对/相对残差容忍度(1e-3)。
eps_prim_inf/eps_dual_inf:原始/对偶不可行性容忍度(1e-4)。

自适应参数:
rho:惩罚参数(初始值0.1,启用自适应调整)。
sigma:正则化参数(1e-6)。
alpha:松弛参数(1.6)。

终止条件:
max_iter = 4000:最大迭代次数。
check_termination: on (interval 25):每25次迭代检查终止条件。

其他选项:
scaling: on:启用问题数据缩放(提高数值稳定性)。
warm start: on:启用热启动(加速求解)。

iter

iter:迭代次数。
objective:当前目标函数值(投资组合方差)。
pri res:原始残差(约束违反程度)。
dua res:对偶残差(最优性条件违反程度)。
rho:当前惩罚参数。
time:累计求解时间。

关键观察:
第1次迭代:目标值为0(初始猜测),原始残差和对偶残差较大(未收敛)。
第25次迭代:目标值收敛到 0.0032,原始和对偶残差均降至容忍度以下(<1e-3),求解成功。

result

status: solved
问题成功求解(其他可能状态:max_iter_reached、primal_infeasible等)。

optimal objective: 0.0032
最优目标函数值(投资组合的最小方差)。

run time: 2ms
总求解时间(性能极佳)。

optimal rho estimate: 1.26e-002
自适应调整后的最终惩罚参数值。

原始残差与对偶残差

在OSQP的求解过程中,原始残差(primal residual)和对偶残差(dual residual)是衡量解的质量和可行性的关键指标。当两者均低于设定的容忍度(如 eps_abs=1e-3 或 eps_rel=1e-3)时,表明求解器找到了一个满足所有约束且接近最优的解。
(1)原始残差(Primal Residual):衡量当前解是否满足原始问题的约束条件,物理意义

  • 值为0:严格满足所有约束。
  • 值很小(如 < 1e-3):约束近似满足,误差在可接受范围内。
  • 值较大:约束被显著违反,解不可行。

(2)对偶残差(Dual Residual):衡量当前解 是否满足最优性条件(即梯度在约束边界上的平衡)。物理意义:

  • 值为0:完全满足最优性条件(如KKT条件)。
  • 值很小(如 < 1e-3):解接近最优。
  • 值较大:解可能未达到最优。

原始残差低 → 约束满足

  • 表明所有约束 l ≤ A x ≤ u l≤Ax≤u lAxu 的违反程度可忽略。

对偶残差低 → 最优性保证

  • 表明目标函数的梯度与约束的拉格朗日乘子平衡,解接近理论最优。

两者结合 → 可行且最优

  • OSQP要求两者同时收敛,确保解既可行(满足约束),又最优(目标函数最小化)。

如何利用残差信息?

(1)检查收敛状态

if (work->info->status_val == OSQP_SOLVED) {
    std::cout << "残差: " << work->info->pri_res << " (原始), " 
              << work->info->dua_res << " (对偶)" << std::endl;
}

(2)调整容忍度
若需要更高精度,可降低 eps_abs(如 1e-5):

settings.eps_abs = 1e-5;

(3)调试不可行问题

若 pri_res 很高,检查约束是否矛盾(如 l > u)。
若 dua_res 很高,验证 P 是否正定或 q 是否正确。

你可能感兴趣的:(自动驾驶控制算法,机器学习,算法,人工智能)