raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.76k stars 936 forks source link

Using Eigen with Pico-sdk #761

Closed nsk126 closed 2 years ago

nsk126 commented 2 years ago

I put Eigen into the same directory as my project and added the below line into CMakeLists.txt.

target_include_directories( 
    eigen/   
)

When I try to run a simple Eigen program below(found it on their website), I'm getting many errors.

#include <iostream>
#include <Eigen/Dense>

using Eigen::MatrixXd;

int main()
{
  MatrixXd m(2,2);
  m(0,0) = 3;
  m(1,0) = 2.5;
  m(0,1) = -1;
  m(1,1) = m(1,0) + m(0,1);
  std::cout << m << std::endl;
}

Output:

In file included from /home/user/dev/PicoEKF/eigen/Eigen/Core:160:0,
                 from /home/user/dev/PicoEKF/eigen/Eigen/Dense:1,
                 from /home/user/dev/PicoEKF/src/main.cpp:12:
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:235:25: error: 'invoke_result' in namespace 'std' does not name a template type
   typedef typename std::invoke_result<F, ArgTypes...>::type type1;
                         ^~~~~~~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:235:38: error: expected unqualified-id before '<' token
   typedef typename std::invoke_result<F, ArgTypes...>::type type1;
                                      ^
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:236:24: error: 'type1' was not declared in this scope
   typedef remove_all_t<type1> type;
                        ^~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:236:29: error: template argument 1 is invalid
   typedef remove_all_t<type1> type;
                             ^
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:241:25: error: 'invoke_result' in namespace 'std' does not name a template type
   typedef typename std::invoke_result<F, ArgTypes...>::type type1;
                         ^~~~~~~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:241:38: error: expected unqualified-id before '<' token
   typedef typename std::invoke_result<F, ArgTypes...>::type type1;
                                      ^
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:242:24: error: 'type1' was not declared in this scope
   typedef remove_all_t<type1> type;
                        ^~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Meta.h:242:29: error: template argument 1 is invalid
   typedef remove_all_t<type1> type;
                             ^
In file included from /home/user/dev/PicoEKF/eigen/Eigen/Core:164:0,
                 from /home/user/dev/PicoEKF/eigen/Eigen/Dense:1,
                 from /home/user/dev/PicoEKF/src/main.cpp:12:
/home/user/dev/PicoEKF/eigen/Eigen/src/Core/util/Memory.h:1176:12: error: 'std::destroy_at' has not been declared
 using std::destroy_at;
            ^~~~~~~~~~
In file included from /home/user/dev/PicoEKF/eigen/Eigen/SVD:37:0,
                 from /home/user/dev/PicoEKF/eigen/Eigen/Dense:5,
                 from /home/user/dev/PicoEKF/src/main.cpp:12:
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 192, 1, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:76:7: error: 'destroy_at' is not a member of 'Eigen::internal'
       internal::destroy_at(&m_qr);
       ^~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 192, 0, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:122:7: error: 'destroy_at' is not a member of 'Eigen::internal'
       internal::destroy_at(&m_qr);
       ^~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 0, 1, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:168:7: error: 'destroy_at' is not a member of 'Eigen::internal'
       internal::destroy_at(&m_qr);
       ^~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 0, 0, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:224:7: error: 'destroy_at' is not a member of 'Eigen::internal'
       internal::destroy_at(&m_qr);
       ^~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 128, 1, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:276:7: error: 'destroy_at' is not a member of 'Eigen::internal'
       internal::destroy_at(&m_qr);
       ^~~~~~~~
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h: In member function 'void Eigen::internal::qr_preconditioner_impl<MatrixType, Options, 128, 0, true>::allocate(const SVDType&)':
/home/user/dev/PicoEKF/eigen/Eigen/src/SVD/JacobiSVD.h:331:7: error: 'destroy_at' is not a member of 'Eigen::internal'
CMakeFiles/PicoEKF.dir/build.make:75: recipe for target 'CMakeFiles/PicoEKF.dir/src/main.cpp.obj' failed
       internal::destroy_at(&m_qr);
       ^~~~~~~~

Edit: Added the Eigen program I used for better understanding.

lurch commented 2 years ago

When I try to run a simple Eigen program below(found it on their website), I'm getting many errors.

What makes you think that this is a problem with pico-sdk, rather than a problem with eigen (whatever that is?)

willeccles commented 2 years ago

