粗大メモ置き場

個人用,たまーに来訪者を意識する雑記メモ

Rplidarをjetson TX1で使う(要Kernel Build)

メモ記事なのでざっくりと.である口調で.

Rplidar

SLAMTECH社の出しているLIDAR.
周囲の距離情報を10hzで400点(0.9°ずつ)出力してくれるすぐれものである.

安価?な割に性能がよくROSでWrapperもきちんと作られているため非常に使い勝手が良い.

f:id:ossyaritoori:20171014102112p:plain:w300

難点は重心が偏っているため回転時に振動を起こしてしまうというもの.
それくらいは頑張って欲しかった.

Rplidar with ROS

ROSのWrapperもあるので簡単にRVizなどで出力可能.
すごいぞ〜かっこいいぞ〜〜!!

f:id:ossyaritoori:20171014101824p:plain

jetsonで運営するにあたっての問題

jetsonで使いたいのだが,これには周知の問題があり,USB通信のCP201xに対応するドライバがjetsonのtegaraには入っていないのである.(よくわかってない)
従ってドライバを認識するためにカーネルを再ビルドする必要が有る.

ここの動画が詳しいので割と障壁は低そうに見えた...が.
www.jetsonhacks.com

空き容量が「3GB」必要なのである.
TX1の使用可能容量は14Gくらいで,Ubuntuまわりで6G,Cudaまわりで3G,ROS周りで2G,Opencvまわりで1G...ん?
はい.容量が足りません.

NVIDIAの強マンとやりとりしたところ「クロスコンパイル」を勧められたが,,,
https://devtalk.nvidia.com/default/topic/1000764/jetson-tx1/rplidar-and-jetson-tx1/?offset=12#5214215

ミスったらどうするんだろう...的な不安で一歩踏み出せていないです.
はぁ困った.

なお,こちらの記述が大いに参考になりそうなのでいつか試す.いつか.
Cross Compile Custom Linux Kernel for the Tegra TX1 · GitHub

販促

できることなら誰か買って重心の偏りを補正する方法を考えて欲しいものです.

ROSにユーザ指定のOpenCVのディレクトリを参照させる.(未解決)

前回の記事でOpencvをcudaのサポート込でコンパイルしたわけですが今度はこれをROSで使えるように教えこまないといけません.

ここで問題になるのがOpencv with cudaとRosのOpencv3パッケージの競合です.
自分でコンパイルした方を使いたいのですが,ROSの方にはcv_bridgeを始めとする超重要パッケージもあるので片方を消すわけにも行かない...

以下の記事にもあるように割と根深い問題です.
qiita.com

cmakelistのfindpackageがどこを見ているのかチェック

ガチの初心者&雰囲気で理解しているので間違っていたらコメントください.
CMakeListでははじめに
findpackage()を呼んでインクルードしたいパッケージのPathを探すようです.
その後,そのPathを用いて目的のソースとライブラリを関連付けます.

${FUGA}の中身を確認するには?

次のような文をprintfよろしく書き連ねることでcmakeをするときにWarning文として出力できます.
stderrにprintfしているような感じなので多分正攻法ではない気がしますがとにかく動きます.

MESSAGE(WARNING "prefix ${CMAKE_PREFIX_PATH}")    
MESSAGE(WARNING "version ${OpenCV_VERSION}")
MESSAGE(WARNING "install path ${OpenCV_INSTALL_PATH}") 
MESSAGE(WARNING "config path ${OpenCV_CONFIG_PATH}") # look at the output of this message
MESSAGE(WARNING "include dirs ${OpenCV_INCLUDE_DIRS}")
MESSAGE(WARNING "libs ${OpenCV_LIBS}")

OpenCVのPathを指定する

私はcmakeとかLinuxとかガチガチの初心者でしたのでcmakeの中身についてメモをば.
以下のような文を足すことで確かにCmakeにOpencvのデフォルトパッケージの位置を教えることができます.

find_package(OpenCV 3 REQUIRED
NO_MODULE 
PATHS /usr/local 
NO_DEFAULT_PATH)

cmakeとcatkin_makeで結果が違うんだけど!?

そうして試してみると,ある時cmakeでコンパイルした時とcatkin_makeでコンパイルした時で結果が違うことにきづきました.

