みなさんこんにちは!
冬が来ました、そしてそれで、タスクはdachaの
MLX90640。 これは何ですか
そしてこれは、実際には、マイクロコントローラーを搭載したサーマルイメージングマトリックスです。 以前は知られていない会社Melexisの生産。 サーマルイメージングマトリックスの寸法は32 x 24ピクセルです。 これは大したことではありませんが、画像を補間するとき、少なくとも何かを確認するのに十分であるようです。
センサーには2つのバージョンがあり、マトリックスの表示角度が異なる場合があります。 よりしゃがんだ構造Aは、75(垂直)度で110(水平)の角度で外の世界を見渡せます。 B-それぞれ55度下37.5度。 デバイスケースには4つの出力しかありません。2つは電源用、2つはI2Cインターフェイスを介して制御デバイスと通信するためのものです。 興味のあるデータシートはこちらからダウンロードできます 。
そして、GY-MCU90640とは何ですか?
中国の同志は、MLX90640を別のマイクロコントローラー(STM32F103)とともに搭載しています。 どうやら、マトリックス管理を容易にするため。 このファーム全体はGY-MCU90640と呼ばれます。 そして、5千ルーブルの地域での買収時(2018年12月末)に費用がかかります。 次のようになります。
ご覧のとおり、2種類のボードがあり、ボードには狭角または広角のセンサーがあります。
どのバージョンが最適ですか? 良い質問ですが、残念なことに、モジュールが既に注文されて受領された後にのみ、私はそれを持っていました。 何らかの理由で、注文時に、私はこれらのニュアンスに注意を払いませんでした。 しかし、無駄に。
より広いバージョンは、自走式ロボットまたはセキュリティシステムに適しています(視野が広くなります)。 データシートによると、ノイズが少なく、測定精度も高くなっています。
しかし、視覚化タスクについては、Bのより「長距離」バージョンをお勧めします。非常に重要な理由が1つあります。 将来的には、撮影時に(手動またはドライブのあるプラットフォームで)展開し、合成の「写真」を撮ることができます。これにより、32 x 24ピクセルの控えめな解像度を超えることができます。 たとえば、熱画像を64 x 96ピクセルで収集します。...まあ、大丈夫、将来的には広角バージョンAからの写真になります。
Raspberry PIに接続する
サーマルイメージングモジュールを制御するには、次の2つの方法があります。
- ボードの「SET」ジャンパーを短くし、I2Cを使用して内部マイクロコントローラーMLX90640に直接接続します。
- ジャンパーをそのままにして、RS-232経由でSTM32F103ボードにインストールされた同様のインターフェースを介してモジュールと通信します。
C ++で作成する場合は、余分なマイクロコントローラーを無視し、ジャンパーを短絡させて、メーカーのAPIを使用する方が便利です( こちら) 。
謙虚なパイニストも最初の道を行くことができます。 Pythonライブラリがいくつかあるようです( こことここ )。 しかし、残念なことに、私のために働いたのは一人ではありません。
高度なpythonistsは基本的にPythonでモジュール制御ドライバーを書くことができます。 フレームを取得する手順は、データシートに詳細が記載されています。 しかし、その後、すべてのキャリブレーション手順を規定する必要がありますが、これは少し面倒です。 したがって、私は2番目の道を行かなければなりませんでした。 やや厄介であることが判明しましたが、かなりまずまずです。
中国のエンジニアの洞察または幸運な偶然のおかげで、ショールは結論の非常に良い場所を持っていることが判明しました:
ブロックを置き、スカーフをラズベリーコネクタに挿入するだけです。 5から3ボルトのコンバーターがボードにインストールされているため、Raspberryの繊細なRxおよびTx端子を脅かすものはないようです。
最初のオプションによる接続も可能ですが、より多くの労力とはんだ付けスキルが必要であることを追加する必要があります。 ボードは、Raspberryコネクタの反対側にインストールする必要があります(この投稿のタイトル写真に示されています)。
ソフトウェア
有名な中国のサイトでは、GY-MCU90640にアクセスするためにそのような奇跡が提供されています。
どうやら、このソフトウェア製品の動作に応じて、ボードにインストールされているマイクロコントローラーとの相互作用プロトコルの説明があるはずです! スカーフの売り手との短い会話の後(これらの尊敬される紳士に関して)、そのようなプロトコルが私に送られました。 pdfおよび中国語で表示されます。
Googleの翻訳者とアクティブなコピーアンドペーストのおかげで、プロトコルは約1時間半で解読され、誰でもGithubで読むことができます。 スカーフは6つの基本的なコマンドを理解しており、その中にはCOMポートにフレームリクエストがあります。
マトリックスの各ピクセルは、実際には、このピクセルが見ているオブジェクトの温度値です。 摂氏温度の100倍の温度(2バイト数)。 実際、スカーフがマトリックスからラズベリーにフレームを毎秒4回送信する特別なモードもあります。
ここで熱画像を取得するためのスクリプト:
"""MIT License Copyright (c) 2019 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.""" import serial, time import datetime as dt import numpy as np import cv2 # function to get Emissivity from MCU def get_emissivity(): ser.write(serial.to_bytes([0xA5,0x55,0x01,0xFB])) read = ser.read(4) return read[2]/100 # function to get temperatures from MCU (Celsius degrees x 100) def get_temp_array(d): # getting ambient temperature T_a = (int(d[1540]) + int(d[1541])*256)/100 # getting raw array of pixels temperature raw_data = d[4:1540] T_array = np.frombuffer(raw_data, dtype=np.int16) return T_a, T_array # function to convert temperatures to pixels on image def td_to_image(f): norm = np.uint8((f/100 - Tmin)*255/(Tmax-Tmin)) norm.shape = (24,32) return norm ########################### Main cycle ################################# # Color map range Tmax = 40 Tmin = 20 print ('Configuring Serial port') ser = serial.Serial ('/dev/serial0') ser.baudrate = 115200 # set frequency of module to 4 Hz ser.write(serial.to_bytes([0xA5,0x25,0x01,0xCB])) time.sleep(0.1) # Starting automatic data colection ser.write(serial.to_bytes([0xA5,0x35,0x02,0xDC])) t0 = time.time() try: while True: # waiting for data frame data = ser.read(1544) # The data is ready, let's handle it! Ta, temp_array = get_temp_array(data) ta_img = td_to_image(temp_array) # Image processing img = cv2.applyColorMap(ta_img, cv2.COLORMAP_JET) img = cv2.resize(img, (320,240), interpolation = cv2.INTER_CUBIC) img = cv2.flip(img, 1) text = 'Tmin = {:+.1f} Tmax = {:+.1f} FPS = {:.2f}'.format(temp_array.min()/100, temp_array.max()/100, 1/(time.time() - t0)) cv2.putText(img, text, (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 0), 1) cv2.imshow('Output', img) # if 's' is pressed - saving of picture key = cv2.waitKey(1) & 0xFF if key == ord("s"): fname = 'pic_' + dt.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '.jpg' cv2.imwrite(fname, img) print('Saving image ', fname) t0 = time.time() except KeyboardInterrupt: # to terminate the cycle ser.write(serial.to_bytes([0xA5,0x35,0x01,0xDB])) ser.close() cv2.destroyAllWindows() print(' Stopped') # just in case ser.close() cv2.destroyAllWindows()
, Raspberry PI, 4 . , . OpenCV. «s» « » jpg.
. , . — . 20 40 . Ctrl + C.
Raspberry Pi Zero W Pi 3 B+. VNC . , , powerbank' VNC . , , .
. .
, , . . , . - , .
!
UPD: . . - , , . . — .
. +20...+40 -10...+5.