I would be shocked if this was an Eigen issue--Eigen is an industry standard library for linear algebra. I would be more likely to think your build environment is somehow broken. What C++ standard are you using? Do you have libstdc++-arm-none-eabi-newlib installed (assuming you are on Debian/Ubuntu)? Could you share your CMakeLists.txt?

My initial thought is that Eigen is made to work with C++98, but it uses newer features when possible. In particular, std::invoke_result is a C++17 feature. If you try building with C++11, does it work?

willeccles commented 2 years ago

Interestingly, Eigen's Meta.h does not contain any reference to std::invoke_result on my machine (Eigen 3.3.9). What version of Eigen are you using? Looks like this feature was added after 3.3.9. The C++ STL being used in the pico-sdk likely does not fully support C++17.

nsk126 commented 2 years ago

Eigen 3.4.0 I think. from https://gitlab.com/libeigen/eigen

I cloned the repo into my project folder.

willeccles commented 2 years ago

Try putting set(CMAKE_CXX_STANDARD 11) in your CMakeLists.txt.

geurtv commented 2 years ago

I think I found it and I'd say it's actually Eigen that is broken (I only tried latest master branch). If you use (ancient) arm gcc 6.3.1 with '-std=gnu++1z' Eigen incorrectly assumes the compiler and/or libstdc++ is fully c++17 capable, while gcc 6.3.1 is definitely not.

cmake will set -std=gnu++1z with set(CMAKE_CXX_STANDARD 17), so instead do set(CMAKE_CXX_STANDARD 14) and the example will compile (Eigen now says c++11 is too old) ... or use a newer compiler, up to gcc 11.2 is available for download.

willeccles commented 2 years ago

I doubt this is an Eigen issue. The compiler will set __cplusplus to 201703L if C++17 is enabled. However, the compiler or its STL may not have full support for C++17. Eigen has no way of knowing this, since the compiler has told it "I support C++17," and then you run into issues like this. For C++17 compiler support, see here: https://en.cppreference.com/w/cpp/compiler_support/17

As an example, if you tell GCC to use experimental C++23 features, the __cplusplus macro will be set to a suitable value for C++23, despite the compiler not fully supporting the standard. Otherwise the STL's code wouldn't know which of the features it does support to enable.

(Eigen now says c++11 is too old)

According to the release notes for 3.4.0, 3.4.0 is the last one that still supports C++03, so I'd imagine 11 would work just fine. 11 is generally considered the minimum for "modern" C++ and is probably still the most widely used by a large margin.

geurtv commented 2 years ago

Not quite, gcc 6 never tells Eigen it's c++17. With -std=gnu++1z gcc 6 sets __cplusplus to 201500L (gcc 7 and later use 201703L). Next Eigen sets EIGEN_COMP_CXXVER to 17 when __cplusplus > 201402L. So whether it's standard or not I don't know, but Eigen can know it's not c++17 simply because 201500 < 201703.

Concerning c++11, I simply cloned the gitlab repo as I assumed nsk126 did too. I think it has its version set to 3.4.90, so it's not the 3.4.0 release. When you then set(CMAKE_CXX_STANDARD 11) you'll get: #error This compiler appears to be too old to be supported by Eigen. c++14 is the minimum for this one.

nsk126 commented 2 years ago

I've had no success fixing this. Building from the source in my project didn't help.

So I tried installing eigen3 directly into root with sudo apt install libeigen3-dev and it still pops out errors like

/usr/include/eigen3/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h:396:1: error: template with C linkage
 template<typename MatrixType>

And a lot more like these.

My CMakeLists.txt :


# Set minimum required version of CMake
cmake_minimum_required(VERSION 3.12)

# Include build functions from Pico SDK
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

# Set name of project (as PROJECT_NAME) and C/C++ standards
project(PicoEKF C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)

# Creates a pico-sdk subdirectory in our project for the libraries
pico_sdk_init()

# Tell CMake where to find the executable source file
add_executable(${PROJECT_NAME} 
    src/eigen_test.cpp
)

