📊

MATLABを用いた異なるサンプリング時間のデータの同期

2023/05/28に公開

はじめに

複数センサのログデータを解析するとき等,異なるデータの時間軸を合わせたいときがある.
その際にデータの時間軸を同期して解析しやすくなる方法について記載する.

背景と状況例

例えば,センサーAとセンサーBによりデータが取得される場合において,サンプリング時間はt_A=2 [s],センサーBのサンプリング時間はt_B=5[s]の状況を考える.
ここで,t=9[s]のときのAとBの値を計算に使いたいとき,センサーAの値はt=8[s]のときの値を,センサーBの値はt_B=5[s]のときの値を採用する必要がある.
さらに,実際のログデータはサンプリング時間通りにぴったりとれているとは限らず,ブレがある.
これらを考慮して,ログデータを用いて解析や計算するプログラムを作成するのは面倒である.

そこで,あらかじめそれぞれのデータの時間軸を合わせておき,解析時間に合わせてサンプリング時間を調整する.

結論

MATLABでログデータをtimetableにし,synchronize関数を用いると簡単にできる.
https://jp.mathworks.com/help/matlab/ref/timetable.synchronize.html#d124e1412234

使用例

移動ロボットのデータセットで使われているUTIAS Multi-Robot Cooperative Localization and Mapping Datasetを対象に処理をする.
ftpでdownloadになるので,ダウンロード先[ftp://asrl3.utias.utoronto.ca/MRCLAM/]をエクスプローラのアドレス欄に入力したらファイルにアクセスできる(windowsの場合).
なお,時間補間するスクリプトが公式で置いているけど,あまりスマートじゃない印象…
http://asrl.utias.utoronto.ca/datasets/mrclam/index.html

datasetのload

データの中身の詳細は公式ページの中身を参照していただきたいが,Dataset1Robot1のOdometryとGround truthのデータを同期させる対象にする.
importdataでファイル名を指定してデータをロードするとtextとdataの二種類が含まれるので,.dataでデータを読み出す.

datasetFolder="./MRCLAM_Dataset1";
robotNumber = "Robot1";
% truth data
groundtruth = importdata(strcat(datasetFolder,"/",robotNumber,"_Groundtruth.dat"))
groundtruth = groundtruth.data; 
% odometry data
odometry = importdata(strcat(datasetFolder,"/",robotNumber,"_Odometry.dat"));
odometry = odometry.data;

次に,データを使いやすいようにtimetableに変換する.
時間軸はduration配列にする必要があるのでseconds()で変換し,そのほかの変数名はVariableNamesで指定する.
なお,groundTruthはモーションキャプチャによる二次元上の位置(X,Y)と姿勢(\theta),Odometryはロボット内での並進速度と回転速度である.

groundtruthTime = seconds(groundtruth(:,1)); %duration配列にする
groundtruthTable = array2timetable(groundtruth(:,2:4),'RowTimes',groundtruthTime,'VariableNames',{'X','Y','Theta'})

odometryTime = seconds(odometry(:,1)); %duration配列にする
odometryTable = array2timetable(odometry(:,2:3),'RowTimes',odometryTime,'VariableNames',{'Vel','AngVel'})

サンプリング時間の確認

サンプリング時間を確認してみる.
各行の時間とその前の時間の差分をとり,プロットしてみる.

Ground truthデータの場合

plot(groundtruthTime(2:end)-groundtruthTime(1:end-1),'b')
xlim([30000 31000])

Odometryデータの場合

plot(odometryTime(2:end)-odometryTime(1:end-1),'r')
xlim([30000 31000])

これらより,どちらのデータにおいてもサンプリング時間の間隔が一定でないことがわかる.

データの結合とリサンプリング

二つのデータの結合をする.
1行目で結合しており,オプションを何も指定しない場合は欠損値はNaNになるため,前の値を保持するようpreviousを指定する.
また,上記の補間をした後でも最初の方はNaNが存在するため(Ground truthの方が開始時間が早く,Odometryはしばらくデータがない),rmmissing()でNaNがある行を削除する.
この処理を2行目で行っている.

syncTable = synchronize(groundtruthTable,odometryTable,'union','previous'); %tableの結合し,欠損値は直前の値を保持.
syncTable = rmmissing(syncTable); %NaNの行を削除
syncTable.Time = syncTable.Time-syncTable.Time(1) %スタート時間を0にする

これにより下記のようなtimetableが得られる.

Timeの列を見てもわかるとおり,サンプリング時間の間隔は一定ではない.
そこで,サンプリング時間を0.01[s]にしてサンプリングしなおす.
これにはretime()を用いる.

dt = seconds(0.01);
syncTableRetime = retime(syncTable,'regular','previous','TimeStep',dt)

下記のようなtimetableが得られる.
特に0.05秒と0.06秒の間を見比べると,欲しい出力になっていることがわかる.

これにより,複数のデータの時間軸を同期することができ,使いやすくなった.
なお,retime関数ではサンプリング時間の指定だけではなく,サンプリング周期で指定したり,時間配列を作成してそれに合わせたサンプリングも行えるので,状況に応じてoptionを使用できる.
https://jp.mathworks.com/help/matlab/ref/timetable.retime.html

おわりに

実質数行でデータの時間同期できてMATLAB便利.

Discussion