Acados and Beckhoff TwinCat

Hi :wave:

for my master’s thesis i want to run my working acados MPC in TwinCat.
As described in their manual, one can just add the s-Function with the source code in the Simulink Model and the Simulink Coder compiles it in C++ for the use in TwinCat. Alternatively one can build the project manually in Visual Studio. Unfortunatelly i get lots of syntax errors like these:

1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/sim_interface.h(36): error C2059: syntax error: 'string'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(48): error C2143: syntax error: missing ')' before '*'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(48): error C2143: syntax error: missing '{' before '*'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(48): error C2059: syntax error: 'type'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(48): error C2059: syntax error: ')'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(50): error C2143: syntax error: missing ')' before '*'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(50): error C2143: syntax error: missing '{' before '*'
1>C:\Diplomarbeit\_Matlab\acados\include\acados_c/external_function_interface.h(50): error C2059: syntax error: ')'

So i wanted to ask the community if someone has experince with TwinCat + Acados?
I would also be very grateful for other suggestions regarding C++ and Acados.

So far i have built acados with the same compiler as is used for TwinCat (MSVC), targets for blasfeo and hpipm are generic and EXT_DEP=OFF for blasfeo. I have added the include folders and adapted the timing.h for the use with time.h from TwinCat.

Thanks and best regards,
Alex :slightly_smiling_face:

Hi @AlexT ,

I’m facing the exact same errors as you described in your post while integrating Acados with TwinCat. Have you managed to resolve them? Any guidance would be greatly appreciated!

Thanks,
Housin

Hi Housin,

actually i got it really running and i wanted to update this post this week.
But be aware, it is quite a time consuming way.

The way to go is:

  • S-Function Builder: You need to use a S-Function Builder block. There you have to activate “Generate wrapper TLC”. The goal is to successfully build the S-Function Builder. So you leave Start, Outputs, Derivatives, Update and Terminate empty. In Data Properties you define your In- and Outputs. Then in Libraries you write in “Includes:” all the headers which are included in the acados-generated S-Function. In the “Library/Object/Source” files you need to include ALL necessary source files (libraries do not work). So you try to build it, you get an error for a missing function, you search where the function is defined, you include the source file.
    In my case it looked like this:
INC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\include
INC_PATHD:\%YOURPATH%\_Matlab\SWR\c_generated_code
INC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder
INC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\blasfeo\include

SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\c_generated_code\
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\c_generated_code\SWR_min_2_model
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados\ocp_nlp
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados\ocp_qp
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados\sim
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados\utils
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados\dense_qp
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\acados_c
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\blasfeo\blasfeo_hp_pm
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\blasfeo\blasfeo_ref
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\blasfeo\kernel\generic
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\blasfeo\auxiliary
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\ocp_qp
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\cond
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\dense_qp
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\auxiliary
SRC_PATHD:\%YOURPATH%\_Matlab\SWR\sFuncBuilder\hpipm\ipm_core


-DLA=HIGH_PERFORMANCE
-DTARGET_GENERIC
-DK_MAX_STACK=300
-DOS_WINDOWS
-DSAMPLINGTIME=0.05

my_exit.c

acados_solver_SWR_min_2.c
SWR_min_2_expl_ode_fun.c
SWR_min_2_expl_ode_hess.c
SWR_min_2_expl_vde_adj.c
SWR_min_2_expl_vde_forw.c


ocp_nlp_common.c
ocp_nlp_constraints_bgh.c
ocp_nlp_constraints_bgp.c
ocp_nlp_constraints_common.c
ocp_nlp_cost_common.c
ocp_nlp_cost_conl.c
ocp_nlp_cost_external.c
ocp_nlp_cost_ls.c
ocp_nlp_cost_nls.c
ocp_nlp_dynamics_common.c
ocp_nlp_dynamics_cont.c
ocp_nlp_dynamics_disc.c
ocp_nlp_reg_common.c
ocp_nlp_reg_convexify.c
ocp_nlp_reg_mirror.c
ocp_nlp_reg_noreg.c
ocp_nlp_reg_project.c
ocp_nlp_reg_project_reduc_hess.c
ocp_nlp_sqp.c
ocp_nlp_sqp_rti.c


