【STA24】Vector線図を左右Trajectory線図に変更<VSCODEに乗換Pgm整理>

Python便利なことが分かったので、本格的にSTA24システムの全処理をPythonで1本プログラムに詰め込んでます。

この10日ほどで、所用で長野ー松本間行ったり来たりしていて、

プログラムの細部内容忘れてしまったので、本記事を備忘録として記述しておきます。
備忘録しながらやっていることが膨大であることを認識した次第です。

毎年なら1システム手一杯ですが、今年は、2システムのデータ処理ですが
Python pandasのおかげで、1システムとあまり変わらない開発進捗でデータ解析プログラム作業進んでます。
※表紙画像は、左右をプロットしてみたものですが、左右の周期が違うので、600msec毎の板の姿勢をプロットしてありますが、
今一左右で合ってない感じがします。RTKでの100msecと120msecの違いは、時間的にリニアではないので、BootsOn100msecをやめて120msecで測定しなおしたほうが良いので4月末の実測から周期合わせます。

●STA24ログシステムの概要
①方式
ネットワーク型RTKの基準局は、長野市のNC_NAGANO様でよませスキー場から20数km離れてますが、
RTKのFix精度は、水平±3cm以内、垂直±10cm以内でてます。
STA24は、欲張って2種類のRTKシステムを組んであります。
・1個は、SkiOnMovingBase:RTK MovingBase方式でスキー板前後にアンテナ2個固定して2点でheading計測します。
同時にIMU(BNO085)計測もしてます。

・2個目は、BootsOnRTK:RTK単独測位ユニットをスキーブーツのつま先に固定してます。
IMU(BNO085)のyaw角でHeadingとして使います。

この2個を右_BootsON右_SkiONと右_BootsOn左_SkiONと使い分けてスキー場で履き替えながら実測しました。
②デバイスと CPU
デバイスとしてRTKGPSチップ(Ublox F9P2個、F9H1個)と9軸IMU(BNO0852個)を使ってます。
BootsOn:F9P(SimpleRTK2B),BNO085(Adafruit BNO085ボード)+ESP32e =>UDPでデータを無線送信1-2MbpsポケットWiFiを経てタブレットログ
SkiOn:F9P(MovingBase:SimpleRTK2Blite)F9H(Rover:SimpleRTK2 F9H)BNO085(Adafruit BNO085ボード)*Teensy4.1 SDログ
+BlueToothSPP(ESP32e)でモニター通信 115200bps タブレットのWIN10 BTSPP機能

③ログデータ
マイコンシステムから10msec,100msec,120msecと2種類の周期でログされている、ログデータバイナリファイル3個からC#のプログラムでcsv変換

●変更点と内容

変更点0:ターングラフの仕様
ターンのNutralPointを抽出して、NutralPoint番号範囲を指定してグラフを描画することで、ターンの観察をする仕様としました。

変更点1:ベクトル線図からTrajectory線図に変更
ベクトルの角度の差をSKID値としてTEXTで表示していたのですが、目視で直観的にスキーのズレを見たいので、
前後のアンテナの軌跡線の間隔のズレ
を表現することで、直観的にズレを見えるようにしました。
左右では、周期が違うので、100msecと120msecの最小公倍数の600msecに一回板の姿勢のプロットしてます。
次回の実測からは、120msecに統一します。同じにしても、左右がぴたりしてないよに見ので、
これは、IMUのyaw角の誤差も相当きいているのかもしれません。同期のズレは10msecでも見える感じがします。板の方向も左右で比較すると1-2度でも目視できるので、シビアになります。
とても安いIMUのYAW角ではスキーではダメかもしれません。

●Trajectroy線図メリット・デメリット
メリットとして、左右システムでセンサのサンプリング周期が違う場合でも軌跡をプロットすれば、同期を無視して動き全体がみえる
デメリットとして、左右のスキー板の同時刻での位置関係が判らない点があります。どの瞬間でそういう関係にあるのかを別の表現方法が必要です。

 

 

 

速度が上がってターン弧が浅くなってくるろとズレも小さくなってきましたが、線が一致してないので全然切れてません。
もっと上手にならないといいデータになりません。

変更点2:PycharmからVSCODEへ乗り換え
プログラムが大きくなって、PycharmからVSCODEへ乗り換えました。
違い1:Pycharmはプロジェクト単位で管理するので、重いフォルダーがたくさんできて、動作も遅い
違い2:VSCODEは、実行ファイルのみでプロジェクトフォルダー無しで、管理できて軽い。
違い3:VSCODEは、エディタとして高度な機能が満載されていて、ユーザーも多く、使い方学習しやすい

 

変更点3:pandas DFで列を新規追加することで開発作業を進める
データ解析方法の開発なので、試行錯誤でどんどん新たなパラメータを作成するので、DFが便利です。
   DataFrameのメリットとして、列の追加が簡単なので、新たなデータ配列を何個でも随時追加できる点です。
下の例では、
3-1極値の探索
データフレームdfに headMot_peaksという列を追加して、初期値としてゼロを代入して新規作成してます。

df[“headMot_peaks”]=0

次に、headMotMA3というデータ列から極値を探索するライブラリーをつかって1行で極値をみつけだします。
条件ピーク間は、最小8個離れて検出

peaks, _ = find_peaks(df[“headMotMA3”], distance=8)

※peaksを使うには、scipyライブラリをimportしておきます。
教えていただいた記事へ感謝 https://note.com/whakamarie/n/n4a72cf0ddd1f

from scipy.signal import find_peaks # type: ignore

