ESP32の開発環境を整える

開発規模が大きくなったので,Arduino for VSCodeからEPS-IDFに移行
逆運動学計算に時間がかかりすぎて,ゲイン下げないと振動するから複スレッドにしたい

やったこと

  • ESP32をEPS-IDFで開発
  • CMakeでのビルドに挑戦
  • ESP32で本家Eigen(全く情報がなくて困った,,,)
  • GoogleTestを用いたアルゴリズム単体テスト

想定読者

C++,CMake,単体テスト(gtest),git submoduleに関心がある人かな
非線形カルマンフィルタとか追加しているけど,単体テスト参照
GitHub - Libra23/robot-ws
g++でビルドすると単体テストが可能で,同じソースでesp-idfでビルドするとesp32用バイナリができる

ESP32をEPS-IDFで開発 & CMakeでのビルド

本家通りに導入,,,Macで開発環境を構築
Get Started - ESP32 - — ESP-IDF Programming Guide latest documentation
cloneしてinstall.shしておしまい.下記のコマンドを実行するとPATHにESP-IDF関連が追加される.

source ~/esp/esp-idf/export.sh

ターミナル開いて,printenvでPATHが追加されたことを確認

~/esp/esp-idf/examples/ の下にサンプルがあるので活用(やっぱりhello_world!!)
idf.py buildを使わずにCMakeでビルドしてみる

cd ~/esp/esp-idf/examples/get-started/hello_world
cmake -B build -G Ninja # buildフォルダに出力makeよりNinjaの方がビルドが早いらしい
ninja -C build
# ninja flash # buildフォルダに移動してシリアルポートを指定してフラッシュできる
ソースフォルダのリネーム

esp-idfでは"main"フォルダ下のソースは特別扱いで,コンパイルに必要な準備をしてくれるので基本的に"main"が良さそう
ただ,ソースフォルダはsrc/,テストフォルダはtest/ にしたいので"src"にリネーム
プロジェクトトップディレクトリのCMakeLists.txtを編集

cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "./src") # srcフォルダを見てもらうようにEXTRA_COMPONENT_DIRSに追加
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(hello-world)

srcディレクトリのCMakeLists.txtを編集

idf_component_register(SRCS "hello_world_main.cpp"
                       INCLUDE_DIRS "."
                       REQUIRES spi_flash) # srcフォルダだと必要な物を自分で準備する必要がある

ESP32で本家Eigen

ここからはrobot-wsレポジトリの話
Eigenをeigenとして,googletestをgtestとして追加
robot-ws/src/algorithm/math_utility.hppがEigenをIncludeしている

深いレイヤーのCMakeListから紹介
robot-ws/src/algorithm/CMakeLists.txt

# src/algorithm
message("!!! src/algorithm !!!")
# make library
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRCS) # directory内のソースをまとめる
add_library(algorithm STATIC ${SRCS}) # ライブラリを作る
target_include_directories(algorithm PUBLIC ${CMAKE_SOURCE_DIR}/src/eigen) # ライブラリに必要なファイルをincludeする

robot-ws/src/CMakeLists.txt

message("!!! src !!!")
# create object
idf_component_register(SRCS "app_main.cpp" INCLUDE_DIRS ".") # espのオブジェクト作成
# subdirectory
add_subdirectory(algorithm) # src/algorithmのCMakeListを追加
# register library
target_link_libraries(${COMPONENT_LIB} algorithm) # src/algorithm/CMakeList.txtで作成したライブラリーをリンク

topレイヤー(robot-ws/)のCMakeListはhello_worldのtopレイヤーと同じ("main"ではなく"src"を使っているので)
これでEigenが使える

GoogleTestを用いたアルゴリズム単体テスト

これはg++で
robot-ws/testにgoogletestを追加
googletestはEigenと異なりインクルードライブラリではないので
robot-ws/test/CMakeLists.txt

# test
# enable ctest
enable_testing() #ctestの有効化
# subdirectory
add_subdirectory(algorithm)
add_subdirectory(gtest) # googletestもビルド

robot-ws/test/algorithm/CMakeLists.txt

# test/algorithm
# create excute object
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRCS)
add_executable(algorithm_test ${SRCS})
# link library
target_link_libraries(algorithm_test algorithm gtest_main)
target_include_directories(algorithm_test PUBLIC ${PROJECT_SOURCE_DIR}/src/algorithm)
# make test
add_test(algorithm_test algorithm_test)

適当に逆運動学の単体テスト

/**
 * @test check inverse kinematic
 */
TEST_F(KinematicTest, CheckInverseKinematic) {
    // prepare q
    Joint q_expect;
    q_expect << 45.0, 45.0, 45.0;
    q_expect *= DEG_TO_RAD;
    // set tip_trans
    Affine3d tip_trans_expect;
    kinematic_.Forward(q_expect, Affine3d::Identity(), tip_trans_expect);
    std::cout << q_expect.transpose() * RAD_TO_DEG << std::endl;
    std::cout << tip_trans_expect.translation().transpose() << std::endl;
    // check IK
    Joint q_standard;
    q_standard << 45.0, 30.0, 60.0;
    q_standard *= DEG_TO_RAD;
    Joint q_ik;
    bool ik_ret = kinematic_.Inverse(tip_trans_expect, Affine3d::Identity(), q_standard, q_ik);
    std::cout << q_ik.transpose() * RAD_TO_DEG << std::endl;
    for (int i = 0; i < NUM_JOINT; i++) {
        EXPECT_NEAR(q_expect(i), q_ik(i), TOLERANCE);
    }
}

乱数使って回すのと,Vectorの判定関数作んないと,,,

build用コマンド

robot-ws/scriptの中にbuild用にシェルコマンド

./script/build.sh # g++用
./script/build_esp32.sh # esp-idf用(一度実行すると実行したターミナルではパスが有効になるので上記コマンドは同じターミナルでは使えなくなる)