2018年2月14日水曜日

超音波距離計


 とりあえずユニ基板に実装しました。といってもハンダ付け作業は数日前に行っており、ソフトウェアで結構つまずいていました。

 やっとDACとADCが動くようになったので報告に参った次第。


 STBeeF4miniのボードの下にオペアンプが4回路分、DIP8が2個入ってします。他に抵抗が18本入っています。抵抗は古い1/4Wサイズ(現行の秋月1/2Wサイズ)しか手元になく、寝せると実装面積が足りなくなるので、すべて立てて実装しています。あとはパスコンにチップコンが数個ですが、これはほとんど面積を食いません。



 とりあえずリニアチャープで動くようになったので、机から天井に向けて測距してみました。机から天井まではおよそ160cmです。

 横軸が時間(msec)、縦軸は青(左)がADCの電圧、赤(右)が相関値で、相関値は32bitで計算して、16bitに圧縮して配列に保存し、計算が終わってからまとめてUARTで吐いています。
 一番最初のピークは直接波によるものと思われます。次の9msecあたりが天井からの反射波で、9msec*音速/2は約1.5mなので、実際の距離と近いです(差は温度を計算に含めていないためと思われます)。
 18msecあたりや27msec、36msecあたりにも少しずつピークが有ります。たぶん天井で反射し、それが机で反射し、また天井で反射し、ということだと思いますが、2往復しても十分な相関が有って、目で見るなら4往復でも見える、というのはちょっと驚きです。36msecの部分は6m相当の距離ですが、間で7回反射し、机は乱反射でかなり衰退しているでしょうから、実際の距離ならもっと強くピークが出るはずです。
 最初の20msecは振り切れていますが、これは想定通りです。ブレッドボードで試した時に、オペアンプからの出力が振り切れていても問題なく相関が取れることがわかったので、固定倍率で基板を作りました(適当なボリュームが手持ちになかった、という理由もありますが)。


 今回はマイコンの中で相関の計算を行いましたが、200kspsでパルス幅10msec、受信期間50msecの場合、相関の計算に1100msec以上かかりました。今回はforで回していますが、例えば200組をべた書きでループ回数を減らす、といった、オーバーヘッドを減らすような最適化を行った場合、多少の性能改善が見込めると思います。数割増しから数倍程度に高速化できると思いますが、それでも400-700msec程度です。
 なお、相関の計算は整数で行い、FPUは使用していません。今のところ27.5bit+符号で収まっているので、32bitでも余裕があります。


 ゲート幅5mで500msecとして、ちょっとした測距を行うなら1.5Hz程度でも使えるかもしれませんが、レーダーとして使うのはかなり厳しそうです。
 パルス幅を短くすれば計算量は大幅に減りますが、最大距離も短くなります。

 とりあえず、相関処理の最適化、それと相関結果からピークを拾い出す処理が必要です。あとは温度センサを取り付ければ、温度から音速を計算し、ピーク位置から距離を求めることができます。


 ということで、しばらくは退屈なチューニング作業です。


追記
 当初は計算時間1144msecでしたが、少し最適化して668msec、ループ内の処理を20回べた書きしてループ数を減らして415msecと、当初の2.75倍程度まで高速化できました。
 ちょっといじるだけでパフォーマンスが倍近くになるんだから、コレだからARMはやめられねぇぜ…。最初がどれだけ冗長なコードだったんだ、という話ですが。
 べた書きするだけでも1.6倍になるので、forのオーバーヘッドは馬鹿になりません。この部分はべた書きでforを減らせば減らすほど早くなりますが、あまりやりすぎると効率が悪化していきます。あとメンテナンス性が悪くなったり、べた書きするとメモリ長を変えた際にバッファオーバーランを起こしやすくなります。

 Cortex-M4にはSIMD命令が有るらしいので、コレを有効活用できれば2倍近く効率化できるかもしれません。とはいえ、結果は約28bit必要ですから、32bitコアのSIMDでは処理できません。うまい方法を考える必要があります。

追記
 SIMDを試してみました。668msecが715msecになりました。遅くなってんじゃん!!

 試しに、SMLADを使ってみました。これはint16_t*int16_t+int16_t*int16_t+int32_tを返す命令です。ADC値はすでに16bitで取っているので、相関値を8bitから16bitに変更し、SMLADを呼んだ次第です。よく確認してませんが、引数が増えた分、レジスタが足りなくなってるんじゃないかなーって気がします。

 今まで、ベクトル演算を使えば無条件に早くなるもんだと思いこんでいましたが、そうじゃないんだなぁ。
 特にCortex-M4のSIMDはほぼアセンブリを直接書くので、コンパイラ最適化が弱くなってしまいます。そのため、コードを書く側が最適なコードを考える必要がありそうです。使えそうな命令は他にも色々有るので、もうちょっとじっくり試してみたいと思います。

追記
 SMLADはforで回すより、べた書きしたほうが効率が高いようです。
 スカラ演算をべた書きした場合、1回の計算に3命令が必要です。一方、SMLADを使った場合は1回の計算に5命令が必要です。ただしSMLADは1回の計算で2個の変数を処理できるので、実質的に1回の計算は2.5命令で行うことができます。
 ベクトル演算をべた書きした場合、スカラ演算の1.2倍のパフォーマンスとなります。
 ベクトル演算を40組べた書きした場合、サンプリング期間50msec/パルス幅10msec/200kspsを処理するのに350msecを要しています。

 8bit符号ありレジスタを掛け算して和を返す、みたいな命令があれば更に高速に処理できると思うんですが、8bitの積を計算する命令はなさそうです。

 アセンブリを見てみると、若干謎な動作をしています。このあたりのコンパイラ最適化?をもっと最適化できれば、更に高速になりそうですが、とりあえず眠いので寝ます。
 土曜は朝早いのにまた位相ズレが。毎回こんなことしてるなぁ。

0 件のコメント:

コメントを投稿