どうもcatkin_makeでは前回のBuildに依存しているようで,一度Buildとdevelフォルダを消し飛ばす必要があります.
How to tell Catkin_make to use opencv with gpu? - ROS Answers: Open Source Q&A Forum

develをまるごと消すとdarknet_rosとかに不都合が生じるのできちんとバックアップをとっておくように!

そうして再コンパイルしてこの事件は解決...かにおもわれました.

結局解決していないっぽい理由

結局ソースコードと正しいOpencvとを関連付けることができませんでした.

原因は最後のLinkに有ると思っています.

target_link_libraries(my_function ${OpenCV_LIBS} ${catkin_LIBRARIES})

このCatkin_librariesがある$catkin_develを見たところ,以下のpathが紛れておりバッチリROSのOpencvを参照していたからです.

/opt/ros/kinetic/include/opencv-3.2.0-dev

ということは,現状次の解決策はROSのOpencvとBuildしたOpencvのうち,被っている箇所のどちらかを吹き飛ばすか,先にBuildした方のOpencvのPathを先に解決させることが考えられます.(小学生並みの希望的観測)

が,どっちもやり方がよくわかんないので誰かおしえてください〜〜〜ってなっているのが現在の状況.

NG集

catkin_make -DOpenCV_INCLUDE_DIRS=/opt/opencv/include
みたいな感じでBuild時に教えることができますが,雑に試した結果はうまく行っていません.

保存用

ros-kinetic-oepncv3のライブラリ

opencv_calib3d;opencv_core;opencv_features2d;opencv_flann;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_shape;opencv_stitching;opencv_superres;opencv_video;opencv_videoio;opencv_videostab;opencv_viz;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_cvv;opencv_datasets;opencv_dpm;opencv_face;opencv_fuzzy;opencv_hdf;opencv_line_descriptor;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_reg;opencv_rgbd;opencv_saliency;opencv_stereo;opencv_structured_light;opencv_surface_matching;opencv_text;opencv_xfeatures2d;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto

ビルドしたライブラリ

opencv_xphoto;opencv_xobjdetect;opencv_ximgproc;opencv_xfeatures2d;opencv_tracking;opencv_text;opencv_surface_matching;opencv_structured_light;opencv_stereo;opencv_sfm;opencv_saliency;opencv_rgbd;opencv_reg;opencv_plot;opencv_optflow;opencv_line_descriptor;opencv_hdf;opencv_fuzzy;opencv_face;opencv_dpm;opencv_dnn;opencv_datasets;opencv_cvv;opencv_ccalib;opencv_bioinspired;opencv_bgsegm;opencv_aruco;opencv_videostab;opencv_videoio;opencv_video;opencv_superres;opencv_stitching;opencv_shape;opencv_photo;opencv_objdetect;opencv_ml;opencv_imgproc;opencv_imgcodecs;opencv_highgui;opencv_flann;opencv_features2d;opencv_cudev;opencv_cudawarping;opencv_cudastereo;opencv_cudaoptflow;opencv_cudaobjdetect;opencv_cudalegacy;opencv_cudaimgproc;opencv_cudafilters;opencv_cudafeatures2d;opencv_cudacodec;opencv_cudabgsegm;opencv_cudaarithm;opencv_core;opencv_calib3d

Cudaの部分だけROSに移植したらなんとかならないかなぁ...

Build OpenCV for GPU version in Jetson TX1

容量に関する問題

OpenCVソースコードからフルでコンパイルするには容量が4GB必要になります。
16GBの容量のTX1にそれは非常に厳しい...

外部メモリ上でのビルド

似たような状況がRaspberryPiにもあるようで、そちらの記事を参照しました。
PkLab - Install OpenCV 3.2 Python/C++ on Raspberry PI

USBをLinuxファイルシステムとして認識させてその上でコンパイルします。

USBの中身のフォーマット

とにかくUSBの中身をLinuxで見れるext2等のフォーマットへと変える必要があります。

sudo fdisk -l

で中身を確認しながらパーティションをクリアできるとありましたが、
私の場合うまく行かなかったので以下のサイトに準拠してフォーマットしました。
How to format a USB flash drive? - Ask Ubuntu


マウント

私の場合/dev/sda1が対象のUSBのデバイス名なので

