Infeasible QPs when tightening bounded constraints

Hi :wave:

This post is a follow-up to my previous post. The constraint-tightening implementation is inspired by this post about variable constraints. and this post about constraint-tightening.

I am implementing probabilistic constraint-tightening for my MPC, so I play around with the parameter p for the constraints. I implemented two variants (with bounded constraint and nonlinear constraint expressions) based on the example code of Acados. I noticed that everything works well with nonlinear constraints, but the optimization is infeasible when using bounded constraints.

What I tried to implement is tightening Fmax by F_tighten from both sides in the input constants, namely, - (Fmax - F_tigthen) <= u <= (Fmax - F_tigthen). The F_tighten is defined as a parameter using p and is set at runtime.

With bounded constraints, I added the variable to the constraint as follows

# tighten the input constraints
ocp.constraints.Jbu = np.eye(nu)
ocp.constraints.lbu = np.array([-Fmax + p_tightening])
ocp.constraints.ubu = np.array([+Fmax - p_tightening])
ocp.constraints.idxbu = np.array([0])

With nonlinear constraint expressions, I rewrite them into half-space expressions,

h_expr = cs.vertcat(-Fmax + p_tightening - ocp.model.u , \
                                -Fmax + p_tightening + ocp.model.u )
h0_expr = cs.vertcat(-Fmax + p_tightening - ocp.model.u , \
                                -Fmax + p_tightening + ocp.model.u )
h_ub = 0 * np.ones(h_expr.shape) 
h_lb = -1e6 * np.ones(h_expr.shape)
h0_ub = 0 * np.ones(h0_expr.shape)
h0_lb = -1e6 * np.ones(h0_expr.shape) 

The parameter is set at runtime

# set parameter values
if constraint_tightening:
    for i in range(N):  
        # tighten the input constraints
        ocp_solver.set(i, "p", np.array([F_tighten]))

The nonlinear constraints tighten the constraint by the given amount while the bounded constraints complain an error during QP solving with the following error message

 ocp_nlp_sqp: maximum iterations reached

