Add new library to the waf build system

So, I see there are a number of other people who’ve asked this question. Would it be possible for someone experienced to outline the procedure?

I’d be quite happy to write it up formally, etc.

just add the directory, then add to this list if the library will be used by all vehicles:


if instead the library is for just one vehicle type, then add to that vehicle, like this:

then do a new waf configure, clean and build

2 Likes

Many thanks Tridge. Much appreciated.

@tridge Excuse me , does it also apply to static library or dynamic library?

@tridge

Hi Andrew.

Thank you for the reply. I can confirm that the modification you suggested does work for typical cases.

However, I wanted to add the “Eigen” library, just because all my control algorithms are written in it, and I don’t want to have to re-write them using ardupilot matrix-matrix/matrix-vector/vector-vector multiplication functions. Moreover, as I am using the “Beaglebone Blue” board, I would typically be able able to use the “Neon” fpu by adding -mfpu=neon flag to enable explicit vectorization (see https://eigen.tuxfamily.org/index.php?title=FAQ#Vectorization). I have indeed done some benchmarking with the NEON and the computation gain is quite significant, especially when using floats which is what the NEON is prepared to process parallely in that board, so I would appreciate if you could tell me how to enable that as well.

I tried to add this library in the same way that you mentioned but I got the following error.

The “libraries/Personal_Code/examples/test_code/test_code101.cpp” literally does nothing but to add “#include<Eigen/Dense>” as you normally would. (see below)

If I remove that single line it compiles, but I am unable to bring in any of the functions from Eigen.

I’d be greatful if you could advise as to how to make this library work with ArduPilot’s entire source code.

Thanks in advance.

Look forward to hearing back from you.

Regards,
Oscar

I’m also interested in example of linking external library. Also maybe there are some general examples for CMake users for command equivalence? I also found this script, but I don’t know if it helps to convert CMake files or do something else

Hi Oscar,

Coincidentally I’ve been working my way down the same path to get Eigen-3.3.9 running inside the Ardupilot/Chibios build environment. We have a 15-state EKF algorithm developed and used within our lab (umn/aem/uav lab) for the last 15 or so years and I think I have just managed to gett that to build/run on my little Pixracer board as of today. Our code will run at 100hz (woohoo!) but not 125hz … so it’s just on the edge.

In my project I wrangle some 4x4 homogeneous matrix/vector operations for orientation and mag calibration, and then for the ekf we use some 15x15, 9x15, 12x15 matrix operations (transpose, multiply, etc.) Eigen really works out nice for all of this and avoids me having to custom craft/debug picky matrix code and memory allocations.

Eigen is a big chunk of code (as I’ve seen mentioned here before), but it is just an include tree, so you only have to pay for the parts you use.

From my notes (sorry this is a bit complicated, but I’m trying to be as thorough as possible):

  1. You need to #define ALLOW_DOUBLE_MATH_FUNCTIONS before including AP_HAL to avoid the conflict/error message with HAL that leads to your pow() error message. I added defines=[‘ALLOW_DOUBLE_MATH_FUNCTIONS’] into my wscript (using Blimp/wscript as an example of how to do this.)

  2. In ardupilot/Tools/ardupilotwaf/boards.py (chibios section at least), I had to comment out:
    -Werror=float-equal
    There are some uses of float == in eigen3 that appear to be legit (but then be cautious in your own code because you have removed a guardrail.)

  3. In my own host cross compile build system (fedora 34) I had to make one small change: In /usr/arm-none-eabi/include/c++/10.2.0/bits/basic_string.h remove std:: from in front of vsnprint() calls. You may also need to #include <stdio.h> in your own code. It seemed like a discrepancy between the compiler and the includes … like the compiler had moved vsnprint() to a system level, but the #include’s hadn’t caught up yet?

  4. Eigen3 has intricate code for making sure all it’s structures and values are aligned … and doing this on a variety of platforms and compilers. Part of this includes a call to std::realloc() which Chibios doesn’t implement and the AP build environment is rigged to flag this as a build error (rightly so.)
    Tridge said the AP_HAL has it’s own implementation of std::realloc() but I didn’t use that because I’m not comfortable that I fully understand the intricacies as they relate to Eigen’s structure alignment requirements (I think on 16 byte boundaries?)
    Instead I found someone else who had blazed a path before me and implemented a ‘generic’ realloc() based on eigen’s already existing “handmade” aligned alloc() and free(). Then I needed to carefully remove all the references to std::realloc() in Eigen. All of this is limited to a single file … Memory.h

You can grab my modified eigen-3.3.9 tree here: https://github.com/RiceCreekUAS/rc-fmu-ap/tree/main/eigen-3.3.9

Or you can grab the one specific file I modified directly here (NOTE: for v3.3.9): https://github.com/RiceCreekUAS/rc-fmu-ap/blob/main/eigen-3.3.9/Eigen/src/Core/util/Memory.h

Not quite done! Inside my own source code, my includes ended up like this:

    #pragma push_macro("_GLIBCXX_USE_C99_STDIO")
    #undef _GLIBCXX_USE_C99_STDIO
    #include "eigen3/Eigen/Core"
    #pragma pop_macro("_GLIBCXX_USE_C99_STDIO")
  1. For convenience, I created a symbolic link from eigen-3.3.9 -> eigen3 inside my source tree, and the eigen tree is directly in my app subdirectory right now, not in a system or library location. (So include “…” instead of <…>)

  2. Eigen’s name space conflicts with AP_Math (things like Vector2f, Vector3f, etc.) so I had to remove my using namespace Eigen; and use Eigen:: everywhere to avoid that issue.

  3. There is some vsnprint() nonsense going on that I never completely figured out, but #undefining _GLIBCXX_USE_C99_STDIO seemed to cure it. I didn’t want to chance screwing something up somewhere else so I used #pragma push/pop to keep the change only for the eigen includes. It makes things a little ugly, but seems to work. I need to circle back and see if this is truly necessary.

  4. One other note: I had a crash in one of my functions that I suspect was me overflowing the stack frame (but I don’t know how to confirm/test that.) P = PHI * P * PHI.transpose() + Q; These are all 15x15 matrices. The code is just a string of matrix ops that I know is well tested on other platforms. I ended up statically allocating temporary matrices and unwinding the operations into separate steps and now it sails through.

    t1 = PHI * P;
    t2 = PHI.transpose();
    t3 = t1 * t2;
    P = t3 + Q;

I’ve done a fast/deep/sprint dive on this over the past day or two … so it’s quite possible that I’ve missed something important in my notes, or after circling around and trying 100 variations, something here isn’t directly needed or could be handled more cleanly by someone with a better understanding of all the things.

Sorry this got so long, hope this helps. If you give this a shot and still run into errors, feel free to ask about those specifically. I may or may not have seen every related error possible in 2 days, and may or may not remember something about what I did to work around them.

Best regards,

Curt.

1 Like

Hi Curt

I think you just saved my life. :stuck_out_tongue: Thank you very much! I will definitely try it.

To be completely honest, I would have never done any of that. I would have just used an external platform for computation and share whatever relevant variables using SPI communication, or even find a way to run a separate program within my own BeagleboneBlue and share the results somehow between the codes.

It would be nice if the developers actually gave a clean solution for this. I just fail to see why such basic stuff like including these kind of libraries would be such a pain. Like for example, I am planning to use state of the art QP solver libraries like QPOases, QPFORCES or QPDunes. Who knows what troubles am I going to get with those too. At this point, I am tempted to just write my own code and do it all by myself. After all, I don’t need the 700,000 lines of code for handling all the different scenarios that are captured by Ardupilot.

But anyways, thanks a lot!

Regards,
Oscar