sudo mkfs -t ext2 /dev/sda1

これでLinuxファイルシステムとしてフォーマットして
次のコマンドでUSBメモリの中身をLinuxファイルシステムから確認する事ができるようになります。

mkdir ~/usbmem
sudo mount /dev/sda1 ~/usbmem

Opencvのダウンロードとコンパイル

大部分はOpenCV: Building OpenCV for Tegra with CUDAを参考にしています。

ソースのダウンロードと所々の修正は[参考文献1]を。
その後contrib moduleをダウンロードするところは[参考文献2]を参照しています。
SIFTとかも使いたいですからね。

git clone https://github.com/opencv/opencv.git
git cherry-pick 10896
git cherry-pick cdb9c
git cherry-pick 24dbb
cd opencv-3.1.0
git clone --depth 1 https://github.com/Itseez/opencv_contrib.git opencv_contrib
cd opencv_contrib
git fetch origin --tags --depth 1
git checkout 3.1.0

Dependencyの解決

依存するパッケージをインストールします。

sudo apt-add-repository universe
sudo apt-get update
sudo apt-get install \
    libglew-dev \
    libtiff5-dev \
    zlib1g-dev \
    libjpeg-dev \
    libpng12-dev \
    libjasper-dev \
    libavcodec-dev \
    libavformat-dev \
    libavutil-dev \
    libpostproc-dev \
    libswscale-dev \
    libeigen3-dev \
    libtbb-dev \
    libgtk2.0-dev \
    pkg-config

準備していない人はPython環境もちゃんと準備しましょう。(下は2.7用)

sudo apt-get install python-dev python-numpy python-py python-pytest

Cmake と make

追記:完全に参考文献1に準拠し直しました。

cmake .. -DWITH_OPENGL:BOOL=ON -DWITH_QT:BOOL=ON -DWITH_CUDA:BOOL=ON -DCUDA_ARCH_BIN="5.2" -DCUDA_ARCH_PTX="5.2" -DCMAKE_BUILD_TYPE=RelWithDebugInfo -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_TESTS:BOOL=OFF -DBUILD_PERF_TESTS:BOOL=OFF -DWITH_FFMPEG:BOOL=OFF -DENABLE_NEON:BOOL=ON -DBUILD_EXAMPLES:BOOL=ON -DINSTALL_C_EXAMPLES:BOOL=OFF -DINSTALL_PYTHON_EXAMPLES:BOOL=ON -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules
以下古いバージョン(不具合あり)

多分どれかがおかしい...
Cmakeですが、参考文献1と2をあわせた手法を取っています。>||
mkdir build && cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_PNG=OFF \
-DBUILD_TIFF=OFF \
-DBUILD_TBB=OFF \
-DBUILD_JPEG=OFF \
-DBUILD_JASPER=OFF \
-DBUILD_ZLIB=OFF \
-DBUILD_EXAMPLES=OFF \
-DBUILD_opencv_java=OFF \
-DBUILD_opencv_python2=ON \
-DBUILD_opencv_python3=OFF \
-DENABLE_PRECOMPILED_HEADERS=OFF \
-DWITH_OPENCL=OFF \
-DWITH_OPENMP=OFF \
-DWITH_FFMPEG=ON \
-DWITH_GSTREAMER=OFF \
-DWITH_GSTREAMER_0_10=OFF \
-DWITH_CUDA=ON \
-DWITH_GTK=ON \
-DWITH_VTK=OFF \
-DWITH_TBB=ON \
-DWITH_1394=OFF \
-DWITH_OPENEXR=OFF \
-DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-8.0 \
-DCUDA_ARCH_BIN=5.3 \
-DCUDA_ARCH_PTX="" \
-DINSTALL_C_EXAMPLES=OFF\
-DINSTALL_TESTS=OFF \
-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules
|

  • DCMAKE_INSTALL_PREFIX を /sur/localにするか迷う...(よくわかっていない)


次にmakeします。

make -j4 VERBOSE=1

このVERBOSE=1を入れることでエラーが出た時の詳細が出て進めやすくなります。
(エラー出る前提)
よく出たエラーについては下を参照。

make install

最後のこのコマンドでmakeした結果の実行ファイルなどをローカルのPATHの通ったLibなどにコピーします。
(間違っていたら教えてください)

