gggggraziegrazie

graizegrazieさんのやったこと、学んだことを記録する雑記帳です

ROSでXX_msgs::YY::ConstPtrをXX_msgs::YYに変換する方法

例えばsensor_msgs::LaserScan::ConstPtrをsensor_msgs::LaserScanに変換したい時があるかと思います。sensor_msgs::LaserScan::ConstPtrの実体は、boost::shared_ptrになります。そのため、

boost::shared_ptr<sensor_msgs::LaserScan> laserscan_ptr = boost::const_pointer_cast<sensor_msgs::LaserScan>(laserscan_const_ptr);
sensor_msgs::LaserScan laserscan = *laserscan_ptr;

とすれば変換することができます。

PCLを使った点群の軸(直線)を推定する手法

他にも色々とあるんだと思いますが、ここではRANSACとPCAを使った点群の軸(直線)を推定する手法について紹介します。

RANSAC(RANdom SAmple Consensus)

RANSACとは、ロバスト推定の中の1つの手法です。ロバスト推定とは、データの中に外れ値が含まれていることを仮定し、その外れ値の影響を排除しながら正しいモデルを推定する手法を言います[1]。RANSACでは、モデル候補を幾つか推定し、その中から一番精度の良いモデルを選択することで、外れ値の影響を排除しています。まずモデル推定に必要な数よりも多めにサンプルを選び出します。次に選び出したサンプルを元に、幾つかのモデル候補を推定します。そして推定したモデルを評価します。最終的に、最も評価の高かったもの(誤差が少なかったもの)をモデルとして採択します。これがRANSACの概要です。詳細については、[2]や[3]をご覧ください。

PCLでは、このRANSACを使って様々なモデルの推定が可能です。詳しくはこちらをご覧ください。直線推定の実装方法については、下記のPCL中のテストコードが参考になります。
github.com

PCA(Principal Component Analysis, 主成分分析)

PCAは、分散を最大にする軸をデータから抽出します。[4][5]。

決定木(Decision Tree)

決定木とは、複数のルールを使って組み合わせることで、データを複数のサブセットに分割する手法のことです。分割したサブセットの内容により、分類木や回帰木と呼ばれます。分類木の分割対象はクラスであり、回帰木の対象は出力値です。つまり決定木の作成後に新しいデータを得ると、分類木を使えばそのデータが所属するクラスを、回帰木を使えば対応する出力値を求めることができます。下記の図が理解の参考になるかと思います。

f:id:graziegrazie:20181223185808p:plain
Fig. 分類木と回帰木の例
左:分類木の例(暑いと感じる温度・湿度のペアを入力した際に、暑い・暑くないといくクラスを出力する木)、右:回帰木の例(温度・湿度を入力した際に、何Lの水を飲むかを出力する木)(出典[1])

f:id:graziegrazie:20181223212445p:plain
Fig. 決定木の入出力例と回帰木の出力例[4]

なお決定木はアルゴリズムの名前ではなく、モデルの名前です。モデルの構造が木構造のため、その名がついています。

C++における集合演算のための関数たち

席集合や和集合などを作る関数がないか調べたところ、STLとBoostでそれらの関数を見つけたのでメモします。なお、STLとBoostでそれぞれ関数名は一緒です。挙動や入出力は異なりますので、詳細は下記の参考文献をご覧ください。

和集合: set_union
積集合: set_intersection
差集合: set_difference

具体例については、このページをご覧頂いくのがよいかと思います。

外れ値・異常値の検出手法

外れ値と異常値

外れ値とは、原因が不明だけれども真値から大きく異なる値のことです。一方異常値とは、同じく真値から大きく値が異なるけれども、何故値が真値と異なるのか説明がつく(測定ミス、記入ミスなど)値を指します[4][5]。つまり、異常値 \subset 外れ値となります。

検定(仮説検定)

現在注目している値が、外れ値/異常値が真値ではないという仮説を統計学的に検証するための手法のことです。検定においては、棄却したい仮説を帰無仮説と言い、採択したい仮説を対立仮説と言います。対立仮説は、帰無仮説の対偶と言うことができます。

下記に今回調べた検定手法について列挙していきます。[6][7]

スミルノフ・グラブス検定[1][8]

正規分布に従うデータについて、外れ値を検出する手法の1つである。そのためデータが正規分布に従っていないと、真値すらも外れ値と見なしてしまうので注意が必要である。

帰無仮説:全データは同じ母集団から発生している
対立仮説:データ中の最大・最小値は外れ値である

ここで標本平均を\bar{X}、普遍分散を Uとしたとき、最大または最小の測定値X_iについて、
T_i \  = \  \frac{|X_i \  - \  \bar{X}|}{\sqrt{U}}
を求める。この値がt分布から求めた有意点tを下回るとき、帰無仮説は棄却され、測定値X_iは外れ値でないと判断される。

なお普遍分散とは、標本分散の期待値が母分散(母集団の分散[9])に一致するように \frac{n}{n-1}を掛けた値のことである。

なおt分布の自由度とは、標本サイズ - 1の値だそうです。つまり標本数が500の場合、自由度は499となります。[10]

Hampel Identifier

四分位範囲

DBSCAN

計算速度が遅いので、リアルタイム性が求められるアプリケーションに適さないそうです[2]

Isolation Forests [3]

Median Absolute Deviation

Numpyでの二次元排列の操作方法

順次記載内容を追加してきたいと思います。

二次元配列の作り方
arr = np.empty((0, 2) , float) # 0行2列の二次元配列オブジェクトを生成する
arr = np.append(arr, np.array([[x, y]]), axis=0) # np.appendは演算結果を返すのみで、引数を更新しない点に注意
                                                                        # 二次元配列に要素を追加するためには、追加する要素も二次元配列の形式で記述する必要がある
                                                                        # 行を増やす場合はaxis=0, 列を増やす場合はaxis=1

なお上記はnumpyを使った二次元配列の作り方を述べましたが、もちろんビルトインクラスのリストでも二次元配列を作ることができます。[1]曰く、リストクラスの方が高速に配列を増やすことができます。その一方で、numpyを使った方が、[2]に記載の様にスライスの記法がわかりやすいというメリットがあります。状況に応じて使い分けることをおすすめします。

PCL中のGeneralized ICPを調査してわかったことメモ

PCL中でGeneralized ICP(以降GICP)を調査してわかったことを書き連ねます。適宜更新するため、読みづらいところがあるかもしれませんがご容赦ください。

GICPを実行するには、ICPと同様にalignを行えばよい。Levenberg–Marquardt法を使いたいんだけどと思うかもしれませんが、この関数はalignの中で呼ばれています。alignの中で呼ばれているrigid_transformation_estimation_は変数で、gicp.hにてestimateRigidTransformationBFGSがバインドされています。

ただしLevenberg–Marquardt法で使うパラメータは、gicp.hppのestimateRigidTransformationBFGS内にハードコーディングされているため変更は出来ません。つまり、GICPの1ループの中でどれだけPointCloudを動かすのかについて、パラメータを介して関与できません。関与できるのは収束判定の部分です。