※極値検出では、データのノイズが問題となるので、移動平均でノイズを均す処理が良く使われます。
rollingを使って1行で、移動平均完了できます。3は3データの移動平均です。
記事へ感謝 https://note.nkmk.me/python-pandas-rolling/

df[“headMotMA3”] = df[“headMot360”].rolling(3).mean()

 

3-2 pandas DF同志の合体

左側に合体する場合原則として行数が合わないと合体できませんが
生データはデバイス毎に長短があって、行数を合わせないと合体できません。
更に行が周期でそろっているので、デバイス毎の周期を合わせないといけません。
●行数の違うDF合体方法の基本
行数が短いDFの足りない範囲にゼロデータを埋めます
DFは、個別データ並び替えなどは不便なので、加工するDFをlistに変換して、
ゼロを追加して最後にlistをDFに変換する方法がシンプルで判りです。
①list作成

 list_bno = []#listを作成してからDFに変換

②list_bnoにゼロ埋め
7桁x行数分ゼロデータをlistに追加します。この場合整数basestartが行数

list_bno = [[0] * 7] * (basestart)  # 上のデータゼロ埋め

③ ②でゼロ埋めしたlistの続きに、
合体するDF df_BNO120 のデータを1行ずつilocで読みだして、list_bnoに追加(append)します。

blen = len(df_BNO120)

f or  i   in range(0, blen – 1):  # 中間にdf_BNO120をlistに追加

                 r = df_BNO120.iloc[i, :]
                  list_bno.append(r)

④完成したlistをDFに変換
list_bnoをDFに変換すれば、合体したいDFと行数が一致するので合体が可能となります。

 df_BNOL = pd.DataFrame(list_bno)

⑤合体したいDFの右に合体
ゼロ埋めして行数を合わせたdf_BNOLを合体したいDF resultにconcatして resultBRBNというDFを作成できました。
axis=1は横方向合体

resultBRBN = pd.concat([result, df_BNOL], axis=1)

●周期の違うデータの合わせテクニック(将来的には、10msecで補間した曲線で、ぴったり合わせる予定ですが夏ごろになります)
120msecのデータDFに100msec周期のデータDFを合体させると時間がズレてしまうので、一工夫してあります。
600msecに一回時間が一致するので、その間の100msecデータ5個のうち、真ん中の300msecのデータを間引きします。
1個目120-100=20msec差 2個目240-200=40msec差 3個目360-400=40msec差 4個目480-500=20msec 5個目 600-600=0msec
こうすることで、時間ずれが最大40msecまで抑えられます。
さらに、グラフもTrajectory線図になったので、時間ズレが見えないので、600msecに一回スキー姿勢線をいれれば、
時間と左右スキーの角度があわせられます。

変更点4:NutralPoint検出アルゴリズムの変更
たくさんのデータでNutralPointを抽出してみると、headMotのピークとHeadingが一致しない場合があります。
4-1極値
今までは、headMotの差分のゼロ点をNutralPointとしてましたが、
scipyライブラリーのpeaksのほうが楽なので、peaksでNutralPoint抽出します。
4-2headMotとHeading、yawのピーク合わせ
当初のアルゴリズムでは、同時刻にheadMot NutralPointがHeading角と一致するとしてましたが、
実際ピークをみると時間が前後してました。
これは、スキー動作で板がずれながら方向転換している場合はそうなります。

4-3 アルゴリズム
headMotのピーク基準で板の方向を決めるというあらずじなので、時間の前後よりピークのレベルの一致を優先してみました。
やり方は、headMot基準にループを回して、headingのピークがhedMotより先にくるか後に来るか、その時間差はどのくらいかをみて
一番近いheadingのピークをheadMotと一致させる方法をとりました。

 #peaksに近いypeaksを探す
    for i in range(0,len(peaks)-1):
            print(“i=”,i)
            headMotindex.append(peaks[i])
            crow=peaks[i]
            headMotmax.append(df.loc[crow,”headMot_TMA3″])  
            df.loc[crow,”headMot_peaks”]=headMotmax[i]      
            for j in range(0,len(ypeaks)-1):
                    print(“j=”,j)
                    if peaks[i]>ypeaks[j] and peaks[i]<ypeaks[j+1] :                        
                            maesa=peaks[i]-ypeaks[j+1]
                            atosa=ypeaks[j+1]-peaks[i]
                            if maesa<=atosa:
                                df.loc[ypeaks[j],”yaw_peakPos”]=100#yawpeak位置マーク
                                yawmax.append(df.loc[ypeaks[j],”yaw_T”])
                                yawindex.append(ypeaks[j])
                                df.loc[crow,”yaw_peaks”]=yawmax[i]#yawpeask値をheadMotpeak値に並べる                  
                            else:
                                df.loc[ypeaks[j+1],”yaw_peakPos”]=100#yawpeak位置マーク
                                yawmax.append(df.loc[ypeaks[j+1],”yaw_T”])
                                yawindex.append(ypeaks[j+1])
                                df.loc[crow,”yaw_peaks”]=yawmax[i]#yawpeask値をheadMotpeak値に並べる
                    elif peaks[i]==ypeaks[j] or peaks[i]==ypeaks[j+1]:#headMot peakとyaw peakが等しい時
                            df.loc[ypeaks[j],”yaw_peakPos”]=100  
                            df.loc[crow,”yaw_peaks”]=df.loc[ypeaks[j],”yaw_T”]
                            yawmax.append(df.loc[ypeaks[j],”yaw_T”])
                            yawindex.append(ypeaks[j])

 

●以後

BootsONのデータのDF合体まではできたのですが、左右のプロットでエラーがでていてまだできませんので来週くらいには左右ターングラフ完成させたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です