make install

時々sudoでやっているサイトを見るのだけれどどっちが正しいのだろう?うまく行かなかったらsudoで。

sudo ldconfigを行う場合もあるそうな。

エラー集

当然出まくるエラー。

hdf5.h : No such file or directory

もしhdf5.hがあるのにこのエラーが出るなら
Pythonがhdf5を探せていないのが原因。

modules/python/common.cmake の最後の行に以下の2文を追加。

find_package(HDF5)
include_directories(${HDF5_INCLUDE_DIRS})

can't find hdf5.h when build caffe · Issue #156 · NVIDIA/DIGITS · GitHub
build error for python bindings with opencv_contrib modules · Issue #6016 · opencv/opencv · GitHub

onlineMIL.hpp でのエラー

In file included from /home/ubuntu/usbmem/opencv/opencv_contrib/modules/tracking/include/opencv2/tracking/tracker.hpp:48:0,
                 from /home/ubuntu/usbmem/opencv/build/modules/python2/pyopencv_generated_include.h:49,
                 from /home/ubuntu/usbmem/opencv/modules/python/src2/cv2.cpp:12:
/home/ubuntu/usbmem/opencv/opencv_contrib/modules/tracking/include/opencv2/tracking/onlineMIL.hpp:57:23: error: expected unqualified-id before ‘>’ token
 #define  sign(s)  ((s > 0 ) ? 1 : ((s<0) ? -1 : 0))

この場合、同様の定義が他のcppファイルで使われているのが原因。
error building ```opencv_contrib/modules/tracking/include/opencv2/tracking/tracker.hpp``` · Issue #618 · opencv/opencv_contrib · GitHub

onlineMIL.hppにある次の文をカットして
src/omlineMIL.cppへとペーストする。

#define  sign(s)  ((s > 0 ) ? 1 : ((s<0) ? -1 : 0))

cuda_gl_interop.hに関するエラー

/usr/local/cuda-8.0/includes にある同盟ファイルの GL/gl.hのインクルード周りを次のように変更。

//#if defined(__arm__) || defined(__aarch64__)
//#ifndef GL_VERSION
//#error Please include the appropriate gl headers before including cuda_gl_interop.h
//#endif
//#else
#include <GL/gl.h>
//#endif

Webカメラから画像が取れない?

謎。v4l2周りのエラーということはわかっているのだが...。謎。

テスト

Python

python

import cv2
cv2.__version__
print(cv2.getBuildInformation())

Cpp

以下の通りでコンパイル可能。

g++ -ggdb XXXX.cpp -o XXXX `pkg-config --cflags --libs opencv`

ソースの削除について

基本的にsudo make installを済ませたらもとのOpencvのフォルダに用はないです。
jetsonは容量がカツカツなので中でコンパイルした人はデリート推奨。
私は怖いのでまるっとドライブにバックアップしました。
stackoverflow.com

実行の際のコツ(最初に空の関数を呼び出す)

Opencvでは初回の関数呼び出し時にGPUを初期化する的な動作があるため,
実行する際にはとても時間がかかるという問題があります.

実際にOpenCVのフォーラムに問い合わせたところ,
以下のように空のcuda関数を呼び出すことで初期化を済ませることができるという回答を得ました.

cv::cuda::GpuMat test;
test.create(1, 1, CV_8U);

answers.opencv.org

Python Computation Time Mesurement Class(Pythonでプログラムの実行時間を測る方法)

Computation time measuring is important issue for most programmers.
Pythonで実行時間を測って可視化したい時があるかと思います。

Basis (原理)

Many functions exist for the time measurement.
以下に詳しくありますがtimeをimportすればOKです。
16.3. time — 時刻データへのアクセスと変換 — Python 3.6.3rc1 ドキュメント

I use time.time() to get current time.
There are some alternative such as time.clock() or something.

そのうち、time.time()で現在の時間を浮動小数点で得ることができるため、

start=time.time()
# ここでなにかする
end=time.time()
dt=end-start # かかった時間

となります。

My class (作成したクラス)

いちいち宣言したり、過去の値を保存するのは管理が面倒で馬鹿らしいため、クラスとして実装しました。