ocp_qp_common.c
ocp_qp_common_frontend.c
ocp_qp_full_condensing.c
ocp_qp_hpipm.c
ocp_qp_partial_condensing.c
ocp_qp_xcond_solver.c


sim_collocation_utils.c
sim_common.c
sim_erk_integrator.c
sim_gnsf.c
sim_irk_integrator.c
sim_lifted_irk_integrator.c


external_function_generic.c
math.c
mem.c
print.c
timing.c


dense_qp_common.c
dense_qp_hpipm.c


condensing_interface.c
dense_qp_interface.c
external_function_interface.c
ocp_nlp_interface.c
ocp_qp_interface.c
sim_interface.c



d_blas1_lib4.c
d_blas2_diag_lib.c
d_blas2_lib4.c
d_blas3_diag_lib4.c
d_blas3_lib4.c
d_lapack_lib4.c


d_blas1_ref.c
d_blas2_diag_ref.c
d_blas2_ref.c
d_blas3_diag_ref.c
d_blas3_ref.c
d_blas3_ref_blas.c
d_lapack_ref.c
d_lapack_ref_blas.c


kernel_align_generic.c
kernel_d_aux_lib.c
kernel_daxpy_lib.c
kernel_ddot_lib.c
kernel_dgecp_lib4.c
kernel_dgemm_4x4_lib.c
kernel_dgemm_4x4_lib4.c
kernel_dgemm_diag_lib4.c
kernel_dgemv_4_lib.c
kernel_dgemv_4_lib4.c
kernel_dgeqrf_4_lib4.c
kernel_dger_lib.c
kernel_dger_lib4.c
kernel_dgetr_lib.c
kernel_dgetr_lib4.c
kernel_dgetrf_pivot_lib.c
kernel_dgetrf_pivot_lib4.c
kernel_dpack_buffer_lib4.c
kernel_dpack_lib4.c
kernel_dsymv_4_lib.c
kernel_dsymv_4_lib4.c


blasfeo_processor_features.c
blasfeo_stdlib.c
d_aux_common.c
d_aux_lib4.c
d_aux_ext_dep.c
i_aux_ext_dep_lib.c



d_ocp_qp.c
d_ocp_qp_dim.c
d_ocp_qp_sol.c
d_ocp_qp_kkt.c
d_ocp_qp_ipm.c
d_ocp_qp_res.c
d_ocp_qp_utils.c
d_ocp_qp_red.c


d_cond.c
d_cond_aux.c
d_part_cond.c


d_dense_qp.c
d_dense_qp_dim.c
d_dense_qp_sol.c
d_dense_qp_kkt.c
d_dense_qp_ipm.c
d_dense_qp_res.c
d_dense_qp_utils.c


aux_string.c
aux_mem.c


d_core_qp_ipm.c
d_core_qp_ipm_aux.c
  • INC_PATH - Folder with header files
  • SRC_PATH - Folder with source files (which are in the list)
  • (there are no spaces between INC_PATH and the PATH because of a bug of 2018b.)
  • -D… - Directives for the compiler
  • my_exit.c is a self written file. Since the real-time pc does not have an exit function (what should he do after exit anyways), you have to define a exit or otherwise you get a linker error.
#include <stdlib.h>

#ifdef TARGET_TWINCAT

int __cdecl exit(int a)
{
	return a;
}

#endif
  • If you can build your empty S-Function Builder, you can copy the code from the acados-generated S-Function. But you have to use a PWorks instead of S (Simu Struct). For example in the Start:
// Instead of
//ssSetUserData(S, (void*)capsule);

