【STA24】BNO085yaw角補正結局ローパスフィルタで潰した<力ずくで終わらせた>

2か月間 BNO085のyaw角誤差をRTK GPSの高精度パラメータを使って補正する作業をしてきましたが、
最終的に、RTKで大きなオフセット補正を合わせてから、波形内の誤差の最大の外れ値の周波数が4-5Hzである点に注目して、1Hzのローパスフィルタで、元波形をつぶすことで、誤差のレンジを半減近くにすることで終わりとしました。
あとは、2025シーズンで、いろいろな滑走データと比較検証して、妥当な処理か検証します。

●誤差外れ値の波形

代表的なターンを切り取って、フィルター有無で比較してみると 黒が元波形、赤がフィルタ後波形
上のグラフがHeading角とyaw角のプロットで、200度から80度まで回転してます。CCWなので左ターンです。
下のグラフがheading角とyaw角の差分=誤差をプロットしてあります。
横軸は、時間で、1目盛10msecです。下グラフの黒の波形をみると25-40目盛周期で大きな誤差が発生してます。
これは、Heading角が揺れているのですが、IMUのyaw角はこの揺れを感知してませんので、誤差となってでてきてます。どちらが正しいのかというとHeading角が正しいはずですが、RTKの周期が120msecと遅いため、周期変動が
2-3サンプリングで7度も揺れていることを示してますが、スキー板で方向がそんなに速い周期で変わるのも不自然なので、この大きな外れ値は、基準となるRTK GPS側のノイズだろうと判断して基準波形にローパスフィルタを1Hzカットオフでいれました。そうすると黒線が赤線までなだらかになって、誤差が減少するという結果になります。

●全体の統計値
抜けが多いのですが、抜け以外のデータをみると50-80%レンジまで縮小してます。
角度範囲でいうと、だいたい±3.5度以内におさまっているものが多いということで、±5-8度だった元波形を
フィルターでつぶして、誤差を圧縮したという結果です。

 ●ローパスフィルタのやり方はscipyライブラリを使えばバターワースフィルタが簡単にできます。
コードはgistにあります。https://gist.github.com/dj1711572002/f781fc4eb451b36e230616341e6ffc39

シンプルな説明が判り易かったので、こちらのサンプルプログラムをいただきました。作者様に感謝します。

【Python】scipy.signal.butterでローパス(ハイパス)フィルタを作成する

コードは。def LFP(df,coln)にdfとローパスフィルタをかけるカラム名を引数として渡します。
戻り値は、dfに新たに、coln+”_f”とサフィックスがついたフィルター済みカラムが追加されて戻りdfとして受け取れます。フィルタの詳細はわからないので、いろいろな参考サイトがあります。
https://watlab-blog.com/2019/04/30/scipy-lowpass/

from scipy import signal
def LPF(df,coln):
        print(“df_colnにLowPassFilter処理して、df_coln_fというコラムをつけて戻す”)
        #————butter worse————————–
        # パラメータ設定
        sample_freq = 100
        cutoff_freq = 1#1hz固定
        filter_order = 4
        sec = len(df)*0.01#dfの時間
        # —-colnをフィルタ
        t = np.linspace(0, sec , int(sample_freq * sec))
        y=df.loc[:,coln].to_numpy()
        sos = signal.butter(filter_order, cutoff_freq, ‘lowpass’, output=’sos’, fs=sample_freq)
        yf = signal.sosfiltfilt(sos, y)
        print(yf)
        new_coln=coln+”_f”
        df[new_coln]=pd.DataFrame(yf)
        return df

これを使ったlpf処理の機能
ターン毎に分割されたcsvデータファイル群を一個ずつ読んでフィルター処理して、プロット新たにデータとグラフと統計値保存する機能です。