import numpy as np
import matplotlib.pyplot as plt
import time # time.time() to get time

class Checktime:
    def __init__(self):
        self.checktime = [0]
        self.difftime = []
        self.start = time.time()
    def check(self,name=""):
        self.checktime.append(time.time()-self.start)
        self.difftime.append(self.checktime[-1]-self.checktime[-2])
        print(name)
        print("Now time is "+str(self.checktime[-1])+" [s]")
        print("Computed in "+str(self.difftime[-1])+" [s]\n")
        
    def show(self):
        leng = len(self.checktime)
        plt.plot(np.arange(0,leng,1),np.array(self.checktime),label="Accumulation time")
        plt.plot(np.arange(1,leng,1),np.array(self.difftime),label="Each Process time")
        plt.legend()
        plt.grid()
        plt.show()

How to Use

使い方は簡単。

import

Just put this function on you source or save this program as "FILENAME.py" and write as follows.

from FILENAME import *

上のソースコードをまるっとコピーして自分のソースに貼るか、適当な名前で保存して上のようにImportできます。

Usage

プロセス名を入れることでどのプロセスにどれだけ時間がかかったかを可視化できます。

# init 
T = Checktime()

# do something
T.check() # print current time

# or you can name your processing
T.check("PROCESS NAME")

# Finally you can see the time consumption graph
T.show()


以下のような出力を得るでしょう。
results is like this:
Console output

Image Reading
Now time is 0.00387120246887 [s]
Differ from 0.00387120246887 [s]

Get Histogram
Now time is 0.00810813903809 [s]
Differ from 0.00423693656921 [s]

Get Mask
Now time is 0.00903105735779 [s]
Differ from 0.000922918319702 [s]

Extract ORB Feature
Now time is 0.0206370353699 [s]
Differ from 0.0116059780121 [s]

出て来る図はこんな感じ。

Figure output
f:id:ossyaritoori:20171005075324p:plain

pyplotlibに関する覚書

python初心者にありがたいわかりやすい日本語記事がこちら。
bicycle1885.hatenablog.com

Python 配列の形式メモ

c++Pythonを行ったり来たりしていると頭が混乱してきます。

どれがどれ?

基本は以下の3つ。

  • ()がtuple
  • []がlist
  • {}がdict

これらに加えてnumpyのarrayとmatrixがよく使う奴らでしょう。

とりあえず名前さえわかればGoogle先生に聞くことができるので
最低限どんなオブジェクトを対象にしているのか知っておきましょう。

以下にあまりわかり易くなかったTutorialも載せておきます
Lesson 6 - Tuples, Lists, and Dictionaries

各々の違い

  • list :
  • tuple :要素を変更できないlist
  • dict :文字通り辞書。

Pythonのこれらの配列は型の異なる値を代入可能です。
すごいけどそのせいで時々エラーを見逃しちゃったり...

あと、Printした時のnumpyのarrayとlistの出力はとても似通っています。

listのlist的表現