FILE(GLOB FreeRTOS_src FreeRTOS-Kernel/*.c)

add_library( FreeRTOS STATIC
    ${FreeRTOS_src}
    FreeRTOS-Kernel/portable/GCC/ARM_CM0/port.c
    FreeRTOS-Kernel/portable/MemMang/heap_4.c
)

# Create map/bin/hex/uf2 files
pico_add_extra_outputs(${PROJECT_NAME})

find_package (Eigen3 3.3 REQUIRED NO_MODULE)

#add include directiories
target_include_directories( FreeRTOS PUBLIC
    FreeRTOS-Kernel/include
    include/
    FreeRTOS-Kernel/portable/GCC/ARM_CM0
)

# Link to pico_stdlib (gpio, time, etc. functions)
target_link_libraries(${PROJECT_NAME} 
    pico_stdlib
    hardware_i2c
    pico_multicore
    hardware_dma
    FreeRTOS
    Eigen3::Eigen
)

# Enable usb output, disable uart output
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)

Edit: I also put set(CMAKE_CXX_STANDARD 11) into my CMakeLists.txt. But found no success.

kilograham commented 2 years ago

Not following closely, but did you try with a newer compiler? GCC 6 is very old.

Side note: https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/main/portable/ThirdParty/GCC/RP2040 is the port of FreeRTOS you should be using for RP2040.

willeccles commented 2 years ago

I had no problem doing basically what your original approach was. I dropped Eigen 3.4.0's source into the eigen-3.4.0 directory in the project, then did target_include_directories(..... PRIVATE eigen-3.4.0/) and all went swimmingly. I am using C++11, which should be supported even on GCC 6.

willeccles commented 2 years ago

Not following closely, but did you try with a newer compiler? GCC 6 is very old.

@kilograham I don't think OP has ever mentioned using GCC 6. I believe that was another person. GCC 6 is certainly not going to help anyone out here, but they should still be able to build this project just fine.

nsk126 commented 2 years ago

Thanks for the suggestion. @willeccles

I had no problem doing basically what your original approach was. I dropped Eigen 3.4.0's source into the eigen-3.4.0 directory in the project, then did target_include_directories(..... PRIVATE eigen-3.4.0/) and all went swimmingly. I am using C++11, which should be supported even on GCC 6.

target_include_directories(${PROJECT_NAME} PRIVATE
    eigen-3.4.0/
)

I suppose the core of the problem was CXX 17. After I re-downloaded eigen-3.4.0 from tuxfamilys' website and changed my CMakeLists.txt file to set(CMAKE_CXX_STANDARD 11) I don't see any errors during make.

I have to test this with my Pico. Will do in a bit.

Edit: Does this mean Eigen was never meant to be used with C++ 17? What happen?

willeccles commented 2 years ago

Does this mean Eigen was never meant to be used with C++ 17? What happen?

This actually means your C++ STL or compiler is too old to fully support C++17. Eigen will use C++17 features only if the compiler reports that they are available. If the compiler partially supported C++17, it may have reported that it was available, but Eigen ran into issues when some of it was not supported by the compiler. With a sufficiently modern compiler + STL, C++17 will work just fine with Eigen--in your case, however, C++17 support appears to be incomplete in your compiler/STL.

Eigen 3.4.0 does use C++17 features, but since you're compiling with C++11 it will harmlessly avoid compiling that code.

willeccles commented 2 years ago

(I think we can close this issue, as it's been proven that this is not only doable, but unrelated to both Eigen and the Pico SDK)

nsk126 commented 2 years ago

Does this mean Eigen was never meant to be used with C++ 17? What happen?

This actually means your C++ STL or compiler is too old to fully support C++17. Eigen will use C++17 features only if the compiler reports that they are available. If the compiler partially supported C++17, it may have reported that it was available, but Eigen ran into issues when some of it was not supported by the compiler. With a sufficiently modern compiler + STL, C++17 will work just fine with Eigen--in your case, however, C++17 support appears to be incomplete in your compiler/STL.

Eigen 3.4.0 does use C++17 features, but since you're compiling with C++11 it will harmlessly avoid compiling that code.

Could this be because of my OS or my g++ running on version g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 ?

willeccles commented 2 years ago

What is the output of arm-none-eabi-g++ --version? This is the compiler used for the Pico.

nsk126 commented 2 years ago

arm-none-eabi-g++ (15:6.3.1+svn253039-1build1) 6.3.1 20170620

willeccles commented 2 years ago

Wow, you really are using GCC 6. That explains that for sure. GCC 6 won't properly support C++17, and Eigen is slightly busted on GCC 6 as someone pointed out above. However, it does support C++11, so using that ought to make everything go smoothly.

nsk126 commented 2 years ago

Thanks for the help! I'll close this issue.