// you use
pW[0] = (void*)malloc( sizeof(capsule) );
pW[0] = capsule;
  • If you can build your S-Function Builder with the code, you need to build the whole Simulink model for TwinCat. Assuming you have installed TwinCat and the Simulink package, you need to configure under “Code Generation” the system target file as TwinCAT.tlc. The used directives in the S-Function Builder block have to be copied to “Code Generation/Custom Code/Additional build information/Defines”.
    After a successfull build of the S-Function Builder it might be necessary to temporary change the system target file to anything else and then back to TwinCAT.tlc. (When you get the error because of the change of the make command)

Then you need to address the errors one after one, similar to the build of the S-Function Builder. For example i did rewrite the timing.c of acados, to always return 1. I guess it is only used for analyzing anyways.

  • If you can build your Simulink model, you get your Model for TwinCat, which you can add in the TwinCat project. If it does run in TwinCat but does not compute anything, you should deactivate the optimization flags for the c compiler. Either by adding -DNDEBUG in the Defines or by adding the *.vcproj file in VisualStudio under the C++ category and then you can build and publish your module with further settings.

  • It did run in my case, but in comparison to Simulink it did run much slower.

Hope i could help you and others who try this :slight_smile:

Best regards, Alex

1 Like

Hi Alex,

Great that you made it work and thanks for sharing the information on the process! :slight_smile:
I don’t have experience with compilation for Beckhoff systems, but would like to comment.

It sounds a bit weird that libraries do not work.
Did you try to cross compile the acados core library?
Could it be that there was something wrong in that process?

Of course, it would be useful to implement this correctly.
A pull request on this would be very welcome.

Probably it is interesting for you to assess this further.
Things to look into:

  • The debug flags that you added, I am not sure what there were needed for or how you got the behavior.

    If it does run in TwinCat but does not compute anything

  • What CPU is in your system? What would be the dedicated BLASFEO target, this could give a significant speedup compared to the GENERIC target.

Best,
Jonathan

Hi,

first of all thank you for your comment.

  • Cross Compilation:
    Unfortunatelly i don’t have much experince with this subject, but i did compile acados with the generic settings for blasfeo and hpipm as well as the EXT_DEP flag off (inspired by some other posts on this forum).
    I just got the information that libraries do not work, due to some memory allocation things, from the colleague which gave me the hint with the S-Function Builder. He did use this approach and got a casadi controller running in TwinCat.
  • Timing:
    To get the timing working on the Beckhoff PC, one would have to use the c++ functions provided by Beckhoff and do some time conversions i guess. But since these functions are not available in Simulink, i guess it would result in some other linker errors.
    Or what was your thought about this?
  • Execution Speed
    The use of the debug flag was not a precise description of the problem from me. The cause of the problem it the -Og optimization flag. When used, the resulting/compilated TwinCat Module does run, and is indeed alive (tested with a simple output=2*input code in the S-Function Builder) but it does not execute/call the acados solver. Some other colleague did hear about my problem and said he had a similar problem and that he had to use unique ponters instead of “regular” pointers. The debug capabilities are unfortunatelly limited in TwinCat and i could not track down what went wrong.
  • CPU
    I will take a look later and check what CPU the PC has and if there is a target in BLASFEO.
    Edit: It is a “Intel Atom® E3845, 1,91 GHz”.

Best regards,
Alex

Regarding the cross-compilation.
This should certainly be possible, but I can imagine that it might be easier to just add the needed sources from acados and BLASFEO in the Simulink builder.
However, compiling BLASFEO with a different target, probably does not work out of the box with your current approach, since it will then use different source files.

Of course one has to get the linking right, but it should be similar to how it is done for the dSpace systems, i.e. modify timing.c, timing.h similar to here: acados/acados/utils/timing.h at master · acados/acados · GitHub

About execution speed and CPU.
I don’t have experience with the -Og flag, I either use an optimization or a debug flag, but this somehow does a mixture of both.
The dedicated target for your CPU should be X64_INTEL_CORE as the Silvermont architecture does not support AVX or AVX2 instructions.

Best,
Jonathan