よく出てくるのはこういうやつらです。[({の形からどのタイプのファイルか確認できます。
私は現状、Printした時に横に並ぶのがlistで縦に並ぶのがarrayと覚えています。

[[0. 0.],[1. 1.],[2. 2.]] #こっちがlistのlist

[[0. 0.]
 [1. 1.]
 [2. 2.]] # np.array

初期化と要素へのアクセス

よく使う機能をまとめます。

  • 長さを取得するlen()はtuple,list,dict全てに使えます。

list

[]でアクセスします。もちろん0から。

list = [2005, 2006, 2007, 2008] #初期化
list[0] #最初の要素へのアクセス

リストオブジェクト - リスト - Python入門

tuple

listに準じます。

tuple = (2005, 2006, 2007, 2008) #初期化
tuple[0] #最初の要素へのアクセス

タプル - Python入門

dict

数字が名前になっただけです。

dict = {"yamada":75, "endou":82} #初期化
value =  dict["yamada"] #yamadaへのアクセス

2次元配列とかの対処

以下を見てください。なんてことないでしょう?

list = [10, [20, 16], [32, 34], 18]
list[1] # [20, 16] が左から2番めの要素
list[1][0] # 20 が[20,16]の1番目の要素

各種変換

実際numpy arrayとの変換とかが一番使う気もします。
まぁおいおい追記しましょう。

tuple と list 相互変換

tuple(list)
list(tuple)

numpy arrayとlist 相互変換

これはよく使うと思います。

a = [1, 2]
np.asarray(a) # array
b = np.array([1, 2])
b.tolist() # list

練 習 問 題

Q1.以下の配列の構造を答えよ

a=[(2228.739013671875, 1203.9490966796875), (2898.794189453125, 1092.8704833984375), (3060.037353515625, 852.7973022460938)

Q2.上のaを以下のbへと変換するコードを書け

print(b)
[[[[ 2228.73901367  1203.94909668]
   [ 2898.79418945  1092.8704834 ]
   [ 3060.03735352   852.79730225]]]]


Ans:

b=np.array([[a]])

使用例

OpenCVでcalcOpticalFlowPyrLK()関数へとKeyPointsに渡すときに使います。

Passing ORB Features to calcOpticalFlowPyrLK - OpenCV Q&A Forum

にゃーんAAジェネレータ

にゃーん。

f:id:ossyaritoori:20170929174031p:plain

にゃ?

ーーーーーーーーーーーー                                          んん                      にんに      ーー
ーーーーーーーーーーーー      に  にゃににににに            にににゃゃゃにに    にににににににに  ーーーーー
ーーーーーーーーーーーー      にーゃ        ににゃににゃに    ー  にゃゃににに  ににゃに    にに  に    ーー
ーーーーーーーーーーーー      に  にに              にゃに  ん      ににゃゃに        にゃにゃにに      ーー
ーーーーーーーーーーーー        に  ゃ                    ににににに      に      にににににゃにに      ーー
ーーーーーーーーーーーーー        にゃに                  ににににに              ににににに  ゃ      ーーー
ーーーーーーーーーーーーーーー      にゃ                      に            ん          に      に        ー
ーーーーーーーーーーーーーー          にに                  ゃゃゃゃ                  ゃゃゃに    に      ー
ーーーーーーーーーーーーーーー      に          ん          ゃゃゃに                  ゃゃゃ      に      ー
ーーーーーーーーーーーーーーー      ゃ      ににんににに              ん    にゃゃゃゃに            に    ー
ーーーーーーーーーーーーーーーーーーにににににに    にににに                ゃゃゃゃゃゃ            に    ー
ーーーーーーーーーーーーーーー      ゃ  にゃゃゃんゃゃゃに                    にゃゃに          に  に    ー
ーーーーーーーーーーーーーーー      ににに                に                                    に  ー    ー
ーーーーーーーーーーーーーーー        に                          に      ゃゃゃゃに      ん      に      ー
                      ーーーー          に          ー              ににに        にににに      に        ー
                            ーー          にに                                              に          ーー
  に      ににに                ー        ににゃゃに              んん  ん          ににゃゃに          ーー
  に  ににににゃににに                  に        ににゃゃゃににににんににににゃゃゃにに      にに          
      ににににに  にに  に            に                にゃゃにににににゃゃ            に        にに      
                  ゃに    に          に                  ゃ          にゃゃゃににんににーに          に    
ーーー                に    ゃ      にーに              にゃ    ににゃに          ににゃに          ーに    
ーーーーーー            に    に    ゃ  にゃ              にににに                ゃに                ゃ    
ーーーーーーーー        ゃ    ゃ    に    にゃに                              にゃに              にゃ      
ーーーーーーーーーー    に    に    に        にゃに        ん          にゃゃにににににににににゃにゃーー  
ーーーーーーーーー        に    に  に            ににゃににににににゃにに                ん        ゃ    ー
ーーーーーーーーー        ゃ    に  ゃ                      にに                  ー                に    ー
ーーーーーーーーーー        に    にゃ                                  ーーーーーーーー          に      ー
ーーーーーーーーーー          にに  にににに      ーーーーーーーーーーーーーーーーー            に        ー
ーーーーーーーーーーー                にゃに      ーーーーーーーーーーーーーー                に          ー
ーーーーーーーーーーーーー                にー    ーー                                  ににゃに        ーー
ーーーーーーーーーーーーーーーー      ー  に      ー          に  ん        ににににゃゃに    に        ーー
ーーーーーーーーーーーーーーーーー          ー  ー        にに  んんんん      ゃに          ーーー      ーー
ーーーーーーーーーーーーーーーーー      に    ー        に                      にに          に      ーーー
ーーーーーーーーーーーーーーーーー        に        にに          ーーー            にに    に        ーーー


にゃ

にゃーん

ArduinoのPID制御ライブラリはどれがいいか

本来なら車輪の再発明はなるべく避けるべきと言いたいところですが
Arduinoのライブラリはちょっと挙動が特殊だったりするので自分で書いたほうが安全のような気もします.

見解まとめ

少なくとも公式のPIDライブラリはデフォルトの設定を一度見直して使うべきです。
アンチワインドアップがないのでI制御のゲインはなるべく落とすなど,ちょっとしたチューニングをいじる必要がありそうです.


発展的な事をしたい場合やリアルタイム性が気になる場合は「自分で書こう

というのが結論。

公式のPIDライブラリ

公式のページをどうぞ。
Classできちんと書かれており、見た目すっきりとした感じで書くことができます。
Arduino Playground - PIDLibrary

以下日本語で解説してくれている人もいますね。
ArduinoのPIDライブラリ - カラクリの館

中身について

Arduino-PID-Library/PID_v1.cpp at master · br3ttb/Arduino-PID-Library · GitHub

Compute関数の中をみると、まず前回との時間差分をとってサンプリング時間を超えているかどうか調べて,PIDゲインを更新。
うーん,,,うーん,,,,
追記:一周回ってまずまずなんじゃないかと思ってきました。


毎時更新されるわけじゃないので以下のようにif文を使ったほうが安全です。

  if(myPID.Compute()){//値が更新されたならTrueが帰る
     // 出力値の更新をここに書く
  }

計算自体は特に離散化するでもなく,連続系のままPID制御をしています。(安全っちゃ安全)

「初心者が比較的安全に動かせる」「ゲインをハンドチューニングする」ことを念頭において作られているようです.

不満

  • 一定周期で関数を実行してくれない。
  • サンプリング時間のデフォルトが200ms(遅すぎ)
  • デフォルトで負値の信号を生成しない(Limitがかけられている)
  • 出力制限はかけているのにアンチワインドアップが組み込まれていない。

デフォルトの設定が悪すぎるのが一番の問題。

モータ制御とかをしたいと思ったら200msの制御周期とワインドアップ,負値の出力不可の三連コンボでまず振動・発散します。
泣く泣くI制御を切る初心者の顔が浮かぶ(´・ω・`)

ArduPIDライブラリ

Gitに個人の作ったライブラリがあります。
GitHub - Tellicious/ArduPID-Library: A PID Library for Arduino digitalized with the Tustin's method with Anti-Windup

良いところ

  • きちんとTustin変換で離散化した制御器(離散化による不安定化とかいう負の側面もあるけど)
  • AntiWindupを2種類搭載

この上の2つだけで公式ライブラリより推したいところです。
プログラムの使い勝手としてはまぁまぁでしょうか。
サンプル時間をきちんと管理していない場合,AutoCompute()機能を使わないと少々変な挙動になったりするので要注意です。


あと,疑似微分の時定数もちゃんといじれたりと制御屋が作ったんだなぁと感じさせるライブラリ。

不満

  • 依然厳密なサンプル時間で実行しているわけではない。

- 私のコードにバグが残っている所(このライブラリは多分悪くない)

  • これが公式ではないという点(一番の問題点)

結論・再び

あまり良いものがないのできちんとしたものを書けば多方面から感謝されること請け合い。
(つべこべ言わずに自分で書いてはどうだろうか。)

余談:タイマ割り込みとシリアル通信の相性の悪さ

タイマ割り込みを実装しようとすると一部関数やピンが使えなくなるようです。
arduino使い方:タイマー割り込み(MsTimer2)

もっと問題なのはシリアル通信などを阻害するというケースがある点で,ROSのSubscriberとかと共存させるのにちょっと手の込んだ事をする必要がありそうですね。
Enabling timer overflow interrupt breaks rosserial - ROS Answers: Open Source Q&A Forum
arduino rosserial with interrupt - ROS Answers: Open Source Q&A Forum

解決法はもう少し暇で余裕のあるときに探します。