mirror of
https://github.com/MoreTore/openpilot.git
synced 2026-08-05 16:26:14 +08:00
longitudinal MPC: use reset() function instead of recreating the solver in (#24091)
* scons: add acados_template as dependency for lat and long mpc * long MPC: use acados reset instead of recreating the solver * long MPC: print timings and reset commented * update acados x86_64 * update acados include folder * update acados Python interface * update acados reference commit to latest acados/master * update x86 libs * update comma two * update acados again with commit 8ea8827fafb1b23b4c7da1c4cf650de1cbd73584 * update comma two * update comma three * update x86 Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Comma Device <device@comma.ai>
This commit is contained in:
@@ -668,6 +668,9 @@
|
||||
"hessian_approx": [
|
||||
"str"
|
||||
],
|
||||
"hpipm_mode": [
|
||||
"str"
|
||||
],
|
||||
"regularize_method": [
|
||||
"str"
|
||||
],
|
||||
|
||||
@@ -2155,6 +2155,7 @@ class AcadosOcpOptions:
|
||||
self.__globalization_use_SOC = 0
|
||||
self.__full_step_dual = 0
|
||||
self.__eps_sufficient_descent = 1e-4
|
||||
self.__hpipm_mode = 'BALANCE'
|
||||
|
||||
|
||||
@property
|
||||
@@ -2165,6 +2166,21 @@ class AcadosOcpOptions:
|
||||
"""
|
||||
return self.__qp_solver
|
||||
|
||||
@property
|
||||
def hpipm_mode(self):
|
||||
"""
|
||||
Mode of HPIPM to be used,
|
||||
|
||||
String in ('BALANCE', 'SPEED_ABS', 'SPEED', 'ROBUST').
|
||||
|
||||
Default: 'BALANCE'.
|
||||
|
||||
see https://cdn.syscop.de/publications/Frison2020a.pdf
|
||||
and the HPIPM code:
|
||||
https://github.com/giaf/hpipm/blob/master/ocp_qp/x_ocp_qp_ipm.c#L69
|
||||
"""
|
||||
return self.__hpipm_mode
|
||||
|
||||
@property
|
||||
def hessian_approx(self):
|
||||
"""Hessian approximation.
|
||||
@@ -2541,6 +2557,15 @@ class AcadosOcpOptions:
|
||||
raise Exception('Invalid collocation_type value. Possible values are:\n\n' \
|
||||
+ ',\n'.join(collocation_types) + '.\n\nYou have: ' + collocation_type + '.\n\nExiting.')
|
||||
|
||||
@hpipm_mode.setter
|
||||
def hpipm_mode(self, hpipm_mode):
|
||||
hpipm_modes = ('BALANCE', 'SPEED_ABS', 'SPEED', 'ROBUST')
|
||||
if hpipm_mode in hpipm_modes:
|
||||
self.__hpipm_mode = hpipm_mode
|
||||
else:
|
||||
raise Exception('Invalid hpipm_mode value. Possible values are:\n\n' \
|
||||
+ ',\n'.join(hpipm_modes) + '.\n\nYou have: ' + hpipm_mode + '.\n\nExiting.')
|
||||
|
||||
@hessian_approx.setter
|
||||
def hessian_approx(self, hessian_approx):
|
||||
hessian_approxs = ('GAUSS_NEWTON', 'EXACT')
|
||||
@@ -2890,10 +2915,10 @@ class AcadosOcp:
|
||||
self.solver_options = AcadosOcpOptions()
|
||||
"""Solver Options, type :py:class:`acados_template.acados_ocp.AcadosOcpOptions`"""
|
||||
|
||||
self.acados_include_path = f'{acados_path}/include'
|
||||
"""Path to acados include directory, type: string"""
|
||||
self.acados_lib_path = f'{acados_path}/lib'
|
||||
"""Path to where acados library is located, type: string"""
|
||||
self.acados_include_path = os.path.join(acados_path, 'include').replace(os.sep, '/') # the replace part is important on Windows for CMake
|
||||
"""Path to acados include directory (set automatically), type: `string`"""
|
||||
self.acados_lib_path = os.path.join(acados_path, 'lib').replace(os.sep, '/') # the replace part is important on Windows for CMake
|
||||
"""Path to where acados library is located, type: `string`"""
|
||||
|
||||
import numpy
|
||||
self.cython_include_dirs = numpy.get_include()
|
||||
|
||||
@@ -54,6 +54,7 @@ from .acados_model import acados_model_strip_casadi_symbolics
|
||||
from .utils import is_column, is_empty, casadi_length, render_template,\
|
||||
format_class_dict, ocp_check_against_layout, np_array_to_list, make_model_consistent,\
|
||||
set_up_imported_gnsf_model, get_ocp_nlp_layout, get_python_interface_path
|
||||
from .builders import CMakeBuilder
|
||||
|
||||
|
||||
def make_ocp_dims_consistent(acados_ocp):
|
||||
@@ -132,6 +133,15 @@ def make_ocp_dims_consistent(acados_ocp):
|
||||
f'\nGot W_0[{cost.W.shape}], yref_0[{cost.yref_0.shape}]\n')
|
||||
dims.ny_0 = ny_0
|
||||
|
||||
elif cost.cost_type_0 == 'EXTERNAL':
|
||||
if opts.hessian_approx == 'GAUSS_NEWTON' and opts.ext_cost_num_hess == 0 and model.cost_expr_ext_cost_custom_hess_0 is None:
|
||||
print("\nWARNING: Gauss-Newton Hessian approximation with EXTERNAL cost type not possible!\n"
|
||||
"got cost_type_0: EXTERNAL, hessian_approx: 'GAUSS_NEWTON.'\n"
|
||||
"GAUSS_NEWTON hessian is only supported for cost_types [NON]LINEAR_LS.\n"
|
||||
"If you continue, acados will proceed computing the exact hessian for the cost term.\n"
|
||||
"Note: There is also the option to use the external cost module with a numerical hessian approximation (see `ext_cost_num_hess`).\n"
|
||||
"OR the option to provide a symbolic custom hessian approximation (see `cost_expr_ext_cost_custom_hess`).\n")
|
||||
|
||||
# path
|
||||
if cost.cost_type == 'LINEAR_LS':
|
||||
ny = cost.W.shape[0]
|
||||
@@ -161,6 +171,15 @@ def make_ocp_dims_consistent(acados_ocp):
|
||||
f'\nGot W[{cost.W.shape}], yref[{cost.yref.shape}]\n')
|
||||
dims.ny = ny
|
||||
|
||||
elif cost.cost_type == 'EXTERNAL':
|
||||
if opts.hessian_approx == 'GAUSS_NEWTON' and opts.ext_cost_num_hess == 0 and model.cost_expr_ext_cost_custom_hess is None:
|
||||
print("\nWARNING: Gauss-Newton Hessian approximation with EXTERNAL cost type not possible!\n"
|
||||
"got cost_type: EXTERNAL, hessian_approx: 'GAUSS_NEWTON.'\n"
|
||||
"GAUSS_NEWTON hessian is only supported for cost_types [NON]LINEAR_LS.\n"
|
||||
"If you continue, acados will proceed computing the exact hessian for the cost term.\n"
|
||||
"Note: There is also the option to use the external cost module with a numerical hessian approximation (see `ext_cost_num_hess`).\n"
|
||||
"OR the option to provide a symbolic custom hessian approximation (see `cost_expr_ext_cost_custom_hess`).\n")
|
||||
|
||||
# terminal
|
||||
if cost.cost_type_e == 'LINEAR_LS':
|
||||
ny_e = cost.W_e.shape[0]
|
||||
@@ -183,6 +202,14 @@ def make_ocp_dims_consistent(acados_ocp):
|
||||
raise Exception('inconsistent dimension: regarding W_e, yref_e.')
|
||||
dims.ny_e = ny_e
|
||||
|
||||
elif cost.cost_type_e == 'EXTERNAL':
|
||||
if opts.hessian_approx == 'GAUSS_NEWTON' and opts.ext_cost_num_hess == 0 and model.cost_expr_ext_cost_custom_hess_e is None:
|
||||
print("\nWARNING: Gauss-Newton Hessian approximation with EXTERNAL cost type not possible!\n"
|
||||
"got cost_type_e: EXTERNAL, hessian_approx: 'GAUSS_NEWTON.'\n"
|
||||
"GAUSS_NEWTON hessian is only supported for cost_types [NON]LINEAR_LS.\n"
|
||||
"If you continue, acados will proceed computing the exact hessian for the cost term.\n"
|
||||
"Note: There is also the option to use the external cost module with a numerical hessian approximation (see `ext_cost_num_hess`).\n"
|
||||
"OR the option to provide a symbolic custom hessian approximation (see `cost_expr_ext_cost_custom_hess`).\n")
|
||||
|
||||
## constraints
|
||||
# initial
|
||||
@@ -622,15 +649,25 @@ def ocp_generate_external_functions(acados_ocp, model):
|
||||
generate_c_code_external_cost(model, 'terminal', opts)
|
||||
|
||||
|
||||
def ocp_render_templates(acados_ocp, json_file):
|
||||
def ocp_get_default_cmake_builder() -> CMakeBuilder:
|
||||
"""
|
||||
If :py:class:`~acados_template.acados_ocp_solver.AcadosOcpSolver` is used with `CMake` this function returns a good first setting.
|
||||
:return: default :py:class:`~acados_template.builders.CMakeBuilder`
|
||||
"""
|
||||
cmake_builder = CMakeBuilder()
|
||||
cmake_builder.options_on = ['BUILD_ACADOS_OCP_SOLVER_LIB']
|
||||
return cmake_builder
|
||||
|
||||
|
||||
def ocp_render_templates(acados_ocp, json_file, cmake_builder=None):
|
||||
|
||||
name = acados_ocp.model.name
|
||||
|
||||
# setting up loader and environment
|
||||
json_path = os.path.join(os.getcwd(), json_file)
|
||||
json_path = os.path.abspath(json_file)
|
||||
|
||||
if not os.path.exists(json_path):
|
||||
raise Exception('{} not found!'.format(json_path))
|
||||
raise Exception(f'Path "{json_path}" not found!')
|
||||
|
||||
code_export_dir = acados_ocp.code_export_directory
|
||||
template_dir = code_export_dir
|
||||
@@ -652,9 +689,14 @@ def ocp_render_templates(acados_ocp, json_file):
|
||||
out_file = f'acados_solver.pxd'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
|
||||
in_file = 'Makefile.in'
|
||||
out_file = 'Makefile'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
if cmake_builder is not None:
|
||||
in_file = 'CMakeLists.in.txt'
|
||||
out_file = 'CMakeLists.txt'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
else:
|
||||
in_file = 'Makefile.in'
|
||||
out_file = 'Makefile'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
|
||||
in_file = 'acados_solver_sfun.in.c'
|
||||
out_file = f'acados_solver_sfunction_{name}.c'
|
||||
@@ -764,25 +806,30 @@ class AcadosOcpSolver:
|
||||
"""
|
||||
Class to interact with the acados ocp solver C object.
|
||||
|
||||
:param acados_ocp: type AcadosOcp - description of the OCP for acados
|
||||
:param acados_ocp: type :py:class:`~acados_template.acados_ocp.AcadosOcp` - description of the OCP for acados
|
||||
:param json_file: name for the json file used to render the templated code - default: acados_ocp_nlp.json
|
||||
:param simulink_opts: Options to configure Simulink S-function blocks, mainly to activate possible Inputs and Outputs
|
||||
"""
|
||||
if sys.platform=="win32":
|
||||
from ctypes import wintypes
|
||||
dlclose = ctypes.WinDLL('kernel32', use_last_error=True).FreeLibrary
|
||||
from ctypes import WinDLL
|
||||
dlclose = WinDLL('kernel32', use_last_error=True).FreeLibrary
|
||||
dlclose.argtypes = [wintypes.HMODULE]
|
||||
else:
|
||||
dlclose = CDLL(None).dlclose
|
||||
dlclose.argtypes = [c_void_p]
|
||||
|
||||
@classmethod
|
||||
def generate(cls, acados_ocp, json_file='acados_ocp_nlp.json', simulink_opts=None):
|
||||
def generate(cls, acados_ocp, json_file='acados_ocp_nlp.json', simulink_opts=None, cmake_builder: CMakeBuilder = None):
|
||||
"""
|
||||
Generates the code for an acados OCP solver, given the description in acados_ocp.
|
||||
:param acados_ocp: type AcadosOcp - description of the OCP for acados
|
||||
:param json_file: name for the json file used to render the templated code - default: acados_ocp_nlp.json
|
||||
:param simulink_opts: Options to configure Simulink S-function blocks, mainly to activate possible Inputs and Outputs
|
||||
:param json_file: name for the json file used to render the templated code - default: `acados_ocp_nlp.json`
|
||||
:param simulink_opts: Options to configure Simulink S-function blocks, mainly to activate possible inputs and
|
||||
outputs; default: `None`
|
||||
:param cmake_builder: type :py:class:`~acados_template.builders.CMakeBuilder` generate a `CMakeLists.txt` and use
|
||||
the `CMake` pipeline instead of a `Makefile` (`CMake` seems to be the better option in conjunction with
|
||||
`MS Visual Studio`); default: `None`
|
||||
"""
|
||||
model = acados_ocp.model
|
||||
acados_ocp.code_export_directory = os.path.abspath(acados_ocp.code_export_directory)
|
||||
@@ -810,25 +857,32 @@ class AcadosOcpSolver:
|
||||
ocp_formulation_json_dump(acados_ocp, simulink_opts, json_file)
|
||||
|
||||
# render templates
|
||||
ocp_render_templates(acados_ocp, json_file)
|
||||
ocp_render_templates(acados_ocp, json_file, cmake_builder=cmake_builder)
|
||||
acados_ocp.json_file = json_file
|
||||
|
||||
|
||||
@classmethod
|
||||
def build(cls, code_export_dir, with_cython=False):
|
||||
def build(cls, code_export_dir, with_cython=False, cmake_builder: CMakeBuilder = None):
|
||||
"""
|
||||
Builds the code for an acados OCP solver, that has been generated in code_export_dir
|
||||
:param code_export_dir: directory in which acados OCP solver has been generated, see generate()
|
||||
:param with_cython: option indicating if the cython interface is build, default: False.
|
||||
:param cmake_builder: type :py:class:`~acados_template.builders.CMakeBuilder` generate a `CMakeLists.txt` and use
|
||||
the `CMake` pipeline instead of a `Makefile` (`CMake` seems to be the better option in conjunction with
|
||||
`MS Visual Studio`); default: `None`
|
||||
"""
|
||||
code_export_dir = os.path.abspath(code_export_dir)
|
||||
cwd=os.getcwd()
|
||||
os.chdir(code_export_dir)
|
||||
if with_cython:
|
||||
os.system('make clean_ocp_cython')
|
||||
os.system('make ocp_cython')
|
||||
else:
|
||||
os.system('make clean_ocp_shared_lib')
|
||||
os.system('make ocp_shared_lib')
|
||||
if cmake_builder is not None:
|
||||
cmake_builder.exec(code_export_dir)
|
||||
else:
|
||||
os.system('make clean_ocp_shared_lib')
|
||||
os.system('make ocp_shared_lib')
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
@@ -856,11 +910,11 @@ class AcadosOcpSolver:
|
||||
acados_ocp_json['dims']['N'])
|
||||
|
||||
|
||||
def __init__(self, acados_ocp, json_file='acados_ocp_nlp.json', simulink_opts=None, build=True, generate=True):
|
||||
def __init__(self, acados_ocp, json_file='acados_ocp_nlp.json', simulink_opts=None, build=True, generate=True, cmake_builder: CMakeBuilder = None):
|
||||
|
||||
self.solver_created = False
|
||||
if generate:
|
||||
self.generate(acados_ocp, json_file=json_file, simulink_opts=simulink_opts)
|
||||
self.generate(acados_ocp, json_file=json_file, simulink_opts=simulink_opts, cmake_builder=cmake_builder)
|
||||
|
||||
# load json, store options in object
|
||||
with open(json_file, 'r') as f:
|
||||
@@ -873,14 +927,22 @@ class AcadosOcpSolver:
|
||||
code_export_directory = acados_ocp_json['code_export_directory']
|
||||
|
||||
if build:
|
||||
self.build(code_export_directory, with_cython=False)
|
||||
self.build(code_export_directory, with_cython=False, cmake_builder=cmake_builder)
|
||||
|
||||
# prepare library loading
|
||||
lib_prefix = 'lib'
|
||||
lib_ext = '.so'
|
||||
if os.name == 'nt':
|
||||
lib_prefix = ''
|
||||
lib_ext = ''
|
||||
# ToDo: check for mac
|
||||
|
||||
# Load acados library to avoid unloading the library.
|
||||
# This is necessary if acados was compiled with OpenMP, since the OpenMP threads can't be destroyed.
|
||||
# Unloading a library which uses OpenMP results in a segfault (on any platform?).
|
||||
# see [https://stackoverflow.com/questions/34439956/vc-crash-when-freeing-a-dll-built-with-openmp]
|
||||
# or [https://python.hotexamples.com/examples/_ctypes/-/dlclose/python-dlclose-function-examples.html]
|
||||
libacados_name = 'libacados.so'
|
||||
libacados_name = f'{lib_prefix}acados{lib_ext}'
|
||||
libacados_filepath = os.path.join(acados_lib_path, libacados_name)
|
||||
self.__acados_lib = CDLL(libacados_filepath)
|
||||
# find out if acados was compiled with OpenMP
|
||||
@@ -892,8 +954,8 @@ class AcadosOcpSolver:
|
||||
print('acados was compiled with OpenMP.')
|
||||
else:
|
||||
print('acados was compiled without OpenMP.')
|
||||
|
||||
self.shared_lib_name = f'{code_export_directory}/libacados_ocp_solver_{self.model_name}.so'
|
||||
libacados_ocp_solver_name = f'{lib_prefix}acados_ocp_solver_{self.model_name}{lib_ext}'
|
||||
self.shared_lib_name = os.path.join(code_export_directory, libacados_ocp_solver_name)
|
||||
|
||||
# get shared_lib
|
||||
self.shared_lib = CDLL(self.shared_lib_name)
|
||||
@@ -959,6 +1021,17 @@ class AcadosOcpSolver:
|
||||
return self.status
|
||||
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Sets current iterate to all zeros.
|
||||
"""
|
||||
getattr(self.shared_lib, f"{self.model_name}_acados_reset").argtypes = [c_void_p]
|
||||
getattr(self.shared_lib, f"{self.model_name}_acados_reset").restype = c_int
|
||||
getattr(self.shared_lib, f"{self.model_name}_acados_reset")(self.capsule)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def set_new_time_steps(self, new_time_steps):
|
||||
"""
|
||||
Set new time steps.
|
||||
@@ -1364,12 +1437,12 @@ class AcadosOcpSolver:
|
||||
return out[0]
|
||||
|
||||
|
||||
def get_residuals(self):
|
||||
def get_residuals(self, recompute=False):
|
||||
"""
|
||||
Returns an array of the form [res_stat, res_eq, res_ineq, res_comp].
|
||||
"""
|
||||
# compute residuals if RTI
|
||||
if self.solver_options['nlp_solver_type'] == 'SQP_RTI':
|
||||
if self.solver_options['nlp_solver_type'] == 'SQP_RTI' or recompute:
|
||||
self.shared_lib.ocp_nlp_eval_residuals.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||
self.shared_lib.ocp_nlp_eval_residuals(self.nlp_solver, self.nlp_in, self.nlp_out)
|
||||
|
||||
@@ -1403,7 +1476,7 @@ class AcadosOcpSolver:
|
||||
Set numerical data inside the solver.
|
||||
|
||||
:param stage: integer corresponding to shooting node
|
||||
:param field: string in ['x', 'u', 'pi', 'lam', 't', 'p']
|
||||
:param field: string in ['x', 'u', 'pi', 'lam', 't', 'p', 'xdot_guess', 'z_guess']
|
||||
|
||||
.. note:: regarding lam, t: \n
|
||||
the inequalities are internally organized in the following order: \n
|
||||
@@ -1419,7 +1492,7 @@ class AcadosOcpSolver:
|
||||
cost_fields = ['y_ref', 'yref']
|
||||
constraints_fields = ['lbx', 'ubx', 'lbu', 'ubu']
|
||||
out_fields = ['x', 'u', 'pi', 'lam', 't', 'z', 'sl', 'su']
|
||||
mem_fields = ['xdot_guess']
|
||||
mem_fields = ['xdot_guess', 'z_guess']
|
||||
|
||||
# cast value_ to avoid conversion issues
|
||||
if isinstance(value_, (float, int)):
|
||||
@@ -1663,11 +1736,21 @@ class AcadosOcpSolver:
|
||||
"""
|
||||
Set options of the solver.
|
||||
|
||||
:param field: string, e.g. 'print_level', 'rti_phase', 'initialize_t_slacks', 'step_length', 'alpha_min', 'alpha_reduction', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC'
|
||||
:param value: of type int, float
|
||||
:param field: string, e.g. 'print_level', 'rti_phase', 'initialize_t_slacks', 'step_length', 'alpha_min', 'alpha_reduction', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC', 'qp_tol_stat', 'qp_tol_eq', 'qp_tol_ineq', 'qp_tol_comp', 'qp_tau_min', 'qp_mu0'
|
||||
|
||||
:param value: of type int, float, string
|
||||
|
||||
- qp_tol_stat: QP solver tolerance stationarity
|
||||
- qp_tol_eq: QP solver tolerance equalities
|
||||
- qp_tol_ineq: QP solver tolerance inequalities
|
||||
- qp_tol_comp: QP solver tolerance complementarity
|
||||
- qp_tau_min: for HPIPM QP solvers: minimum value of barrier parameter in HPIPM
|
||||
- qp_mu0: for HPIPM QP solvers: initial value for complementarity slackness
|
||||
- warm_start_first_qp: indicates if first QP in SQP is warm_started
|
||||
"""
|
||||
int_fields = ['print_level', 'rti_phase', 'initialize_t_slacks', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC']
|
||||
double_fields = ['step_length', 'tol_eq', 'tol_stat', 'tol_ineq', 'tol_comp', 'alpha_min', 'alpha_reduction', 'eps_sufficient_descent']
|
||||
int_fields = ['print_level', 'rti_phase', 'initialize_t_slacks', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC', 'warm_start_first_qp']
|
||||
double_fields = ['step_length', 'tol_eq', 'tol_stat', 'tol_ineq', 'tol_comp', 'alpha_min', 'alpha_reduction', 'eps_sufficient_descent',
|
||||
'qp_tol_stat', 'qp_tol_eq', 'qp_tol_ineq', 'qp_tol_comp', 'qp_tau_min', 'qp_mu0']
|
||||
string_fields = ['globalization']
|
||||
|
||||
# check field availability and type
|
||||
|
||||
@@ -112,6 +112,13 @@ cdef class AcadosOcpSolverCython:
|
||||
return acados_solver.acados_solve(self.capsule)
|
||||
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Sets current iterate to all zeros.
|
||||
"""
|
||||
return acados_solver.acados_reset(self.capsule)
|
||||
|
||||
|
||||
def set_new_time_steps(self, new_time_steps):
|
||||
"""
|
||||
Set new time steps.
|
||||
@@ -450,12 +457,12 @@ cdef class AcadosOcpSolverCython:
|
||||
return out
|
||||
|
||||
|
||||
def get_residuals(self):
|
||||
def get_residuals(self, recompute=False):
|
||||
"""
|
||||
Returns an array of the form [res_stat, res_eq, res_ineq, res_comp].
|
||||
"""
|
||||
# compute residuals if RTI
|
||||
if self.nlp_solver_type == 'SQP_RTI':
|
||||
if self.nlp_solver_type == 'SQP_RTI' or recompute:
|
||||
acados_solver_common.ocp_nlp_eval_residuals(self.nlp_solver, self.nlp_in, self.nlp_out)
|
||||
|
||||
# create output array
|
||||
@@ -504,7 +511,7 @@ cdef class AcadosOcpSolverCython:
|
||||
cost_fields = ['y_ref', 'yref']
|
||||
constraints_fields = ['lbx', 'ubx', 'lbu', 'ubu']
|
||||
out_fields = ['x', 'u', 'pi', 'lam', 't', 'z', 'sl', 'su']
|
||||
mem_fields = ['xdot_guess']
|
||||
mem_fields = ['xdot_guess', 'z_guess']
|
||||
|
||||
field = field_.encode('utf-8')
|
||||
|
||||
@@ -635,11 +642,21 @@ cdef class AcadosOcpSolverCython:
|
||||
"""
|
||||
Set options of the solver.
|
||||
|
||||
:param field: string, e.g. 'print_level', 'rti_phase', 'initialize_t_slacks', 'step_length', 'alpha_min', 'alpha_reduction'
|
||||
:param value: of type int, float
|
||||
:param field: string, e.g. 'print_level', 'rti_phase', 'initialize_t_slacks', 'step_length', 'alpha_min', 'alpha_reduction', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC', 'qp_tol_stat', 'qp_tol_eq', 'qp_tol_ineq', 'qp_tol_comp', 'qp_tau_min', 'qp_mu0'
|
||||
|
||||
:param value: of type int, float, string
|
||||
|
||||
- qp_tol_stat: QP solver tolerance stationarity
|
||||
- qp_tol_eq: QP solver tolerance equalities
|
||||
- qp_tol_ineq: QP solver tolerance inequalities
|
||||
- qp_tol_comp: QP solver tolerance complementarity
|
||||
- qp_tau_min: for HPIPM QP solvers: minimum value of barrier parameter in HPIPM
|
||||
- qp_mu0: for HPIPM QP solvers: initial value for complementarity slackness
|
||||
- warm_start_first_qp: indicates if first QP in SQP is warm_started
|
||||
"""
|
||||
int_fields = ['print_level', 'rti_phase', 'initialize_t_slacks', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC']
|
||||
double_fields = ['step_length', 'tol_eq', 'tol_stat', 'tol_ineq', 'tol_comp', 'alpha_min', 'alpha_reduction', 'eps_sufficient_descent']
|
||||
int_fields = ['print_level', 'rti_phase', 'initialize_t_slacks', 'qp_warm_start', 'line_search_use_sufficient_descent', 'full_step_dual', 'globalization_use_SOC', 'warm_start_first_qp']
|
||||
double_fields = ['step_length', 'tol_eq', 'tol_stat', 'tol_ineq', 'tol_comp', 'alpha_min', 'alpha_reduction', 'eps_sufficient_descent',
|
||||
'qp_tol_stat', 'qp_tol_eq', 'qp_tol_ineq', 'qp_tol_comp', 'qp_tau_min', 'qp_mu0']
|
||||
string_fields = ['globalization']
|
||||
|
||||
# encode
|
||||
|
||||
@@ -294,9 +294,9 @@ class AcadosSim:
|
||||
self.solver_options = AcadosSimOpts()
|
||||
"""Solver Options, type :py:class:`acados_template.acados_sim.AcadosSimOpts`"""
|
||||
|
||||
self.acados_include_path = f'{acados_path}/include'
|
||||
"""Path to acados include directors (set automatically), type: `string`"""
|
||||
self.acados_lib_path = f'{acados_path}/lib'
|
||||
self.acados_include_path = os.path.join(acados_path, 'include').replace(os.sep, '/') # the replace part is important on Windows for CMake
|
||||
"""Path to acados include directory (set automatically), type: `string`"""
|
||||
self.acados_lib_path = os.path.join(acados_path, 'lib').replace(os.sep, '/') # the replace part is important on Windows for CMake
|
||||
"""Path to where acados library is located (set automatically), type: `string`"""
|
||||
|
||||
self.code_export_directory = 'c_generated_code'
|
||||
|
||||
@@ -47,6 +47,7 @@ from .acados_ocp import AcadosOcp
|
||||
from .acados_model import acados_model_strip_casadi_symbolics
|
||||
from .utils import is_column, render_template, format_class_dict, np_array_to_list,\
|
||||
make_model_consistent, set_up_imported_gnsf_model, get_python_interface_path
|
||||
from .builders import CMakeBuilder
|
||||
|
||||
|
||||
def make_sim_dims_consistent(acados_sim):
|
||||
@@ -111,7 +112,17 @@ def sim_formulation_json_dump(acados_sim, json_file='acados_sim.json'):
|
||||
json.dump(sim_json, f, default=np_array_to_list, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
def sim_render_templates(json_file, model_name, code_export_dir):
|
||||
def sim_get_default_cmake_builder() -> CMakeBuilder:
|
||||
"""
|
||||
If :py:class:`~acados_template.acados_sim_solver.AcadosSimSolver` is used with `CMake` this function returns a good first setting.
|
||||
:return: default :py:class:`~acados_template.builders.CMakeBuilder`
|
||||
"""
|
||||
cmake_builder = CMakeBuilder()
|
||||
cmake_builder.options_on = ['BUILD_ACADOS_SIM_SOLVER_LIB']
|
||||
return cmake_builder
|
||||
|
||||
|
||||
def sim_render_templates(json_file, model_name, code_export_dir, cmake_options: CMakeBuilder = None):
|
||||
# setting up loader and environment
|
||||
json_path = os.path.join(os.getcwd(), json_file)
|
||||
|
||||
@@ -129,9 +140,15 @@ def sim_render_templates(json_file, model_name, code_export_dir):
|
||||
out_file = f'acados_sim_solver_{model_name}.h'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
|
||||
in_file = 'Makefile.in'
|
||||
out_file = f'Makefile'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
# Builder
|
||||
if cmake_options is not None:
|
||||
in_file = 'CMakeLists.in.txt'
|
||||
out_file = 'CMakeLists.txt'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
else:
|
||||
in_file = 'Makefile.in'
|
||||
out_file = 'Makefile'
|
||||
render_template(in_file, out_file, template_dir, json_path)
|
||||
|
||||
in_file = 'main_sim.in.c'
|
||||
out_file = f'main_sim_{model_name}.c'
|
||||
@@ -161,15 +178,19 @@ def sim_generate_casadi_functions(acados_sim):
|
||||
elif integrator_type == 'GNSF':
|
||||
generate_c_code_gnsf(model, opts)
|
||||
|
||||
|
||||
class AcadosSimSolver:
|
||||
"""
|
||||
Class to interact with the acados integrator C object.
|
||||
|
||||
:param acados_sim: type :py:class:`acados_template.acados_ocp.AcadosOcp` (takes values to generate an instance :py:class:`acados_template.acados_sim.AcadosSim`) or :py:class:`acados_template.acados_sim.AcadosSim`
|
||||
:param json_file: Default: 'acados_sim.json'
|
||||
:param build: Default: True
|
||||
:param acados_sim: type :py:class:`~acados_template.acados_ocp.AcadosOcp` (takes values to generate an instance :py:class:`~acados_template.acados_sim.AcadosSim`) or :py:class:`~acados_template.acados_sim.AcadosSim`
|
||||
:param json_file: Default: 'acados_sim.json'
|
||||
:param build: Default: True
|
||||
:param cmake_builder: type :py:class:`~acados_template.utils.CMakeBuilder` generate a `CMakeLists.txt` and use
|
||||
the `CMake` pipeline instead of a `Makefile` (`CMake` seems to be the better option in conjunction with
|
||||
`MS Visual Studio`); default: `None`
|
||||
"""
|
||||
def __init__(self, acados_sim_, json_file='acados_sim.json', build=True):
|
||||
def __init__(self, acados_sim_, json_file='acados_sim.json', build=True, cmake_builder: CMakeBuilder = None):
|
||||
|
||||
self.solver_created = False
|
||||
|
||||
@@ -203,12 +224,16 @@ class AcadosSimSolver:
|
||||
code_export_dir = acados_sim.code_export_directory
|
||||
if build:
|
||||
# render templates
|
||||
sim_render_templates(json_file, model_name, code_export_dir)
|
||||
sim_render_templates(json_file, model_name, code_export_dir, cmake_builder)
|
||||
|
||||
## Compile solver
|
||||
# Compile solver
|
||||
cwd = os.getcwd()
|
||||
code_export_dir = os.path.abspath(code_export_dir)
|
||||
os.chdir(code_export_dir)
|
||||
os.system('make sim_shared_lib')
|
||||
if cmake_builder is not None:
|
||||
cmake_builder.exec(code_export_dir)
|
||||
else:
|
||||
os.system('make sim_shared_lib')
|
||||
os.chdir(cwd)
|
||||
|
||||
self.sim_struct = acados_sim
|
||||
@@ -234,8 +259,15 @@ class AcadosSimSolver:
|
||||
print('acados was compiled without OpenMP.')
|
||||
|
||||
# Ctypes
|
||||
shared_lib = f'{code_export_dir}/libacados_sim_solver_{model_name}.so'
|
||||
self.shared_lib = CDLL(shared_lib)
|
||||
lib_prefix = 'lib'
|
||||
lib_ext = '.so'
|
||||
if os.name == 'nt':
|
||||
lib_prefix = ''
|
||||
lib_ext = ''
|
||||
self.shared_lib_name = os.path.join(code_export_dir, f'{lib_prefix}acados_sim_solver_{model_name}{lib_ext}')
|
||||
print(f'self.shared_lib_name = "{self.shared_lib_name}"')
|
||||
|
||||
self.shared_lib = CDLL(self.shared_lib_name)
|
||||
|
||||
|
||||
# create capsule
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# -*- coding: future_fstrings -*-
|
||||
#
|
||||
# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren,
|
||||
# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor,
|
||||
# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan,
|
||||
# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl
|
||||
#
|
||||
# This file is part of acados.
|
||||
#
|
||||
# The 2-Clause BSD License
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.;
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
from subprocess import call
|
||||
|
||||
|
||||
class CMakeBuilder:
|
||||
"""
|
||||
Class to work with the `CMake` build system.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._source_dir = None # private source directory, this is set to code_export_dir
|
||||
self.build_dir = 'build'
|
||||
self._build_dir = None # private build directory, usually rendered to abspath(build_dir)
|
||||
self.generator = None
|
||||
"""Defines the generator, options can be found via `cmake --help` under 'Generator'. Type: string. Linux default 'Unix Makefiles', Windows 'Visual Studio 15 2017 Win64'; default value: `None`."""
|
||||
# set something for Windows
|
||||
if os.name == 'nt':
|
||||
self.generator = 'Visual Studio 15 2017 Win64'
|
||||
self.build_targets = None
|
||||
"""A comma-separated list of the build targets, if `None` then all targets will be build; type: List of strings; default: `None`."""
|
||||
self.options_on = None
|
||||
"""List of strings as CMake options which are translated to '-D Opt[0]=ON -D Opt[1]=ON ...'; default: `None`."""
|
||||
|
||||
# Generate the command string for handling the cmake command.
|
||||
def get_cmd1_cmake(self):
|
||||
defines_str = ''
|
||||
if self.options_on is not None:
|
||||
defines_arr = [f' -D{opt}=ON' for opt in self.options_on]
|
||||
defines_str = ' '.join(defines_arr)
|
||||
generator_str = ''
|
||||
if self.generator is not None:
|
||||
generator_str = f' -G"{self.generator}"'
|
||||
return f'cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="{self._source_dir}"{defines_str}{generator_str} -Wdev -S"{self._source_dir}" -B"{self._build_dir}"'
|
||||
|
||||
# Generate the command string for handling the build.
|
||||
def get_cmd2_build(self):
|
||||
import multiprocessing
|
||||
cmd = f'cmake --build "{self._build_dir}" --config Release -j{multiprocessing.cpu_count()}'
|
||||
if self.build_targets is not None:
|
||||
cmd += f' -t {self.build_targets}'
|
||||
return cmd
|
||||
|
||||
# Generate the command string for handling the install command.
|
||||
def get_cmd3_install(self):
|
||||
return f'cmake --install "{self._build_dir}"'
|
||||
|
||||
def exec(self, code_export_directory):
|
||||
"""
|
||||
Execute the compilation using `CMake` with the given settings.
|
||||
:param code_export_directory: must be the absolute path to the directory where the code was exported to
|
||||
"""
|
||||
if(os.path.isabs(code_export_directory) is False):
|
||||
print(f'(W) the code export directory "{code_export_directory}" is not an absolute path!')
|
||||
self._source_dir = code_export_directory
|
||||
self._build_dir = os.path.abspath(self.build_dir)
|
||||
try:
|
||||
os.mkdir(self._build_dir)
|
||||
except FileExistsError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
os.chdir(self._build_dir)
|
||||
cmd_str = self.get_cmd1_cmake()
|
||||
print(f'call("{cmd_str})"')
|
||||
retcode = call(cmd_str, shell=True)
|
||||
if retcode != 0:
|
||||
raise RuntimeError(f'CMake command "{cmd_str}" was terminated by signal {retcode}')
|
||||
cmd_str = self.get_cmd2_build()
|
||||
print(f'call("{cmd_str}")')
|
||||
retcode = call(cmd_str, shell=True)
|
||||
if retcode != 0:
|
||||
raise RuntimeError(f'Build command "{cmd_str}" was terminated by signal {retcode}')
|
||||
cmd_str = self.get_cmd3_install()
|
||||
print(f'call("{cmd_str}")')
|
||||
retcode = call(cmd_str, shell=True)
|
||||
if retcode != 0:
|
||||
raise RuntimeError(f'Install command "{cmd_str}" was terminated by signal {retcode}')
|
||||
except OSError as e:
|
||||
print("Execution failed:", e, file=sys.stderr)
|
||||
except Exception as e:
|
||||
print("Execution failed:", e, file=sys.stderr)
|
||||
exit(1)
|
||||
@@ -0,0 +1,374 @@
|
||||
#
|
||||
# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren,
|
||||
# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor,
|
||||
# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan,
|
||||
# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl
|
||||
#
|
||||
# This file is part of acados.
|
||||
#
|
||||
# The 2-Clause BSD License
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.;
|
||||
#
|
||||
|
||||
{%- if solver_options.qp_solver %}
|
||||
{%- set qp_solver = solver_options.qp_solver %}
|
||||
{%- else %}
|
||||
{%- set qp_solver = "FULL_CONDENSING_HPIPM" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if solver_options.hessian_approx %}
|
||||
{%- set hessian_approx = solver_options.hessian_approx %}
|
||||
{%- elif solver_options.sens_hess %}
|
||||
{%- set hessian_approx = "EXACT" %}
|
||||
{%- else %}
|
||||
{%- set hessian_approx = "GAUSS_NEWTON" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if constraints.constr_type %}
|
||||
{%- set constr_type = constraints.constr_type %}
|
||||
{%- else %}
|
||||
{%- set constr_type = "NONE" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if constraints.constr_type_e %}
|
||||
{%- set constr_type_e = constraints.constr_type_e %}
|
||||
{%- else %}
|
||||
{%- set constr_type_e = "NONE" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if cost.cost_type %}
|
||||
{%- set cost_type = cost.cost_type %}
|
||||
{%- else %}
|
||||
{%- set cost_type = "NONE" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if cost.cost_type_e %}
|
||||
{%- set cost_type_e = cost.cost_type_e %}
|
||||
{%- else %}
|
||||
{%- set cost_type_e = "NONE" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if cost.cost_type_0 %}
|
||||
{%- set cost_type_0 = cost.cost_type_0 %}
|
||||
{%- else %}
|
||||
{%- set cost_type_0 = "NONE" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.nh %}
|
||||
{%- set dims_nh = dims.nh %}
|
||||
{%- else %}
|
||||
{%- set dims_nh = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.nphi %}
|
||||
{%- set dims_nphi = dims.nphi %}
|
||||
{%- else %}
|
||||
{%- set dims_nphi = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.nh_e %}
|
||||
{%- set dims_nh_e = dims.nh_e %}
|
||||
{%- else %}
|
||||
{%- set dims_nh_e = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.nphi_e %}
|
||||
{%- set dims_nphi_e = dims.nphi_e %}
|
||||
{%- else %}
|
||||
{%- set dims_nphi_e = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if solver_options.model_external_shared_lib_dir %}
|
||||
{%- set model_external_shared_lib_dir = solver_options.model_external_shared_lib_dir %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if solver_options.model_external_shared_lib_name %}
|
||||
{%- set model_external_shared_lib_name = solver_options.model_external_shared_lib_name %}
|
||||
{%- endif %}
|
||||
|
||||
{#- control operator #}
|
||||
{%- if os and os == "pc" %}
|
||||
{%- set control = "&" %}
|
||||
{%- else %}
|
||||
{%- set control = ";" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if acados_link_libs and os and os == "pc" %}{# acados linking libraries and flags #}
|
||||
{%- set link_libs = acados_link_libs.qpoases ~ " " ~ acados_link_libs.hpmpc ~ " " ~ acados_link_libs.osqp -%}
|
||||
{%- set openmp_flag = acados_link_libs.openmp %}
|
||||
{%- else %}
|
||||
{%- set openmp_flag = " " %}
|
||||
{%- if qp_solver == "FULL_CONDENSING_QPOASES" %}
|
||||
{%- set link_libs = "-lqpOASES_e" %}
|
||||
{%- else %}
|
||||
{%- set link_libs = "" %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project({{ model.name }})
|
||||
|
||||
# build options.
|
||||
option(BUILD_ACADOS_SOLVER_LIB "Should the solver library acados_solver_{{ model.name }} be build?" OFF)
|
||||
option(BUILD_ACADOS_OCP_SOLVER_LIB "Should the OCP solver library acados_ocp_solver_{{ model.name }} be build?" OFF)
|
||||
option(BUILD_EXAMPLE "Should the example main_{{ model.name }} be build?" OFF)
|
||||
{%- if solver_options.integrator_type != "DISCRETE" %}
|
||||
option(BUILD_SIM_EXAMPLE "Should the simulation example main_sim_{{ model.name }} be build?" OFF)
|
||||
option(BUILD_ACADOS_SIM_SOLVER_LIB "Should the simulation solver library acados_sim_solver_{{ model.name }} be build?" OFF)
|
||||
{%- endif %}
|
||||
|
||||
# object target names
|
||||
set(MODEL_OBJ model_{{ model.name }})
|
||||
set(OCP_OBJ ocp_{{ model.name }})
|
||||
set(SIM_OBJ sim_{{ model.name }})
|
||||
|
||||
# model
|
||||
set(MODEL_SRC
|
||||
{%- if solver_options.integrator_type == "ERK" %}
|
||||
{{ model.name }}_model/{{ model.name }}_expl_ode_fun.c
|
||||
{{ model.name }}_model/{{ model.name }}_expl_vde_forw.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_model/{{ model.name }}_expl_ode_hess.c
|
||||
{%- endif %}
|
||||
{%- elif solver_options.integrator_type == "IRK" %}
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_fun.c
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_fun_jac_x_xdot_z.c
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_jac_x_xdot_u_z.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_hess.c
|
||||
{%- endif %}
|
||||
{%- elif solver_options.integrator_type == "LIFTED_IRK" %}
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_fun.c
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_fun_jac_x_xdot_u.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_model/{{ model.name }}_impl_dae_hess.c
|
||||
{%- endif %}
|
||||
{%- elif solver_options.integrator_type == "GNSF" %}
|
||||
{% if model.gnsf.purely_linear != 1 %}
|
||||
{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun.c
|
||||
{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun_jac_y.c
|
||||
{{ model.name }}_model/{{ model.name }}_gnsf_phi_jac_y_uhat.c
|
||||
{% if model.gnsf.nontrivial_f_LO == 1 %}
|
||||
{{ model.name }}_model/{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz.c
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{{ model.name }}_model/{{ model.name }}_gnsf_get_matrices_fun.c
|
||||
{%- elif solver_options.integrator_type == "DISCRETE" %}
|
||||
{%- if model.dyn_ext_fun_type == "casadi" %}
|
||||
{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun.c
|
||||
{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac_hess.c
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{{ model.name }}_model/{{ model.dyn_source_discrete }}
|
||||
{%- endif %}
|
||||
{%- endif -%}
|
||||
)
|
||||
add_library(${MODEL_OBJ} OBJECT ${MODEL_SRC} )
|
||||
|
||||
# optimal control problem - mostly CasADi exports
|
||||
if(${BUILD_ACADOS_SOLVER_LIB} OR ${BUILD_ACADOS_OCP_SOLVER_LIB} OR ${BUILD_EXAMPLE})
|
||||
set(OCP_SRC
|
||||
{%- if constr_type == "BGP" and dims_nphi > 0 %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_phi_constraint.c
|
||||
{%- endif %}
|
||||
{%- if constr_type_e == "BGP" and dims_nphi_e > 0 %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_phi_e_constraint.c
|
||||
{%- endif %}
|
||||
|
||||
{%- if constr_type == "BGH" and dims_nh > 0 %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt.c
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_fun.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt_hess.c
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if constr_type_e == "BGH" and dims_nh_e > 0 %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt.c
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun.c
|
||||
{%- if hessian_approx == "EXACT" %}
|
||||
{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess.c
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if cost_type_0 == "NONLINEAR_LS" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_0_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_0_fun_jac_ut_xt.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_0_hess.c
|
||||
{%- elif cost_type_0 == "EXTERNAL" %}
|
||||
{%- if cost.cost_ext_fun_type_0 == "casadi" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac_hess.c
|
||||
{%- else %}
|
||||
{{ model.name }}_cost/{{ cost.cost_source_ext_cost_0 }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if cost_type == "NONLINEAR_LS" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_fun_jac_ut_xt.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_hess.c
|
||||
{%- elif cost_type == "EXTERNAL" %}
|
||||
{%- if cost.cost_ext_fun_type == "casadi" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac_hess.c
|
||||
{%- elif cost.cost_source_ext_cost != cost.cost_source_ext_cost_0 %}
|
||||
{{ model.name }}_cost/{{ cost.cost_source_ext_cost }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if cost_type_e == "NONLINEAR_LS" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_e_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_e_fun_jac_ut_xt.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_y_e_hess.c
|
||||
{%- elif cost_type_e == "EXTERNAL" %}
|
||||
{%- if cost.cost_ext_fun_type_e == "casadi" %}
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac.c
|
||||
{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac_hess.c
|
||||
{%- elif cost.cost_source_ext_cost_e != cost.cost_source_ext_cost_0 %}
|
||||
{{ model.name }}_cost/{{ cost.cost_source_ext_cost_e }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
acados_solver_{{ model.name }}.c)
|
||||
add_library(${OCP_OBJ} OBJECT ${OCP_SRC})
|
||||
endif()
|
||||
|
||||
{%- if solver_options.integrator_type != "DISCRETE" %}
|
||||
# for sim solver
|
||||
if(${BUILD_ACADOS_SOLVER_LIB} OR ${BUILD_EXAMPLE}
|
||||
{%- if solver_options.integrator_type != "DISCRETE" %}
|
||||
OR ${BUILD_SIM_EXAMPLE} OR ${BUILD_ACADOS_SIM_SOLVER_LIB}
|
||||
{%- endif -%}
|
||||
)
|
||||
set(SIM_SRC acados_sim_solver_{{ model.name }}.c)
|
||||
add_library(${SIM_OBJ} OBJECT ${SIM_SRC})
|
||||
endif()
|
||||
{%- endif %}
|
||||
|
||||
# for target example
|
||||
set(EX_SRC main_{{ model.name }}.c)
|
||||
set(EX_EXE main_{{ model.name }})
|
||||
|
||||
{%- if model_external_shared_lib_dir and model_external_shared_lib_name %}
|
||||
set(EXTERNAL_DIR {{ model_external_shared_lib_dir }})
|
||||
set(EXTERNAL_LIB {{ model_external_shared_lib_name }})
|
||||
{%- else %}
|
||||
set(EXTERNAL_DIR)
|
||||
set(EXTERNAL_LIB)
|
||||
{%- endif %}
|
||||
|
||||
# set some search paths for preprocessor and linker
|
||||
set(ACADOS_INCLUDE_PATH {{ acados_include_path }} CACHE PATH "Define the path which contains the include directory for acados.")
|
||||
set(ACADOS_LIB_PATH {{ acados_lib_path }} CACHE PATH "Define the path which contains the lib directory for acados.")
|
||||
|
||||
# c-compiler flags for debugging
|
||||
set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb")
|
||||
|
||||
set(CMAKE_C_FLAGS "
|
||||
{%- if qp_solver == "FULL_CONDENSING_QPOASES" -%}
|
||||
-DACADOS_WITH_QPOASES
|
||||
{%- endif -%}
|
||||
{%- if qp_solver == "PARTIAL_CONDENSING_OSQP" -%}
|
||||
-DACADOS_WITH_OSQP
|
||||
{%- endif -%}
|
||||
{%- if qp_solver == "PARTIAL_CONDENSING_QPDUNES" -%}
|
||||
-DACADOS_WITH_QPDUNES
|
||||
{%- endif -%}
|
||||
-fPIC -std=c99 {{ openmp_flag }}")
|
||||
#-fno-diagnostics-show-line-numbers -g
|
||||
|
||||
include_directories(
|
||||
${ACADOS_INCLUDE_PATH}
|
||||
${ACADOS_INCLUDE_PATH}/acados
|
||||
${ACADOS_INCLUDE_PATH}/blasfeo/include
|
||||
${ACADOS_INCLUDE_PATH}/hpipm/include
|
||||
{%- if qp_solver == "FULL_CONDENSING_QPOASES" %}
|
||||
${ACADOS_INCLUDE_PATH}/qpOASES_e/
|
||||
{%- endif %}
|
||||
)
|
||||
|
||||
# linker flags
|
||||
link_directories(${ACADOS_LIB_PATH})
|
||||
|
||||
# link to libraries
|
||||
if(UNIX)
|
||||
link_libraries(acados hpipm blasfeo m {{ link_libs }})
|
||||
else()
|
||||
link_libraries(acados hpipm blasfeo {{ link_libs }})
|
||||
endif()
|
||||
|
||||
# the targets
|
||||
|
||||
# bundled_shared_lib
|
||||
if(${BUILD_ACADOS_SOLVER_LIB})
|
||||
set(LIB_ACADOS_SOLVER acados_solver_{{ model.name }})
|
||||
add_library(${LIB_ACADOS_SOLVER} SHARED $<TARGET_OBJECTS:${MODEL_OBJ}> $<TARGET_OBJECTS:${OCP_OBJ}>
|
||||
{%- if solver_options.integrator_type != "DISCRETE" %}
|
||||
$<TARGET_OBJECTS:${SIM_OBJ}>
|
||||
{%- endif -%}
|
||||
)
|
||||
install(TARGETS ${LIB_ACADOS_SOLVER} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
endif(${BUILD_ACADOS_SOLVER_LIB})
|
||||
|
||||
# ocp_shared_lib
|
||||
if(${BUILD_ACADOS_OCP_SOLVER_LIB})
|
||||
set(LIB_ACADOS_OCP_SOLVER acados_ocp_solver_{{ model.name }})
|
||||
add_library(${LIB_ACADOS_OCP_SOLVER} SHARED $<TARGET_OBJECTS:${MODEL_OBJ}> $<TARGET_OBJECTS:${OCP_OBJ}>)
|
||||
# Specify libraries or flags to use when linking a given target and/or its dependents.
|
||||
target_link_libraries(${LIB_ACADOS_OCP_SOLVER} PRIVATE ${EXTERNAL_LIB})
|
||||
target_link_directories(${LIB_ACADOS_OCP_SOLVER} PRIVATE ${EXTERNAL_DIR})
|
||||
install(TARGETS ${LIB_ACADOS_OCP_SOLVER} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
endif(${BUILD_ACADOS_OCP_SOLVER_LIB})
|
||||
|
||||
# example
|
||||
if(${BUILD_EXAMPLE})
|
||||
add_executable(${EX_EXE} ${EX_SRC} $<TARGET_OBJECTS:${MODEL_OBJ}> $<TARGET_OBJECTS:${OCP_OBJ}>
|
||||
{%- if solver_options.integrator_type != "DISCRETE" %}
|
||||
$<TARGET_OBJECTS:${SIM_OBJ}>
|
||||
{%- endif -%}
|
||||
)
|
||||
install(TARGETS ${EX_EXE} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
endif(${BUILD_EXAMPLE})
|
||||
|
||||
{% if solver_options.integrator_type != "DISCRETE" -%}
|
||||
# example_sim
|
||||
if(${BUILD_SIM_EXAMPLE})
|
||||
set(EX_SIM_SRC main_sim_{{ model.name }}.c)
|
||||
set(EX_SIM_EXE main_sim_{{ model.name }})
|
||||
add_executable(${EX_SIM_EXE} ${EX_SIM_SRC} $<TARGET_OBJECTS:${MODEL_OBJ}> $<TARGET_OBJECTS:${SIM_OBJ}>)
|
||||
install(TARGETS ${EX_SIM_EXE} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
endif(${BUILD_SIM_EXAMPLE})
|
||||
|
||||
# sim_shared_lib
|
||||
if(${BUILD_ACADOS_SIM_SOLVER_LIB})
|
||||
set(LIB_ACADOS_SIM_SOLVER acados_sim_solver_{{ model.name }})
|
||||
add_library(${LIB_ACADOS_SIM_SOLVER} SHARED $<TARGET_OBJECTS:${MODEL_OBJ}> $<TARGET_OBJECTS:${SIM_OBJ}>)
|
||||
install(TARGETS ${LIB_ACADOS_SIM_SOLVER} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
endif(${BUILD_ACADOS_SIM_SOLVER_LIB})
|
||||
{%- endif %}
|
||||
|
||||
@@ -80,21 +80,21 @@ typedef struct sim_solver_capsule
|
||||
} sim_solver_capsule;
|
||||
|
||||
|
||||
int {{ model.name }}_acados_sim_create(sim_solver_capsule *capsule);
|
||||
int {{ model.name }}_acados_sim_solve(sim_solver_capsule *capsule);
|
||||
int {{ model.name }}_acados_sim_free(sim_solver_capsule *capsule);
|
||||
int {{ model.name }}_acados_sim_update_params(sim_solver_capsule *capsule, double *value, int np);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_sim_create(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_sim_solve(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_sim_free(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_sim_update_params(sim_solver_capsule *capsule, double *value, int np);
|
||||
|
||||
sim_config * {{ model.name }}_acados_get_sim_config(sim_solver_capsule *capsule);
|
||||
sim_in * {{ model.name }}_acados_get_sim_in(sim_solver_capsule *capsule);
|
||||
sim_out * {{ model.name }}_acados_get_sim_out(sim_solver_capsule *capsule);
|
||||
void * {{ model.name }}_acados_get_sim_dims(sim_solver_capsule *capsule);
|
||||
sim_opts * {{ model.name }}_acados_get_sim_opts(sim_solver_capsule *capsule);
|
||||
sim_solver * {{ model.name }}_acados_get_sim_solver(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_config * {{ model.name }}_acados_get_sim_config(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_in * {{ model.name }}_acados_get_sim_in(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_out * {{ model.name }}_acados_get_sim_out(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT void * {{ model.name }}_acados_get_sim_dims(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_opts * {{ model.name }}_acados_get_sim_opts(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_solver * {{ model.name }}_acados_get_sim_solver(sim_solver_capsule *capsule);
|
||||
|
||||
|
||||
sim_solver_capsule * {{ model.name }}_acados_sim_solver_create_capsule(void);
|
||||
int {{ model.name }}_acados_sim_solver_free_capsule(sim_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT sim_solver_capsule * {{ model.name }}_acados_sim_solver_create_capsule(void);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_sim_solver_free_capsule(sim_solver_capsule *capsule);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1862,6 +1862,11 @@ void {{ model.name }}_acados_create_6_set_opts({{ model.name }}_solver_capsule*
|
||||
{%- endif %}
|
||||
|
||||
|
||||
{%- if solver_options.qp_solver is containing("HPIPM") %}
|
||||
// set HPIPM mode: should be done before setting other QP solver options
|
||||
ocp_nlp_solver_opts_set(nlp_config, nlp_opts, "qp_hpipm_mode", "{{ solver_options.hpipm_mode }}");
|
||||
{%- endif %}
|
||||
|
||||
{% if solver_options.nlp_solver_type == "SQP" %}
|
||||
// set SQP specific options
|
||||
double nlp_solver_tol_stat = {{ solver_options.nlp_solver_tol_stat }};
|
||||
@@ -2071,6 +2076,63 @@ int {{ model.name }}_acados_update_qp_solver_cond_N({{ model.name }}_solver_caps
|
||||
}
|
||||
|
||||
|
||||
int {{ model.name }}_acados_reset({{ model.name }}_solver_capsule* capsule)
|
||||
{
|
||||
|
||||
// set initialization to all zeros
|
||||
{# TODO: use guess values / initial state value from json instead?! #}
|
||||
const int N = capsule->nlp_solver_plan->N;
|
||||
ocp_nlp_config* nlp_config = capsule->nlp_config;
|
||||
ocp_nlp_dims* nlp_dims = capsule->nlp_dims;
|
||||
ocp_nlp_out* nlp_out = capsule->nlp_out;
|
||||
ocp_nlp_in* nlp_in = capsule->nlp_in;
|
||||
ocp_nlp_solver* nlp_solver = capsule->nlp_solver;
|
||||
|
||||
int nx, nu, nv, ns, nz, ni, dim;
|
||||
|
||||
double* buffer = calloc(NX+NU+NZ+2*NS+2*NSN+NBX+NBU+NG+NH+NPHI+NBX0+NBXN+NHN+NPHIN+NGN, sizeof(double));
|
||||
|
||||
for(int i=0; i<N+1; i++)
|
||||
{
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "x", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "u", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "sl", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "su", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "lam", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "t", buffer);
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "z", buffer);
|
||||
if (i<N)
|
||||
{
|
||||
ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "pi", buffer);
|
||||
{%- if solver_options.integrator_type == "IRK" %}
|
||||
ocp_nlp_set(nlp_config, nlp_solver, i, "xdot_guess", buffer);
|
||||
ocp_nlp_set(nlp_config, nlp_solver, i, "z_guess", buffer);
|
||||
{% elif solver_options.integrator_type == "LIFTED_IRK" %}
|
||||
ocp_nlp_set(nlp_config, nlp_solver, i, "xdot_guess", buffer);
|
||||
{% elif solver_options.integrator_type == "GNSF" %}
|
||||
ocp_nlp_set(nlp_config, nlp_solver, i, "gnsf_phi_guess", buffer);
|
||||
{%- endif %}
|
||||
}
|
||||
}
|
||||
|
||||
{%- if solver_options.qp_solver == 'PARTIAL_CONDENSING_HPIPM' %}
|
||||
// get qp_status: if NaN -> reset memory
|
||||
int qp_status;
|
||||
ocp_nlp_get(capsule->nlp_config, capsule->nlp_solver, "qp_status", &qp_status);
|
||||
if (qp_status == 3)
|
||||
{
|
||||
// printf("\nin reset qp_status %d -> resetting QP memory\n", qp_status);
|
||||
ocp_nlp_solver_reset_qp_memory(nlp_solver, nlp_in, nlp_out);
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int {{ model.name }}_acados_update_params({{ model.name }}_solver_capsule* capsule, int stage, double *p, int np)
|
||||
{
|
||||
int solver_status = 0;
|
||||
@@ -2096,7 +2158,7 @@ int {{ model.name }}_acados_update_params({{ model.name }}_solver_capsule* capsu
|
||||
{%- endif %}
|
||||
{% elif solver_options.integrator_type == "LIFTED_IRK" %}
|
||||
capsule->impl_dae_fun[stage].set_param(capsule->impl_dae_fun+stage, p);
|
||||
capsule->impl_dae_fun_jac_x_xdot_z[stage].set_param(capsule->impl_dae_fun_jac_x_xdot_z+stage, p);
|
||||
capsule->impl_dae_fun_jac_x_xdot_u[stage].set_param(capsule->impl_dae_fun_jac_x_xdot_u+stage, p);
|
||||
{% elif solver_options.integrator_type == "ERK" %}
|
||||
capsule->forw_vde_casadi[stage].set_param(capsule->forw_vde_casadi+stage, p);
|
||||
capsule->expl_ode_fun[stage].set_param(capsule->expl_ode_fun+stage, p);
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#ifndef ACADOS_SOLVER_{{ model.name }}_H_
|
||||
#define ACADOS_SOLVER_{{ model.name }}_H_
|
||||
|
||||
#include "acados/utils/types.h"
|
||||
|
||||
#include "acados_c/ocp_nlp_interface.h"
|
||||
#include "acados_c/external_function_interface.h"
|
||||
|
||||
@@ -172,38 +174,41 @@ typedef struct {{ model.name }}_solver_capsule
|
||||
|
||||
} {{ model.name }}_solver_capsule;
|
||||
|
||||
{{ model.name }}_solver_capsule * {{ model.name }}_acados_create_capsule(void);
|
||||
int {{ model.name }}_acados_free_capsule({{ model.name }}_solver_capsule *capsule);
|
||||
ACADOS_SYMBOL_EXPORT {{ model.name }}_solver_capsule * {{ model.name }}_acados_create_capsule(void);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_free_capsule({{ model.name }}_solver_capsule *capsule);
|
||||
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_create({{ model.name }}_solver_capsule * capsule);
|
||||
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_reset({{ model.name }}_solver_capsule* capsule);
|
||||
|
||||
int {{ model.name }}_acados_create({{ model.name }}_solver_capsule * capsule);
|
||||
/**
|
||||
* Generic version of {{ model.name }}_acados_create which allows to use a different number of shooting intervals than
|
||||
* the number used for code generation. If new_time_steps=NULL and n_time_steps matches the number used for code
|
||||
* generation, the time-steps from code generation is used.
|
||||
*/
|
||||
int {{ model.name }}_acados_create_with_discretization({{ model.name }}_solver_capsule * capsule, int n_time_steps, double* new_time_steps);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_create_with_discretization({{ model.name }}_solver_capsule * capsule, int n_time_steps, double* new_time_steps);
|
||||
/**
|
||||
* Update the time step vector. Number N must be identical to the currently set number of shooting nodes in the
|
||||
* nlp_solver_plan. Returns 0 if no error occurred and a otherwise a value other than 0.
|
||||
*/
|
||||
int {{ model.name }}_acados_update_time_steps({{ model.name }}_solver_capsule * capsule, int N, double* new_time_steps);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_update_time_steps({{ model.name }}_solver_capsule * capsule, int N, double* new_time_steps);
|
||||
/**
|
||||
* This function is used for updating an already initialized solver with a different number of qp_cond_N.
|
||||
*/
|
||||
int {{ model.name }}_acados_update_qp_solver_cond_N({{ model.name }}_solver_capsule * capsule, int qp_solver_cond_N);
|
||||
int {{ model.name }}_acados_update_params({{ model.name }}_solver_capsule * capsule, int stage, double *value, int np);
|
||||
int {{ model.name }}_acados_solve({{ model.name }}_solver_capsule * capsule);
|
||||
int {{ model.name }}_acados_free({{ model.name }}_solver_capsule * capsule);
|
||||
void {{ model.name }}_acados_print_stats({{ model.name }}_solver_capsule * capsule);
|
||||
|
||||
ocp_nlp_in *{{ model.name }}_acados_get_nlp_in({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_out *{{ model.name }}_acados_get_nlp_out({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_out *{{ model.name }}_acados_get_sens_out({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_solver *{{ model.name }}_acados_get_nlp_solver({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_config *{{ model.name }}_acados_get_nlp_config({{ model.name }}_solver_capsule * capsule);
|
||||
void *{{ model.name }}_acados_get_nlp_opts({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_dims *{{ model.name }}_acados_get_nlp_dims({{ model.name }}_solver_capsule * capsule);
|
||||
ocp_nlp_plan_t *{{ model.name }}_acados_get_nlp_plan({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_update_qp_solver_cond_N({{ model.name }}_solver_capsule * capsule, int qp_solver_cond_N);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_update_params({{ model.name }}_solver_capsule * capsule, int stage, double *value, int np);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_solve({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT int {{ model.name }}_acados_free({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT void {{ model.name }}_acados_print_stats({{ model.name }}_solver_capsule * capsule);
|
||||
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_in *{{ model.name }}_acados_get_nlp_in({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_out *{{ model.name }}_acados_get_nlp_out({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_out *{{ model.name }}_acados_get_sens_out({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_solver *{{ model.name }}_acados_get_nlp_solver({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_config *{{ model.name }}_acados_get_nlp_config({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT void *{{ model.name }}_acados_get_nlp_opts({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_dims *{{ model.name }}_acados_get_nlp_dims({{ model.name }}_solver_capsule * capsule);
|
||||
ACADOS_SYMBOL_EXPORT ocp_nlp_plan_t *{{ model.name }}_acados_get_nlp_plan({{ model.name }}_solver_capsule * capsule);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -48,6 +48,7 @@ cdef extern from "acados_solver_{{ model.name }}.h":
|
||||
|
||||
int acados_update_params "{{ model.name }}_acados_update_params"(nlp_solver_capsule * capsule, int stage, double *value, int np_)
|
||||
int acados_solve "{{ model.name }}_acados_solve"(nlp_solver_capsule * capsule)
|
||||
int acados_reset "{{ model.name }}_acados_reset"(nlp_solver_capsule * capsule)
|
||||
int acados_free "{{ model.name }}_acados_free"(nlp_solver_capsule * capsule)
|
||||
void acados_print_stats "{{ model.name }}_acados_print_stats"(nlp_solver_capsule * capsule)
|
||||
|
||||
|
||||
@@ -126,10 +126,14 @@ static void mdlInitializeSizes (SimStruct *S)
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #}
|
||||
{%- set n_inputs = n_inputs + 1 %}
|
||||
{%- endif -%}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W -%} {#- cost_W #}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #}
|
||||
{%- set n_inputs = n_inputs + 1 %}
|
||||
{%- endif -%}
|
||||
{%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e -%} {#- cost_W_e #}
|
||||
{%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {#- cost_W_e #}
|
||||
{%- set n_inputs = n_inputs + 1 -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if simulink_opts.inputs.reset_solver -%} {#- reset_solver #}
|
||||
{%- set n_inputs = n_inputs + 1 -%}
|
||||
{%- endif -%}
|
||||
|
||||
@@ -256,7 +260,7 @@ static void mdlInitializeSizes (SimStruct *S)
|
||||
ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_0 * dims.ny_0 }});
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #}
|
||||
{%- set i_input = i_input + 1 %}
|
||||
// cost_W
|
||||
ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny * dims.ny }});
|
||||
@@ -268,6 +272,12 @@ static void mdlInitializeSizes (SimStruct *S)
|
||||
ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_e * dims.ny_e }});
|
||||
{%- endif %}
|
||||
|
||||
{%- if simulink_opts.inputs.reset_solver -%} {#- reset_solver #}
|
||||
{%- set i_input = i_input + 1 %}
|
||||
// reset_solver
|
||||
ssSetInputPortVectorDimension(S, {{ i_input }}, 1);
|
||||
{%- endif -%}
|
||||
|
||||
{%- if simulink_opts.inputs.x_init -%} {#- x_init #}
|
||||
{%- set i_input = i_input + 1 %}
|
||||
// x_init
|
||||
@@ -406,13 +416,13 @@ static void mdlOutputs(SimStruct *S, int_T tid)
|
||||
{%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_e)) %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {# cost_W_0 #}
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #}
|
||||
{%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_0 * dims.ny_0)) %}
|
||||
{%- endif %}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {# cost_W #}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #}
|
||||
{%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny * dims.ny)) %}
|
||||
{%- endif %}
|
||||
{%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {# cost_W_e #}
|
||||
{%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {#- cost_W_e #}
|
||||
{%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_e * dims.ny_e)) %}
|
||||
{%- endif %}
|
||||
|
||||
@@ -602,7 +612,7 @@ static void mdlOutputs(SimStruct *S, int_T tid)
|
||||
ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "uh", buffer);
|
||||
{%- endif -%}
|
||||
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {# cost_W_0 #}
|
||||
{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #}
|
||||
// cost_W_0
|
||||
{%- set i_input = i_input + 1 %}
|
||||
in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }});
|
||||
@@ -612,7 +622,7 @@ static void mdlOutputs(SimStruct *S, int_T tid)
|
||||
ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "W", buffer);
|
||||
{%- endif %}
|
||||
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {# cost_W #}
|
||||
{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #}
|
||||
// cost_W
|
||||
{%- set i_input = i_input + 1 %}
|
||||
in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }});
|
||||
@@ -633,6 +643,17 @@ static void mdlOutputs(SimStruct *S, int_T tid)
|
||||
ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, {{ dims.N }}, "W", buffer);
|
||||
{%- endif %}
|
||||
|
||||
{%- if simulink_opts.inputs.reset_solver %} {#- reset_solver #}
|
||||
// reset_solver
|
||||
{%- set i_input = i_input + 1 %}
|
||||
in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }});
|
||||
double reset = (double)(*in_sign[0]);
|
||||
if (reset)
|
||||
{
|
||||
{{ model.name }}_acados_reset(capsule);
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
{%- if simulink_opts.inputs.x_init %} {#- x_init #}
|
||||
// x_init
|
||||
{%- set i_input = i_input + 1 %}
|
||||
|
||||
@@ -264,6 +264,11 @@ input_note = strcat(input_note, num2str(i_in), ') cost_W_e in column-major forma
|
||||
i_in = i_in + 1;
|
||||
{%- endif %}
|
||||
|
||||
{%- if simulink_opts.inputs.reset_solver %} {#- reset_solver #}
|
||||
input_note = strcat(input_note, num2str(i_in), ') reset_solver determines if iterate is set to all zeros before other initializations (x_init, u_init) are set and before solver is called, size [1]\n ');
|
||||
i_in = i_in + 1;
|
||||
{%- endif %}
|
||||
|
||||
{%- if simulink_opts.inputs.x_init %} {#- x_init #}
|
||||
input_note = strcat(input_note, num2str(i_in), ') initialization of x for all shooting nodes, size [{{ dims.nx * (dims.N+1) }}]\n ');
|
||||
i_in = i_in + 1;
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
#
|
||||
|
||||
import os
|
||||
from casadi import *
|
||||
import casadi as ca
|
||||
from .utils import ALLOWED_CASADI_VERSIONS, casadi_length, casadi_version_warning
|
||||
|
||||
def generate_c_code_discrete_dynamics( model, opts ):
|
||||
|
||||
casadi_version = CasadiMeta.version()
|
||||
casadi_version = ca.CasadiMeta.version()
|
||||
casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double')
|
||||
|
||||
if casadi_version not in (ALLOWED_CASADI_VERSIONS):
|
||||
@@ -49,13 +49,12 @@ def generate_c_code_discrete_dynamics( model, opts ):
|
||||
p = model.p
|
||||
phi = model.disc_dyn_expr
|
||||
model_name = model.name
|
||||
nx = x.size()[0]
|
||||
nx = casadi_length(x)
|
||||
|
||||
|
||||
if isinstance(phi, casadi.MX):
|
||||
symbol = MX.sym
|
||||
elif isinstance(phi, casadi.SX):
|
||||
symbol = SX.sym
|
||||
if isinstance(phi, ca.MX):
|
||||
symbol = ca.MX.sym
|
||||
elif isinstance(phi, ca.SX):
|
||||
symbol = ca.SX.sym
|
||||
else:
|
||||
Exception("generate_c_code_disc_dyn: disc_dyn_expr must be a CasADi expression, you have type: {}".format(type(phi)))
|
||||
|
||||
@@ -63,12 +62,12 @@ def generate_c_code_discrete_dynamics( model, opts ):
|
||||
lam = symbol('lam', nx, 1)
|
||||
|
||||
# generate jacobians
|
||||
ux = vertcat(u,x)
|
||||
jac_ux = jacobian(phi, ux)
|
||||
ux = ca.vertcat(u,x)
|
||||
jac_ux = ca.jacobian(phi, ux)
|
||||
# generate adjoint
|
||||
adj_ux = jtimes(phi, ux, lam, True)
|
||||
adj_ux = ca.jtimes(phi, ux, lam, True)
|
||||
# generate hessian
|
||||
hess_ux = jacobian(adj_ux, ux)
|
||||
hess_ux = ca.jacobian(adj_ux, ux)
|
||||
|
||||
## change directory
|
||||
code_export_dir = opts["code_export_directory"]
|
||||
@@ -85,15 +84,15 @@ def generate_c_code_discrete_dynamics( model, opts ):
|
||||
|
||||
# set up & generate Functions
|
||||
fun_name = model_name + '_dyn_disc_phi_fun'
|
||||
phi_fun = Function(fun_name, [x, u, p], [phi])
|
||||
phi_fun = ca.Function(fun_name, [x, u, p], [phi])
|
||||
phi_fun.generate(fun_name, casadi_opts)
|
||||
|
||||
fun_name = model_name + '_dyn_disc_phi_fun_jac'
|
||||
phi_fun_jac_ut_xt = Function(fun_name, [x, u, p], [phi, jac_ux.T])
|
||||
phi_fun_jac_ut_xt = ca.Function(fun_name, [x, u, p], [phi, jac_ux.T])
|
||||
phi_fun_jac_ut_xt.generate(fun_name, casadi_opts)
|
||||
|
||||
fun_name = model_name + '_dyn_disc_phi_fun_jac_hess'
|
||||
phi_fun_jac_ut_xt_hess = Function(fun_name, [x, u, lam, p], [phi, jac_ux.T, hess_ux])
|
||||
phi_fun_jac_ut_xt_hess = ca.Function(fun_name, [x, u, lam, p], [phi, jac_ux.T, hess_ux])
|
||||
phi_fun_jac_ut_xt_hess.generate(fun_name, casadi_opts)
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"cost_W_0": 0,
|
||||
"cost_W": 0,
|
||||
"cost_W_e": 0,
|
||||
"reset_solver": 0,
|
||||
"x_init": 0,
|
||||
"u_init": 0
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: future_fstrings -*-
|
||||
#
|
||||
# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren,
|
||||
# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor,
|
||||
@@ -49,7 +50,7 @@ def get_acados_path():
|
||||
ACADOS_PATH = os.path.realpath(acados_path)
|
||||
msg = 'Warning: Did not find environment variable ACADOS_SOURCE_DIR, '
|
||||
msg += 'guessed ACADOS_PATH to be {}.\n'.format(ACADOS_PATH)
|
||||
msg += 'Please export ACADOS_SOURCE_DIR to not avoid this warning.'
|
||||
msg += 'Please export ACADOS_SOURCE_DIR to avoid this warning.'
|
||||
print(msg)
|
||||
return ACADOS_PATH
|
||||
|
||||
@@ -74,7 +75,7 @@ def get_tera_exec_path():
|
||||
platform2tera = {
|
||||
"linux": "linux",
|
||||
"darwin": "osx",
|
||||
"win32": "window.exe"
|
||||
"win32": "windows"
|
||||
}
|
||||
|
||||
|
||||
@@ -212,16 +213,14 @@ def render_template(in_file, out_file, template_dir, json_path):
|
||||
template_glob = os.path.join(acados_path, 'c_templates_tera', '*')
|
||||
|
||||
# call tera as system cmd
|
||||
os_cmd = "{tera_path} '{template_glob}' '{in_file}' '{json_path}' '{out_file}'".format(
|
||||
tera_path=tera_path,
|
||||
template_glob=template_glob,
|
||||
json_path=json_path,
|
||||
in_file=in_file,
|
||||
out_file=out_file
|
||||
)
|
||||
os_cmd = f"{tera_path} '{template_glob}' '{in_file}' '{json_path}' '{out_file}'"
|
||||
# Windows cmd.exe can not cope with '...', so use "..." instead:
|
||||
if os.name == 'nt':
|
||||
os_cmd = os_cmd.replace('\'', '\"')
|
||||
|
||||
status = os.system(os_cmd)
|
||||
if (status != 0):
|
||||
raise Exception('Rendering of {} failed!\n\nAttempted to execute OS command:\n{}\n\nExiting.\n'.format(in_file, os_cmd))
|
||||
raise Exception(f'Rendering of {in_file} failed!\n\nAttempted to execute OS command:\n{os_cmd}\n\nExiting.\n')
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
@@ -235,9 +234,7 @@ def np_array_to_list(np_array):
|
||||
elif isinstance(np_array, (DM)):
|
||||
return np_array.full()
|
||||
else:
|
||||
raise(Exception(
|
||||
"Cannot convert to list type {}".format(type(np_array))
|
||||
))
|
||||
raise(Exception(f"Cannot convert to list type {type(np_array)}"))
|
||||
|
||||
|
||||
def format_class_dict(d):
|
||||
|
||||
Reference in New Issue
Block a user