iter    res_stat        res_eq          res_ineq        res_comp        qp_stat qp_iter alpha
0       4.000000e+03    0.000000e+00    0.000000e+00    0.000000e+00    0       0       0.000000e+00
1       3.769408e+03    2.220446e-16    3.205571e-13    9.167510e-12    0       7       5.764801e-02
2       3.552109e+03    2.220446e-16    6.226347e-13    3.458655e-11    0       7       5.764801e-02
3       3.347337e+03    2.220446e-16    9.072982e-13    7.344135e-11    0       7       5.764801e-02
4       3.154370e+03    4.440892e-16    1.175551e-12    1.232888e-10    0       7       5.764801e-02
5       2.972527e+03    4.440892e-16    1.428340e-12    1.820136e-10    0       7       5.764801e-02
6       2.801166e+03    2.220446e-16    1.666556e-12    2.477882e-10    0       7       5.764801e-02
7       2.639685e+03    2.220446e-16    1.891040e-12    3.190376e-10    0       7       5.764801e-02
8       2.487512e+03    4.440892e-16    2.102582e-12    3.944088e-10    0       7       5.764801e-02
9       2.344112e+03    3.330669e-16    2.301930e-12    4.727425e-10    0       7       5.764801e-02
10      2.208979e+03    6.661338e-16    2.489785e-12    5.530499e-10    0       7       5.764801e-02
11      2.081635e+03    4.440892e-16    2.666811e-12    6.344904e-10    0       7       5.764801e-02
12      1.961633e+03    4.440892e-16    2.833632e-12    7.163535e-10    0       7       5.764801e-02
13      1.848549e+03    6.661338e-16    2.990836e-12    7.980419e-10    0       7       5.764801e-02
14      1.741984e+03    4.440892e-16    3.138977e-12    8.790566e-10    0       7       5.764801e-02
15      1.641562e+03    4.440892e-16    3.278578e-12    9.589848e-10    0       7       5.764801e-02
16      1.546929e+03    6.661338e-16    3.410132e-12    1.037488e-09    0       7       5.764801e-02
17      1.457752e+03    6.661338e-16    3.534102e-12    1.114291e-09    0       7       5.764801e-02
18      1.373715e+03    4.440892e-16    3.650925e-12    1.189177e-09    0       7       5.764801e-02
19      1.294523e+03    4.440892e-16    3.761013e-12    1.261974e-09    0       7       5.764801e-02
20      1.219897e+03    6.661338e-16    3.864756e-12    1.332553e-09    0       7       5.764801e-02
21      1.149572e+03    6.661338e-16    3.962517e-12    1.400822e-09    0       7       5.764801e-02
22      1.083301e+03    4.440892e-16    4.054643e-12    1.466715e-09    0       7       5.764801e-02
23      1.020851e+03    6.661338e-16    4.141458e-12    1.530196e-09    0       7       5.764801e-02
24      9.620013e+02    6.661338e-16    4.223268e-12    1.591248e-09    0       7       5.764801e-02
25      9.065438e+02    6.661338e-16    4.300362e-12    1.649874e-09    0       7       5.764801e-02
26      8.542834e+02    4.440892e-16    4.373012e-12    1.706090e-09    0       7       5.764801e-02
27      8.050356e+02    4.440892e-16    4.441474e-12    1.759928e-09    0       7       5.764801e-02
28      7.586269e+02    4.440892e-16    4.505989e-12    1.811427e-09    0       7       5.764801e-02
29      7.148936e+02    6.661338e-16    4.566785e-12    1.860637e-09    0       7       5.764801e-02
30      6.736814e+02    6.661338e-16    4.624076e-12    1.907614e-09    0       7       5.764801e-02
31      6.348450e+02    8.881784e-16    4.678064e-12    1.952418e-09    0       7       5.764801e-02
32      5.982474e+02    6.661338e-16    4.728940e-12    1.995116e-09    0       7       5.764801e-02
33      5.637597e+02    6.661338e-16    4.776883e-12    2.035775e-09    0       7       5.764801e-02
34      5.312601e+02    6.661338e-16    4.822062e-12    2.074466e-09    0       7       5.764801e-02
35      5.006340e+02    4.440892e-16    4.864637e-12    2.111259e-09    0       7       5.764801e-02
36      4.717734e+02    4.440892e-16    4.904758e-12    2.146227e-09    0       7       5.764801e-02
37      4.445766e+02    4.440892e-16    4.942565e-12    2.179442e-09    0       7       5.764801e-02
38      4.189477e+02    6.661338e-16    4.978193e-12    2.210976e-09    0       7       5.764801e-02
39      3.947962e+02    6.661338e-16    5.011767e-12    2.240900e-09    0       7       5.764801e-02
40      3.720369e+02    6.661338e-16    5.043406e-12    2.269282e-09    0       7       5.764801e-02
41      3.505898e+02    8.881784e-16    5.073221e-12    2.296192e-09    0       7       5.764801e-02
42      3.303790e+02    8.881784e-16    5.101317e-12    2.321695e-09    0       7       5.764801e-02
43      3.113333e+02    1.110223e-15    5.127793e-12    2.345857e-09    0       7       5.764801e-02
44      2.933855e+02    1.110223e-15    5.152743e-12    2.368741e-09    0       7       5.764801e-02
45      2.764724e+02    1.110223e-15    5.176255e-12    2.390407e-09    0       7       5.764801e-02
46      2.605343e+02    1.110223e-15    5.198411e-12    2.410915e-09    0       7       5.764801e-02
47      2.455151e+02    1.110223e-15    5.219290e-12    2.430320e-09    0       7       5.764801e-02
48      2.313616e+02    1.110223e-15    5.238966e-12    2.448678e-09    0       7       5.764801e-02
49      2.180241e+02    1.110223e-15    5.257507e-12    2.466041e-09    0       7       5.764801e-02
50      2.054554e+02    1.332268e-15    5.274979e-12    2.482459e-09    0       7       5.764801e-02
51      1.936113e+02    1.332268e-15    5.291444e-12    2.497980e-09    0       7       5.764801e-02
52      1.824500e+02    1.554312e-15    5.306960e-12    2.512651e-09    0       7       5.764801e-02
53      1.719321e+02    1.554312e-15    5.321581e-12    2.526516e-09    0       7       5.764801e-02
54      1.620206e+02    1.554312e-15    5.335360e-12    2.539616e-09    0       7       5.764801e-02
55      1.526804e+02    1.554312e-15    5.348344e-12    2.551992e-09    0       7       5.764801e-02
56      1.438787e+02    1.776357e-15    5.360580e-12    2.563682e-09    0       7       5.764801e-02
57      1.355844e+02    1.998401e-15    5.372110e-12    2.574722e-09    0       7       5.764801e-02
58      1.277682e+02    1.998401e-15    5.382976e-12    2.585148e-09    0       7       5.764801e-02
59      1.204026e+02    1.998401e-15    5.393215e-12    2.594992e-09    0       7       5.764801e-02
60      1.134617e+02    1.998401e-15    5.402864e-12    2.604286e-09    0       7       5.764801e-02
61      1.069208e+02    1.998401e-15    5.411957e-12    2.613059e-09    0       7       5.764801e-02
62      1.007570e+02    1.776357e-15    5.420525e-12    2.621340e-09    0       7       5.764801e-02
63      9.494860e+01    1.776357e-15    5.428600e-12    2.629156e-09    0       7       5.764801e-02
64      8.947500e+01    1.776357e-15    5.436209e-12    2.636531e-09    0       7       5.764801e-02
65      8.431695e+01    1.776357e-15    5.443380e-12    2.643491e-09    0       7       5.764801e-02
66      7.945624e+01    1.776357e-15    5.450137e-12    2.650058e-09    0       7       5.764801e-02
67      7.487575e+01    1.776357e-15    5.456504e-12    2.656254e-09    0       7       5.764801e-02
68      7.055931e+01    1.776357e-15    5.462505e-12    2.662099e-09    0       7       5.764801e-02
69      6.649171e+01    1.776357e-15    5.468159e-12    2.667613e-09    0       7       5.764801e-02
70      6.265859e+01    1.776357e-15    5.473488e-12    2.672815e-09    0       7       5.764801e-02
71      5.904645e+01    1.776357e-15    5.478509e-12    2.677721e-09    0       7       5.764801e-02
72      5.564254e+01    1.776357e-15    5.483241e-12    2.682349e-09    0       7       5.764801e-02
73      5.106014e+01    1.554312e-15    5.489611e-12    2.688585e-09    0       7       8.235430e-02
74      4.811662e+01    1.554312e-15    5.493703e-12    2.692595e-09    0       7       5.764801e-02
75      4.534279e+01    1.554312e-15    5.497559e-12    2.696376e-09    0       7       5.764801e-02
76      4.272887e+01    1.554312e-15    5.501193e-12    2.699942e-09    0       7       5.764801e-02
77      4.026564e+01    1.776357e-15    5.504617e-12    2.703304e-09    0       7       5.764801e-02
78      3.552843e+01    1.332268e-15    5.511203e-12    2.709776e-09    0       7       1.176490e-01
79      3.348028e+01    1.332268e-15    5.514050e-12    2.712577e-09    0       7       5.764801e-02
80      3.155021e+01    1.332268e-15    5.516733e-12    2.715217e-09    0       7       5.764801e-02
81      2.783836e+01    1.110223e-15    5.521893e-12    2.720299e-09    0       7       1.176490e-01
82      2.623353e+01    1.110223e-15    5.524124e-12    2.722497e-09    0       7       5.764801e-02
83      2.472122e+01    1.110223e-15    5.526226e-12    2.724570e-09    0       7       5.764801e-02
84      2.329609e+01    1.110223e-15    5.528208e-12    2.726524e-09    0       7       5.764801e-02
85      2.195312e+01    1.110223e-15    5.530074e-12    2.728366e-09    0       7       5.764801e-02
86      1.826346e+01    8.881784e-16    5.535204e-12    2.733429e-09    0       7       1.680700e-01
87      1.721061e+01    8.881784e-16    5.536667e-12    2.734875e-09    0       7       5.764801e-02
88      1.579324e+01    8.881784e-16    5.538638e-12    2.736822e-09    0       7       8.235430e-02
89      1.488279e+01    8.881784e-16    5.539903e-12    2.738073e-09    0       7       5.764801e-02
90      1.402483e+01    8.881784e-16    5.541096e-12    2.739252e-09    0       7       5.764801e-02
91      1.321632e+01    8.881784e-16    5.542220e-12    2.740363e-09    0       7       5.764801e-02
92      1.004308e+01    6.661338e-16    5.546631e-12    2.744727e-09    0       7       2.401000e-01
93      9.464121e+00    6.661338e-16    5.547436e-12    2.745524e-09    0       7       5.764801e-02
94      6.217928e+00    2.220446e-16    5.551949e-12    2.749992e-09    0       7       3.430000e-01
95      5.859476e+00    2.220446e-16    5.552447e-12    2.750486e-09    0       7       5.764801e-02
96      5.521689e+00    2.220446e-16    5.552917e-12    2.750951e-09    0       7       5.764801e-02
97      5.203375e+00    2.220446e-16    5.553359e-12    2.751390e-09    0       7       5.764801e-02
98      4.903411e+00    2.220446e-16    5.553776e-12    2.751803e-09    0       7       5.764801e-02
99      4.620739e+00    2.220446e-16    5.554169e-12    2.752192e-09    0       7       5.764801e-02
100     0.000000e+00    0.000000e+00    0.000000e+00    0.000000e+00    0       7       5.764801e-02

