I was really curious about how fast can we calculate the control signal using MPC. Because of this, first I wanted to write a short C code, but finally I end up with C++. because it is easy to overload the operations and more flexible and easier to implement the Matrix operations. Also there are disadvantages just to mention that the std::vector is slower than the C array and C++ can increase the size of the compiled code significantly.
I have to mention that this is my first code in C++, but I already have experience with OOP(Matlab, Python, Java, PHP) and at the University I got the basics of the C.
For the first test I used the Eigen library which is really straight forward, but where is the challenge if I'm using a libraries instead of writing my own code and learning new OP language. With the Eigen I could achieve around 6e-5s which is really good, but this package is optimized for big matrices, take a look on the benchmark. In my case, the biggest matrix is a 4x4 matrix, which is really small.
In the second test I wrote my own library(you can fork from this on the github), I'm using arrays instead of std::vectors, because it's faster as I mentioned before.
The result of the test:
My next step was to test on my Raspberry Pi using ChibiOS/RT(ARM CPU). In this platform to calculate one control signal took around 2ms which is not really good. In the mean time I also install Arch Linux on the RPI where the runtime was 0.4ms which is a bit better, so I will try to compile the Linux kernel with real time capability.
Finally this is the test code
The result of the test:
Runtime |
My next step was to test on my Raspberry Pi using ChibiOS/RT(ARM CPU). In this platform to calculate one control signal took around 2ms which is not really good. In the mean time I also install Arch Linux on the RPI where the runtime was 0.4ms which is a bit better, so I will try to compile the Linux kernel with real time capability.
Finally this is the test code
int main(int argc, char* argv[]) { /* * Defining the variables */ //Control horizon int nc = 4; // Prediction horizon int np = 4; //Control signal Matrixu(1, 1); // Sampling time double Ts = 5e-5; MPC mySysMat(2, 1, 1); //Error matrix, just for testing; Matrix DeltaU(np, 1); Matrix R = 1e-5 * Matrix ::Identity(mySysMat.C.rows() * nc, mySysMat.C.rows() * nc); Matrix Q = 1e4 * Matrix ::Identity(np, np); Matrix x0(2, 1); //Reference signal Matrix y_ref(100000, 1); y_ref.rblock(0, 0, 50000, 1) = 100 * Matrix ::Ones(50000, 1); y_ref.rblock(50000, 0, 100000, 1) = -100 * Matrix ::Ones(50000, 1); double start = now(); //DC motor parameters double Rm = 0.35; double Km = 0.0296; double Ke = 0.0296; double b = 6.7 * 10e-5; double J = 2.9 * 10e-6; double Fc = 0.02; double L = 25 * 10e-6; /* Parsing the input parameters */ for (int i = 1; i < argc; i++) { if (i + 1 != argc) // Check that we haven't finished parsing already if (!strcmp(argv[i], "-s")) { Ts = atof(argv[i + 1]); } if (!strcmp(argv[i], "-nc")) { nc = atoi(argv[i + 1]); } if (!strcmp(argv[i], "-np")) { np = atoi(argv[i + 1]); } if (!strcmp(argv[i], "-h")) { #ifdef CMD std::cout << "Coming soon..." << std::endl; #endif } } //Initializing the system matrices mySysMat.Fi << 1 - Ts * (Rm / L), -Ts * (Ke / L), Ts * (Km / J), 1 - Ts * (b / J); mySysMat.Ga << Ts * 1 / L, 0; mySysMat.C << 0, 1; mySysMat.calcMPCFi(np); mySysMat.calcMPCGa(np); mySysMat.calcMPCGy(np, nc); #ifdef DEBUG std::cout << "Calculation Fi,Ga,Gy took : " << (double) (now() - start) << std::endl; #endif Matrix calcT(1, y_ref.rows()); Matrix u_hist(1, y_ref.rows()); Matrix w_hist(1, y_ref.rows()); for (int i = 0; i < y_ref.rows() - np; i++) { start = now(); //Calculating the error DeltaU = mySysMat.calcContSig( mySysMat.calcError(y_ref.block(i, 0, i + np, 1), x0, u), Q, R); u(0, 0) += DeltaU(0, 0); //We are not including this in the time measurement if (abs(u(0, 0)) > 5) u(0, 0) = sgn(u(0, 0)) * 5; u_hist(0, i) = u(0, 0); w_hist(0, i) = x0(1, 0); //Simulating the system x0 = mySysMat.Fi * x0 + mySysMat.Ga * u; //Storing the calculation time calcT(0, i) = (double) (now() - start); } #ifdef DEBUG std::cout << "Minimum:" << calcT.min() << std::endl; std::cout << "Maximum:" << calcT.max() << std::endl; #endif #ifdef PLOT Gnuplot g3("lines"); plot_x(y_ref, Ts, &g3); plot_x(w_hist, Ts, &g3); Gnuplot g2("lines"); plot_x(u_hist, Ts, &g2); Gnuplot g1("lines"); plot_x(calcT, Ts, &g1); std::cin.get(); #endif
Hi,
ReplyDeleteWhen I wanted to test this library, I could not find a make file or CMakeLists ... How do we compile this ... using gcc ?
All is written in header files, so you just have to include it. Have a look on this test:
ReplyDeletehttps://github.com/iUngi/PMPCToolbox/tree/master/test/dcmotor_mpc_hinf
there is a make file you just have to customize...