レーザー距離センサーのリバースエンジニアリング (翻訳)

以下記事の翻訳

https://habr.com/en/post/274879/　



以前、キーエンスのレーザー距離センサーLK-G407が不稼働になったことがありました。動作しないだけでなく、専用のコントロールユニットがないと使用できない。しかし、このセンサーには、数ミクロンの精度で距離を測定できたり、秒速50キロメートルのスピードが出たりと、面白い特徴があるんです。そのため、稼働させるためには、センサー本体をかなり掘り下げる必要があり、同時に貴重な体験となります。


![image](https://user-images.githubusercontent.com/40942409/184469431-f539ac88-ee42-4a83-8d27-6a725d094a50.png)

センサーの中身はどうなっているのですか？

センサーの光学部を下の写真に示します。

![image](https://user-images.githubusercontent.com/40942409/184469466-8930b026-46fa-4d65-a2d2-392f9c6ebae4.png)

写真左側がレーザーモジュール、その後ろが光を感知する定規、右側がレンズとミラーです。
このセンサーが、三角測量方式のレーザー距離計に分類されることは、デザインからも明らかです。このようなレンジファインダーの動作原理は、こちらによく書かれています。原理はいたってシンプルで、レーザーが照射される物体までの距離が変わると、メーターレンズとレーザースポットのなす角度が変化する。対物レンズの焦点面に感光する定規やマトリックスを置いておけば、最大出力信号の位置で角度を判断することができます。レーザーとレンズの角度と距離を知ることで、対象物までの距離がわかる。
この方法の利点は、近距離での精度が非常に高いことです。ある条件下では、0.1μmより良い精度が得られます。
また、高速感光定規を使用すれば、高速で距離を測ることも問題ありません。
このような距離計の回路も非常にシンプルです。デバイスに高周波が存在せず、信号の一次増幅が定規自体に行われるからです。
しかし、距離が長くなると精度が急激に落ちるという欠点があります。
このセンサーは長焦点レンズ（焦点距離約150mm）を使用するため、センサーを小型化するためにミラーを搭載しています。

さて、ここでセンサーの電子回路に話を移します。
センサーの2番目の部分はこんな感じです。


写真左側がレーザーモジュール、その後ろが光を感知する定規、右側がレンズとミラーです。
このセンサーが、三角測量方式のレーザー距離計に分類されることは、デザインからも明らかです。このようなレンジファインダーの動作原理は、こちらによく書かれています。原理はいたってシンプルで、レーザーが照射される物体までの距離が変わると、メーターレンズとレーザースポットのなす角度が変化する。対物レンズの焦点面に感光する定規やマトリックスを置いておけば、最大出力信号の位置で角度を判断することができます。レーザーとレンズの角度と距離を知ることで、対象物までの距離がわかる。
この方法の利点は、近距離での精度が非常に高いことです。ある条件下では、0.1μmより良い精度が得られます。
また、高速感光定規を使用すれば、高速で距離を測ることも問題ありません。
このような距離計の回路も非常にシンプルです。デバイスに高周波が存在せず、信号の一次増幅が定規自体に行われるからです。
しかし、距離が長くなると精度が急激に落ちるという欠点があります。
このセンサーは長焦点レンズ（焦点距離約150mm）を使用するため、センサーを小型化するためにミラーを搭載しています。

さて、ここでセンサーの電子回路に話を移します。
センサーの2番目の部分はこんな感じです。


![image](https://user-images.githubusercontent.com/40942409/184469574-77696d75-860f-4dbc-a657-b36d563bcbf8.png)

そして、ボード自体も。


![image](https://user-images.githubusercontent.com/40942409/184469590-f19fb989-0aa6-4dbb-81c8-64053fe27145.png)

そして、その一方で


![image](https://user-images.githubusercontent.com/40942409/184469602-6772cab8-b940-4f08-a2b6-1c8cee20e0bb.png)

プリント基板は4層構造になっているようで、ほとんどの信号線が外層に、電源線が内層に配置されています。
先ほど申し上げたように、センサーのコントロールユニットがなく、これがないと動作が始まらないのです。センサーの回路図もなく、ケーブルのピン配置やセンサーへの電圧供給さえも不明であったことは明らかである。結論 - 回路をリバースエンジニアリングする必要があった。
その結果、以下のような回路が出来上がりました。

![image](https://user-images.githubusercontent.com/40942409/184469622-6d896121-fba8-43a4-86a3-2a52932692b2.png)

もちろん、センサー回路全体を描いたわけではなく、FPGAの部分だけ整理したものです。回路図上の素子番号と基板上の番号は対応していません。
こうして、センサーの構造図が出来上がる。

![image](https://user-images.githubusercontent.com/40942409/184469633-80a39cdc-3e59-4e4a-b209-f9aadfbc6b9d.png)

この図から明らかなように、センサー全体の動作は、FPGA Xilinx Spartan-3Aといくつかのカスタムチップの2つのチップで制御されていることがわかります。しかし、私は非常に幸運でした。回路図によると、カスタムチップはFPGAにしか接続されていません。つまり、FPGA自体がセンサーの全信号を制御することができるのです。

全体のデザインの要となるのが、感光式定規です。この定規は、明らかにオーダーメイドのデザインです。顕微鏡で見ると、その縁の一角に刻まれた文字が見えます。
25-512
LI004-02
512はルーラーの画素数、25は画素の幅をミクロン単位で表したものだと考えていました。後で分かったことだが、私は正しかった。
定規の裏には小さな基板がハンダ付けされています。

![image](https://user-images.githubusercontent.com/40942409/184469665-55acd6cf-7cc9-4a05-a0f4-8955ecfad64a.png)

ラインからの信号を2倍にするオペアンプと、数個の抵抗とコンデンサーが含まれています。本基板は、コネクタP1に接続されています。図からわかるように、ルーラーへの信号線は3本だけです。そのうちの1つは、明らかにその出力からアナログ信号が出ている（同軸の別線で伝送されている）。残りの2ラインはデジタルで、ルーラーを制御するために使用されます。回路を解析してみると、これまたラッキーなことに、回路に電圧をかけると、この線の1本に10MHzの周波数が現れる（4）。この線が定規のクロックを担っていることはすぐにわかりました。明らかに、すべてのルーラー制御は残りのライン(3)から行われます。定規をSTM32F4マイコンに接続し、(3)と(4)のラインに異なる信号を与え始めました。つまり、(3)のライン上に高いレベルがある限り、露光が行われ、定規が光を受けるという、極めてシンプルな仕組みになっている。ライン(3)をLowにした後、14クロックパルスを定規に供給する必要がありますが、その後512クロックパルスの間、アナログ信号を出力します。定規は5Vで動作しますが、FPGAは3.3Vで動作するため、DD2チップでレベルを合わせます。

定規からのアナログ信号はLPFを経て、DA1チップに組まれたリピータに送られます。そして、その信号はPGA-プログラマブル・ゲイン付きアンプAD8369に供給されます。本チップの最大利得は40dBで，入力BIT0-3に任意のコードを設定することにより，プログラムによる利得調整が可能です。このチップは差動信号を増幅するように設計されており、その出力も差動であるため、その両方の出力をオペアンプDA4に接続し、信号を2倍に増幅して1つの信号とする。
そして、そのアナログ信号は10ビットADC AD9200の入力に供給されます。このチップは、すでにSDR受信機でおなじみでした。この場合、デジタル化する電圧の範囲が（0.5～2.5）Vになるように配線されています。ADC出力からデジタル化された信号がFPGAに転送されます。
このADCのCLAMP入力に注目するとよいでしょう。この入力もFPGAで制御されます。入力信号の定数成分を一定のレベルにするためのものです。
データシートから、ADC入力段の回路図を示します。


![image](https://user-images.githubusercontent.com/40942409/184469680-4595d803-f60c-4572-bae9-2549d3037a79.png)

CLAMP入力にHighレベルが印加されると、"CLAMP IN "入力の電圧と同じ電圧がアンプ出力とAINに表示されます。
CINコンデンサは，コンデンサの電圧が (Uin - Uclamp_in) に等しくなるまで充電されます。その後，CLAMP入力がLowになり，アンプがADCに一切影響を与えなくなります。本センサでは、"CLAMP IN "入力がADCの下位基準電圧+0.5Vに接続されています。従って、DA4 出力に何らかの定数成分が存在する場合、CLAMP 機能を使用することにより、ADC 結果への影響を排除することが可能です。
FPGAに最初のテストコンフィギュレーションを書き込んだ後、CLAMP信号を制御しなければ、ADCからの信号が非常に大きなDC成分を持つことが判明したのです。私の実装では、ADCがデータを取り込んでいない間は、単純に1を印加しています。

回路を見れば、多くの電源電圧を使用していることがわかる。センサーに含まれる各種電源や各種平滑コンデンサーの回路図は描いていない。結論から言うと、電源リード（これは2ピンの別コネクタで引き出されている）は、TPS62050 DC-DCコンバータに接続されている。最大電圧は10Vで、6Vからはまだ電子回路が起動しないので、センサーの動作電圧は8Vとしました。

このモジュールで使用されているレーザーダイオードは、別基板の電子回路で制御されています。

![image](https://user-images.githubusercontent.com/40942409/184469702-9e19a7d5-aa12-4f27-bc50-2a557a23cb8d.png)

このボードの図は描いていません。コネクタP3を介してメインボードに接続されています。ご覧のように、レーザーは2本の線で制御されています。そのうちの1つ（3）は、インバータを介してFPGAに接続されており、レーザーをオンにする役割を担っている。FPGAの出力がLレベルになると、レーザーがオンになるのだ。もう1本のライン（4）は、レーザーの出力を制御するために必要です。これはアナログ回線で、DA5-7チップで組み立てたDACで電圧を変化させています（なぜ既製品のDACチップを使わず、こんな複雑な回路を作ったのか理解できません）。

ご存知のように、ほとんどのFPGAは不揮発性メモリを搭載していないため、FPGAのコンフィギュレーションはそれぞれ外部チップに保存する必要がある。今回はフラッシュメモリー専用チップ「DD3 XCF01」です。FPGAの電源が入ると、そこから自動的にコンフィギュレーションをメモリに読み込む。FPGA本体とXCF01はJTAGチェーンで接続されており、コネクタP2に接続されています。その結果、このコネクタを介してXCF01の内部プログラム、FPGAのコンフィギュレーション、デバッグを行うことができます。

こうして、センサーの電子回路の原理を理解し、回路の一部を手に入れることができたのです。これで、実験、つまりFPGAのプログラミングを始めることができます。なお、私はこれまでザイリンクス社のFPGAを扱ったことがありません。必要なプログラマーも不在だったので、いくつかのOpenSourceプロジェクトを組み合わせて自作することになったのです。
プログラマは問題なく動作し、FPGA上で簡単なプロジェクトを実行することができました。FPGAのピンを切り替えるだけの簡単なものです。しかし、その後、コンピュータとの通信が必要になりました。LVDSインターフェースには接続したくないので（LVDSラインが接続されているポートには2.5Vの電源がある）、FPGAとASICをつなぐ2本のトラックだけをカットしたのです。基板上にASICのセットアップやテスト用に設計されたらしいコネクタがありましたが、それは使えないので、そこへ行く2本のトラックもカットして、コネクタのピンとFPGAのピンを接続しました。このコネクタにUSB-UARTアダプタを接続することができました。
手直し後の基板の様子。

![image](https://user-images.githubusercontent.com/40942409/184469725-c4308d44-a3ba-4066-8a17-3c96833db7a4.png)

その後、簡単なプログラムを書いて、UARTの動作テストを行いました。FPGAからUARTで送信された1バイトをコンピュータが正しく受信したのです。次に、ライトバーのデータをパソコンに転送します。このFPGAのプログラムフローチャートを使いました。

![image](https://user-images.githubusercontent.com/40942409/184469784-aa1ae6fc-5764-4aab-934e-2ee7ffff86c8.png)

ISEでのスキーム表示。

![image](https://user-images.githubusercontent.com/40942409/184469800-14048692-977c-4bd9-a0b1-c7fc03cfd597.png)

トップレベルのプロジェクトは回路図エディタで描かれ、それを含むすべてのモジュールはVeriolgで記述されます。このプロジェクトの原理は非常にシンプルで、ADCでデジタル化された定規のデータをFPGAで取り込み、RAMに格納します。512個の信号素子をすべて取り込んだら、UARTでコンピューターに送信する。すべてのデータが転送されると、このサイクルが繰り返されます。このプロジェクトの「sensor_reader」モジュールは、定規、レーザー、CLAMP信号の制御を行います。制御は最もシンプルな方法で実装されており、すべての信号はクロックカウンタで制御されます。tx_controller "モジュールには、UARTトランスミッターモジュールが含まれています。モジュールが外部メモリから受信したデータを伝送するためのものです。
操作中は、対象物までの距離の変化や反射率の変化により、定規上で使用可能な信号のレベルが大きく変化します。信号が小さすぎると測定ができなくなり、信号が大きすぎると測定精度が極端に低下する。このため、アナログ信号の増幅率を調整する必要があります。当初、プロジェクトには手動でゲインを変更できるUARTレシーバーモジュールが含まれていましたが、後にそれを取り除き、自動ゲインコントロール（AGC）を作りました。
最大信号を求めるモジュール（"max_finder"）とAGCモジュールそのもの（"agc_module"）が含まれています。このモジュールも非常にシンプルで、信号レベルが170以下ならゲインを上げ、250以上なら下げるというものです。
すべてのモジュール、定規、ADCは10MHzの周波数からクロックされています。露光時間を5μsと同じにした。そのため、露光処理とFPGAへの信号取り込みには、全体で（5+51）μsを要します。クロックレートが500000bpsの場合、512バイトを転送するのに10msかかり、1秒間に100回計測できることになる。

送信データをリアルタイムで見るための簡単なC#プログラムを作成した。

![image](https://user-images.githubusercontent.com/40942409/184469848-70aa4b99-bdbf-4161-9abe-1862f69ef07b.png)

レーザー光に沿って物体が移動すると、最大ピークの位置が変化する。ルーラーには512ピクセルしかないため、単純にピークの位置を計算すると測定精度が低くなる。そのため、より正確に最大信号の位置を測定するためには、サブピクセル精度で最大信号の座標を決定できる重心探索アルゴリズムを使用する必要がある。
次の式で峰の重心を求めてみた。

ここで、n は画素の総数、Int[i] は i 画素の強度である。
信号中に存在する異なるノイズが精度を低下させないようにするためには、ある閾値を超え、ピークの最大値に近いサンプルだけを処理することが有効である。これは、ゲインが不十分で有用な信号がADCスケールの半分を超えない場合に特に重要です。さらに、ゲインを上げるとノイズレベルが上がり、状況が悪化する。上記のプログラムは、これを考慮したものです。
上の写真では、MAX POSはこの式から算出された値、MAX POS Fは過去50回の測定値の平均値です。
先ほど申し上げたように、結果的に測定周波数は100Hzとなり、UARTのデータレートによって制限されます。しかし、信号をコンピュータ上で処理する必要はなく、FPGA上で処理することも可能であり、これにより測定速度を何倍にも高めることができる。

その結果、この構造を持つFPGA用のプログラムが開発された。

![image](https://user-images.githubusercontent.com/40942409/184470002-f10b01d5-57c1-4ae2-a984-46c4366b25fd.png)

![image](https://user-images.githubusercontent.com/40942409/184470009-b50b14ac-e9d1-4ab5-8c74-47d2e922d6cb.png)

ご覧の通り、一部のモジュールは前回のプロジェクトからの流用です。
この場合、「centroid_finder」モジュールが信号の重心位置（セントロイド）を計算する。データ解析の範囲を限定するために、最大位置とその振幅の粗い値がモジュールに渡される。
これらの値は、信号全体が分析された後にしか計算できないので（つまり、512クロックサイクルの遅延で現れる）、デジタル化されたデータは、同じ遅延で「centroid_finder」の入力に供給されなければならないことになる。データを512クロック分遅らせるために、FIFOバッファを使用します。fifo_logic "モジュールは、FIFOを最初に充填するために使用され、充填されていない場合はFIFOからの読み取りを禁止します。
モジュール "tx_controller_3bytes "は3バイトを連続送信し、そのうちの最初のバイトは0、残りの2バイトは重心位置の計算値を含んでいます。50,000ビット/秒の場合、3バイトを送信するのに60μ秒かかり、これは信号をキャプチャするのにかかる時間とほぼ同じです。ゼロバイトはデータタイミングに使用され，常にその後にハイバイトが続きます。
UARTのデータ転送とデータキャプチャは、"start_capture "信号により同時に開始されます。この信号は、前回の送信が終了し、同時に新しい重心位置が算出された場合に生成される。この結果、距離測定とデータ転送の時間は60μs近くになり、座標測定速度は16.6KSPSとなる。これは、センサーメーカーが公表している値よりも少ない。AD9200の最大速度20MSPSでも512画素からの信号取り込み時間は25.6μsなので、この時間がどのように得られるかは明らかではありませんが、最小測定時間20μs（50KSPSに相当）を指定しています。しかも、それは露光時間を考慮しない場合です。

先ほども言いましたが、このセンサーがダメだったんです。私が知る限り、強い振動のある産業プラントに搭載され、その振動でセンサーレーザーが故障した（暗い面にはセンサーが反応しなくなった）ことが問題だったようです。
残念ながら、ネイティブ・ダイオードにはマーキングがなかった。レーザーモジュールのレーザーダイオードを購入した5mWのレーザーダイオードに交換してみたが、しばらくして強度が低下した。レーザーモジュールの電子回路が高出力ダイオード用に設計されている可能性が高い（ただし、パルスモードで動作するため、平均発光レベルはかなり低い）。
せめてセンサーだけでも動作させるために、定発光モードで動作するレーザーダイオードドライバーを自作したのです。

![image](https://user-images.githubusercontent.com/40942409/184470057-edda3c7c-28d2-4dc7-8dc7-f4fa1a2d51c2.png)

センサーのネイティブレーザーダイオードは、レーザーモジュールの金属製ハウジングに巻き込まれていたため、新しいレーザーダイオードをモジュールに接着するだけでよかったのです。しかし、使用したダイオードの寸法が元のダイオードと若干異なっていたため、レーザーモジュールの焦点をうまく合わせることができませんでした。
組み立てたセンサーはこのような感じです。

![image](https://user-images.githubusercontent.com/40942409/184470068-b3c61c18-29f7-4f2b-88c2-1f163c8e0c68.png)

センサーで距離を測るためには、センサーを校正する必要があります。つまり、センサーが返す結果と実際の距離とを結びつける法則を決めるのです。校正プロセス自体は、センサーから対象物までの距離とそれに対応する結果をもたらす一連の測定である。
センサーと対象物の距離を非常に正確に測定する必要があります。キャリブレーションを行うために、リニアエンコーダとレーザーセンサー本体を搭載したベンチを作りました。

![image](https://user-images.githubusercontent.com/40942409/184470088-0a7be74b-bf97-411a-bbda-f6aee353b191.png)

エンコーダーの先端に固定されたプレートまでの距離を測定します。
すべてのデータを収集したら、Mathcadで回帰分析を行うことができます。
その結果、私が得たのは次のような式である。
value_mm = 70.0 / Tan(-0.000277757*max_pos + 0.28355) - 366.23554
式中の定数の値は、センサー部分の位置に依存することは明らかである。部品のわずかなズレにより、距離の計算値が正しくありません。そのため、すべての部品が非常にしっかりと固定されている必要があります。

以下の動画は、センサーからのデータをどのように処理できるかを示したものです。

https://www.youtube.com/watch?v=xuD4vOZ_E2s

動画の前半では、感光棒からの信号がどのように見えるかを紹介しています。被写体が暗い場合、振幅は変わらないが（AGC動作のため）、ノイズレベルが大きくなっていることがよくわかる。
動画の後半では、センサーから重心位置を取得し、それを距離に再計算するプログラムです。また、エンコーダーから受け取った物体距離の値をプログラムに送ります。エンコーダ距離の下に、これらの距離の差が表示されます。移動中は1mm以上の差になるが（これは距離伝達と表示の遅れのため）、どの距離で停止しても0.03mmを超えないことが分かる。

センサーから受け取ったデータを解析しやすくするために、センサーから受け取ったデータをwav形式で保存するプログラムを書きました。このようなファイルは、サウンドエディターで開くことができ、さまざまなフィルターを適用することができます。
例えば、トランス電源の壁面に向けたセンサーからの信号はこんな感じです。

![image](https://user-images.githubusercontent.com/40942409/184470126-ec025974-22aa-4bd8-ae31-76735437df56.png)

電源を入れると、電源が振動し始めた様子がわかると思います。
音楽が流れているスピーカーのコーンにセンサーを向けてみたところ、サウンドエディターで処理した後、確かに音が聞こえました。

このようにして、動作しないセンサーに新しい命を与えると同時に、Xilinx FPGAの経験を積むことができました（将来のプロジェクトで必要になる可能性があります）。`

https://github.com/iliasam/keyence_LK_G407_reverce