elif v==’lpf’:#既存のファイル群をまとめてフィルター処理を追加
                        print(“low pass filter “)  
                        print(“>>>> SELECT  sITP_ _ FILE  “)
                        #headMOtのピークを真の値としてHeading360とyawのピーク高さを合わせることで補正とする
                        root = tkinter.Tk()
                        root.withdraw()
                        iDir = r’C:/RTK_Log/hokan/’
                        fTyp = [(“BNOデータファイル”, “*.csv;*.xlsx;*.xls”), (“すべてのファイル”, “*.*”)]
                        filename = FileDialog.askopenfilename(parent=root, initialdir=iDir, filetypes=fTyp)
                        basenameITP = os.path.basename(filename)
                        #シリーズファイル群まとめて処理
                        fsname=csv_Allname(filename)#ファイルシリーズのフル名のソート済みリスト
                        for i in range (0,len(fsname)):
                               print(i,”:”,fsname[i])
                        #==============LPG処理======================================
                        i=0
                        list_stat=[]
                        list_statAll=[]
                        while i<len(fsname):
                                print(“====>”,i,fsname[i])
                                fn=fsname[i]
                                basename = os.path.basename(fn)
                                df = pd.read_csv(fn, low_memory=True)  
                                df=LPF(df,”Heading360″)#LowpassFilter関数
                                df=LPF(df,”yawHpg”)#LowpassFilter関数
                                print(df.columns)
                                print(“df[yasHg_f]=”,df[“yawHpg_f”])
                                df[“H-Ysapg_f”]=df[“Heading360_f”]-df[“yawHpg_f”]
                                df.to_csv(r”C:/RTK_Log/hokan/LPF/LPF_LOG_”+str(i)+”_”+basename+”.csv”, index=True)  # CSV sav
                                 #max min range
                                old_max=df[“H-Ysapg”].max()
                                old_min=df[“H-Ysapg”].min()
                                old_ave=df[“H-Ysapg”].mean()
                                old_std=df[“H-Ysapg”].std()
                                old_range=old_max-old_min
                                fltd_max=df[“H-Ysapg_f”].max()
                                fltd_min=df[“H-Ysapg_f”].min()
                                fltd_ave=df[“H-Ysapg_f”].mean()
                                fltd_std=df[“H-Ysapg_f”].std()
                                fltd_range=fltd_max-fltd_min
                                list_stat=[old_max,old_min,old_ave,old_std,old_range,fltd_max,fltd_min,fltd_ave,fltd_std,fltd_range]
                                list_statAll.append(list_stat)
                                #plot準備
                                fig = plt.figure()
                                x=np.arange(len(df))
                                yHD0=df[“Heading360”].to_numpy()
                                yHDf=df[“Heading360_f”].to_numpy()
                                yyw0=df[“yawHpg”].to_numpy()
                                yywf=df[“yawHpg_f”].to_numpy()
                                yHY0=df[“H-Ysapg”].to_numpy()
                                yHYf=df[“H-Ysapg_f”].to_numpy()
                                                             
                                #plot
                                ax1 = fig.add_subplot(2,1, 1)
                                fig.suptitle(“LPF_1Hz_”+fsname[i])
                                ax1.set_title(“Heading,yaw plot”)
                                ax1.plot(x,yHD0,label=”Heading360″,color=”orange”)
                                ax1.plot(x,yHDf,label=”Filtered Heading360 “,color=”red”)
                                ax1.plot(x,yyw0,label=”yawHpg”,color=”blue”)
                                ax1.plot(x,yywf,label=”Filtered yawHpg”,color=”green”)
                                plt.legend()
                                ax2 = fig.add_subplot(2,1, 2)
                                ax2.set_title(“Error=Heading-yaw plot”)
                                txt1=”old:max=”+str(round(old_max,1))+”,min=”+str(round(old_min,1))
                                txt2=”Filtered:max=”+str(round(fltd_max,1))+”,min=”+str(round(fltd_min,1))
                                ax2.text(x[0],old_max, txt1, size=10,color=”black”)
                                ax2.text(x[0],old_max-1,txt2, size=10,color=”red”)
                                ax2.plot(x,yHY0,label=”source Error data”,color=”black”)
                                ax2.plot(x,yHYf,label=”Filtered Error data”,color=”red”)
                                #ax2.plot(t,yHY0,label=”HeadingFiltered data”,color=”green”)
                                plt.legend()
                                plt.show()
                                fig.savefig(“C:/RTK_Log/hokan/LPF/PLOT_LPF1Hz_”+basename+”.png”)
                                i+=1
                        df_stat=pd.DataFrame(list_statAll)
                        df_stat.columns=[“old_max”,”old_min”,”old_ave”,”old_std”,”old_range”,”fltd_max”,”fltd_min”,”fltd_ave”,”fltd_std”,”fltd_range”]
                        df_stat.to_csv(r”C:/RTK_Log/hokan/LPF/LPF_STAT_0-“+str(i)+”_”+basename+”.csv”, index=True)  # CSV sav
                        print()

 

●以後
IMUのyaw角補正は、

0段目:ターンの開始から終わりまでの範囲で処理を行う。
1段目:RTK headMot角のピーク値とyaw角の差分のオフセット補正する
2段目:yaw角を1Hzのローパスフィルタを通す
以上で、誤差が±3度付近まで減少すると思われます。
以降、この補正方法を基準に、
いろいろな滑り方の実験をして、統計的な誤差がどの程度おさまるか検証していきます。

 

コメントを残す

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