Exception: acados returned status 2.

The error is reproducible with the following code based on the getting_started/minimal_example_ocp.py.

from acados_template import AcadosOcp, AcadosOcpSolver
from pendulum_model import export_pendulum_ode_model
import numpy as np
from utils import plot_pendulum
import casadi as cs

def main():
    # create ocp object to formulate the OCP
    ocp = AcadosOcp()

    # set model
    model = export_pendulum_ode_model()
    ocp.model = model

    Tf = 1.0
    nx = model.x.rows()
    nu = model.u.rows()
    N = 20

    # set number of shooting intervals
    ocp.dims.N = N

    # set prediction horizon
    ocp.solver_options.tf = Tf

    # set cost
    Q_mat = 2*np.diag([1e3, 1e3, 1e-2, 1e-2])
    R_mat = 2*np.diag([1e-2])

    # 'EXTERNAL' cost type 
    ocp.cost.cost_type = 'EXTERNAL'
    ocp.cost.cost_type_e = 'EXTERNAL'
    ocp.model.cost_expr_ext_cost = model.x.T @ Q_mat @ model.x + model.u.T @ R_mat @ model.u
    ocp.model.cost_expr_ext_cost_e = model.x.T @ Q_mat @ model.x

    # set constraints
    Fmax = 80
    F_tighten = 70 # tighten Fmax by F_tighten from both sides of the input constraints
    '''
    set use_nonlinear_constraints_expression = False to use the original bounded constraints and reproduce the error
    '''
    use_nonlinear_constraints_expression = True 
    # use_nonlinear_constraints_expression = False
    constraint_tightening = True 
    # constraint_tightening = False

    if constraint_tightening:
        # tightening variable
        p_tightening = cs.SX.sym('p_tightening') 
        # pass the parameter to the model
        ocp.model.p = p_tightening
        # set dummy parameter values (will be overwritten later)
        ocp.parameter_values = np.array([0])

    if not use_nonlinear_constraints_expression:
        if constraint_tightening:
            # tighten the input constraints
            ocp.constraints.Jbu = np.eye(nu)
            ocp.constraints.lbu = np.array([-Fmax + p_tightening])
            ocp.constraints.ubu = np.array([+Fmax - p_tightening])
            ocp.constraints.idxbu = np.array([0])
        else:
            # original bounded constraints
            ocp.constraints.Jbu = np.eye(nu)
            ocp.constraints.lbu = np.array([-Fmax])
            ocp.constraints.ubu = np.array([+Fmax])
            ocp.constraints.idxbu = np.array([0])
    else:
        # nonlinear constraints expression 
        if constraint_tightening:
            # define the constraint expressions
            h_expr = cs.vertcat(-Fmax + p_tightening - ocp.model.u , \
                                -Fmax + p_tightening + ocp.model.u )
            h0_expr = cs.vertcat(-Fmax + p_tightening - ocp.model.u , \
                                -Fmax + p_tightening + ocp.model.u )
        else:
            h_expr = cs.vertcat(-Fmax - ocp.model.u, \
                                -Fmax + ocp.model.u)
            h0_expr = cs.vertcat(-Fmax - ocp.model.u, \
                                -Fmax + ocp.model.u)
            
        h_ub = 0 * np.ones(h_expr.shape) 
        h_lb = -1e6 * np.ones(h_expr.shape)
        h0_ub = 0 * np.ones(h0_expr.shape)
        h0_lb = -1e6 * np.ones(h0_expr.shape) 
        
        ocp.model.con_h_expr = h_expr
        ocp.dims.nh = h_expr.shape[0]
        ocp.constraints.uh = h_ub
        ocp.constraints.lh = h_lb

        ocp.model.con_h_expr_0 = h0_expr
        ocp.dims.nh_0 = h0_expr.shape[0]
        ocp.constraints.uh_0 = h0_ub
        ocp.constraints.lh_0 = h0_lb

    ocp.constraints.x0 = np.array([1.0, 0, 0.0, 0.0])

    # set options
    ocp.solver_options.qp_solver = 'PARTIAL_CONDENSING_HPIPM'
    ocp.solver_options.hessian_approx = 'GAUSS_NEWTON' 
    ocp.solver_options.integrator_type = 'IRK'
    # ocp.solver_options.print_level = 1
    ocp.solver_options.nlp_solver_type = 'SQP' 
    ocp.solver_options.globalization = 'MERIT_BACKTRACKING' 

    ocp_solver = AcadosOcpSolver(ocp, json_file = 'acados_ocp.json')

    simX = np.zeros((N+1, nx))
    simU = np.zeros((N, nu))

    # set parameter values
    if constraint_tightening:
        for i in range(N):  
            # tighten the input constraints
            ocp_solver.set(i, "p", np.array([F_tighten]))

    status = ocp_solver.solve()
    ocp_solver.print_statistics() # encapsulates: stat = ocp_solver.get_stats("statistics")

    if status != 0:
        raise Exception(f'acados returned status {status}.')

    # get solution
    for i in range(N):
        simX[i,:] = ocp_solver.get(i, "x")
        simU[i,:] = ocp_solver.get(i, "u")
    simX[N,:] = ocp_solver.get(N, "x")

    print('simU', simU.T)

    plot_pendulum(np.linspace(0, Tf, N+1), Fmax, simU, simX, latexify=True, time_label=model.t_label, x_labels=model.x_labels, u_labels=model.u_labels)
    

if __name__ == '__main__':
    main()

Is there anything wrong with my bounded constraint implementation?

Thank you in advance.

Best,
Mingxuan