Wio Terminal を使用して赤外線サーマルイメージングカメラを構築する
この文書は AI によって翻訳されています。内容に不正確な点や改善すべき点がございましたら、文書下部のコメント欄または以下の Issue ページにてご報告ください。
https://github.com/Seeed-Studio/wiki-documents/issues
Wio Terminal を使用して赤外線サーマルイメージングカメラを構築する
概要
Grove - 赤外線温度センサーアレイ (AMG8833) と Wio Terminal を使用して、低コストで FLIR™ のようなサーマルイメージングカメラを簡単に構築できます!ただし、Grove - 赤外線温度センサーアレイ (AMG8833) の解像度は 8 x 8 (64 ピクセル) に限られており、場合によっては十分な場合もあります。そのため、コード内で線形補間を使用して 70 x 70 (4900 ピクセル) に拡張し、より良い表示を実現しています。
このデモは Kris Kasprzak の動画 に触発されています。Wio Terminal と Grove - 赤外線温度センサーアレイ (AMG8833) に対応するようにいくつかの変更が加えられています。ほとんどのグラフィックは、全体的なパフォーマンスとフレームレートを向上させるために、最初に TFT LCD スプライトに描画されるようになっています。また、画面中央に照準を追加し、照準の温度を表示する機能も追加されています。
必要な部品
特徴
照準の正確な温度を表示
サーマルカメラで熱源を検知
右ボタンでグリッドの ON/OFF 機能を切り替え可能
必要な Arduino ライブラリ
LCD スクリーンライブラリ
Seeed_Arduino_LCD
をインストールしてください。詳細は Wio Terminal LCD を参照してください。Seeed_AMG8833 リポジトリを訪問し、リポジトリ全体をローカルドライブにダウンロードしてください。
- 次に、Seeed_AMG8833 ライブラリを Arduino IDE にインストールします。Arduino IDE を開き、
スケッチ
->ライブラリをインクルード
->ZIP形式のライブラリを追加
をクリックし、先ほどダウンロードしたSeeed_AMG8833
ファイルを選択します。
- 次に、Seeed_AMG8833 ライブラリを Arduino IDE にインストールします。Arduino IDE を開き、
Arduino の手順
Grove - 赤外線温度センサーアレイ (AMG8833) を Wio Terminal の Grove I2C インターフェースに接続します。
完全なコードを こちら からダウンロードするか、以下をコピーしてください。
コードをアップロードします。
完全なコード
注意: この赤外線サーマルイメージングカメラのパフォーマンスとフレームレートを向上させるには、Wio Terminal の CPU スピードを 200MHz にブーストすることができます。ツール
-> CPU Speed
-> 200MHz(Overclock)
を選択してください。

/*
このプログラムは、サーマルカメラの読み取り値を 8 x 8 配列から拡大するためのものです。
10 倍に拡大し、240 x 320 に表示します。
補間は線形で、ディスプレイが 5-6-5 カラーパレットであることを考慮すると「十分良い」ものです。
最終的な配列は、内部ポイントのみで構成された 70 x 70 の配列です。
改訂履歴
1.0 Kasprzak 初期コード
1.1 Anson(Seeed Studio) Grove - 赤外線センサー(AMG8833) に対応するよう Wio Terminal に適応
*/
#include <Seeed_AMG8833_driver.h>
#include <TFT_eSPI.h> // グラフィックライブラリをインクルード (スプライト機能を含む)
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite Display = TFT_eSprite(&tft); // "img" スプライトオブジェクトを "tft" オブジェクトへのポインタで作成
// pushSprite() によって TFT にプッシュされる際にポインタが使用されます
unsigned long CurTime;
uint16_t TheColor;
// 初期色を設定
uint16_t MinTemp = 25;
uint16_t MaxTemp = 35;
// 補間された色の変数
byte red, green, blue;
// 行/列補間の変数
byte i, j, k, row, col, incr;
float intPoint, val, a, b, c, d, ii;
byte aLow, aHigh;
// 表示「ピクセル」のサイズ
byte BoxWidth = 3;
byte BoxHeight = 3;
int x, y;
char buf[20];
// グリッド表示を切り替える変数
int ShowGrid = -1;
// 8 x 8 測定ピクセル用配列
float pixels[64];
// 補間された配列用配列
float HDTemp[80][80];
// カメラオブジェクトを作成
AMG8833 ThermalSensor;
// グリッドのオン/オフを切り替える
void toggleGrid() {
ShowGrid = ShowGrid *-1;
Display.fillRect(15, 15, 210, 210, TFT_BLACK);
yield();
}
void setup() {
Serial.begin(115200);
// ディスプレイを開始し、背景を黒に設定
tft.begin();
tft.fillScreen(TFT_BLACK);
// グリッドのオン/オフを切り替える割り込み
pinMode(WIO_KEY_A, INPUT);
attachInterrupt(digitalPinToInterrupt(WIO_KEY_A), toggleGrid, FALLING);
// ディスプレイの回転を設定 (ディスプレイに応じて 0 に変更する必要がある場合があります)
tft.setRotation(3);
// スプラッシュスクリーンを表示
tft.setCursor(20, 20);
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.print("Thermal ");
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.print("Camera");
// センサーを起動
bool status = ThermalSensor.init();
delay(100);
if (!status) {
Serial.print("AMG8833 の初期化に失敗しました");
}
// 初期テストのためにカメラを読み取る
ThermalSensor.read_pixel_temperature(pixels);
// ステータスを確認し、結果を表示
if (pixels[0] < 0) {
while (1) {
tft.setCursor(20, 40);
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.print("Readings: FAIL");
delay(500);
}
}
else {
tft.setCursor(20, 40);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.print("Readings: OK");
delay(2000);
}
tft.fillScreen(TFT_BLACK);
Display.createSprite(TFT_HEIGHT, TFT_WIDTH);
Display.fillSprite(TFT_BLACK);
// カラー補間ルーチンのカットオフポイントを取得
// 温度スケールが変更されたときにこの関数が呼び出されます
Getabcd();
// センサーの最大値と最小値に一致するスケールで凡例を描画
DrawLegend();
}
void loop() {
CurTime = millis();
// 温度エリアの大きな白い枠を描画
Display.fillRect(10, 10, 220, 220, TFT_WHITE);
// センサーを読み取る
ThermalSensor.read_pixel_temperature(pixels);
// 8 x 8 センサー配列を取得したので
// より大きな画面に補間する
// 8 行を補間する (最初に 8 センサーピクセル間の 70 列ポイントを補間)
for (row = 0; row < 8; row ++) {
for (col = 0; col < 70; col ++) {
// 最初の配列ポイントを取得し、次を取得
// また、後続の行のために 8 ずつ増加する必要があります
aLow = col / 10 + (row * 8);
aHigh = (col / 10) + 1 + (row * 8);
// 各 10 列の補間量を取得
// ここでは単純な線形補間を行い、主にパフォーマンスを高めるためです
// ディスプレイは 5-6-5 カラーパレットなので、高度な補間は低色深度で失われます
intPoint = (( pixels[aHigh] - pixels[aLow] ) / 10.0 );
// 各列をどれだけ増加させるかを決定 (基本的に 0-9)
incr = col % 10;
// 補間値を見つける
val = (intPoint * incr ) + pixels[aLow];
// 70 x 70 配列に格納
// ディスプレイが逆方向を向いているため、行を反転して行データを転置
HDTemp[ (7 - row) * 10][col] = val;
}
}
// 70 列の生データが得られたので
// 各 70 列を補間する
// Arduino では到底速くならない...Teensy で > 72 MHz が出発点
for (col = 0; col < 70; col ++) {
for (row = 0; row < 70; row ++) {
// 最初の配列ポイントを取得し、次を取得
// また、後続の列のために 8 ずつ増加する必要があります
aLow = (row / 10 ) * 10;
aHigh = aLow + 10;
// 各 10 列の補間量を取得
// ここでは単純な線形補間を行い、主にパフォーマンスを高めるためです
// ディスプレイは 5-6-5 カラーパレットなので、高度な補間は低色深度で失われます
intPoint = (( HDTemp[aHigh][col] - HDTemp[aLow][col] ) / 10.0 );
// 各列をどれだけ増加させるかを決定 (基本的に 0-9)
incr = row % 10;
// 補間値を見つける
val = (intPoint * incr ) + HDTemp[aLow][col];
// 70 x 70 配列に格納
HDTemp[ row ][col] = val;
}
}
// 70 x 70 配列を表示
DisplayGradient();
// 画面中央に照準を表示
Display.drawCircle(115, 115, 5, TFT_WHITE);
Display.drawFastVLine(115, 105, 20, TFT_WHITE);
Display.drawFastHLine(105, 115, 20, TFT_WHITE);
// スプライトを画面にプッシュ
Display.pushSprite(0, 0);
// 画面中央の温度を表示
tft.setRotation(3);
tft.setTextColor(TFT_WHITE);
tft.drawFloat(HDTemp[35][35], 2, 90, 20);
// フレームレートを出力するには、以下をコメント解除
Serial.print("Frame rate: "); Serial.println(1/(0.001*(millis() - CurTime)));
}
// 結果を表示する関数
void DisplayGradient() {
tft.setRotation(4);
// 70 行を処理
for (row = 0; row < 70; row ++) {
// 高速でちらつきのないグリッドを描画する方法--10 ピクセルごとに 2x2 にするだけで、3x3 にする必要はありません
// グリッドの後に線を描画すると、ちらつきが多すぎます
if (ShowGrid < 0) {
BoxWidth = 3;
}
else {
if ((row % 10 == 9) ) {
BoxWidth = 2;
}
else {
BoxWidth = 3;
}
}
// 次に各 70 列を処理
for (col = 0; col < 70; col++) {
// 高速でちらつきのないグリッドを描画する方法--10 ピクセルごとに 2x2 にするだけで、3x3 にする必要はありません
if (ShowGrid < 0) {
BoxHeight = 3;
}
else {
if ( (col % 10 == 9)) {
BoxHeight = 2;
}
else {
BoxHeight = 3;
}
}
// 最後に 70 x 70 ポイントを描画、補間された色を取得する呼び出しに注意
Display.fillRect((row * 3) + 15, (col * 3) + 15, BoxWidth, BoxHeight, GetColor(HDTemp[row][col]));
}
}
}
// 高速かつ効果的なカラー補間ルーチン
uint16_t GetColor(float val) {
/*
値を渡して R G B を計算
公開されているいくつかの方法に基づいていますが、基本的に R G B をグラフ化し、単純な線形方程式を開発しました
再び、5-6-5 カラーディスプレイでは、正確な温度から R G B への色計算は必要ありません
以下のリンクに基づく方程式
http://web-tech.ga-usa.com/2012/05/creating-a-custom-hot-to-cold-temperature-color-gradient-for-use-with-rrdtool/index.html
*/
red = constrain(255.0 / (c - b) * val - ((b * 255.0) / (c - b)), 0, 255);
if ((val > MinTemp) & (val < a)) {
green = constrain(255.0 / (a - MinTemp) * val - (255.0 * MinTemp) / (a - MinTemp), 0, 255);
}
else if ((val >= a) & (val <= c)) {
green = 255;
}
else if (val > c) {
green = constrain(255.0 / (c - d) * val - (d * 255.0) / (c - d), 0, 255);
}
else if ((val > d) | (val < a)) {
green = 0;
}
if (val <= b) {
blue = constrain(255.0 / (a - b) * val - (255.0 * b) / (a - b), 0, 255);
}
else if ((val > b) & (val <= d)) {
blue = 0;
}
else if (val > d) {
blue = constrain(240.0 / (MaxTemp - d) * val - (d * 240.0) / (MaxTemp - d), 0, 240);
}
// ディスプレイのカラーマッピング関数を使用して 5-6-5 カラーパレットを取得 (R=5 ビット, G=6 ビット, B=5 ビット)
return Display.color565(red, green, blue);
}
// 温度 vs RGB グラフのカットオフポイントを取得する関数
void Getabcd() {
a = MinTemp + (MaxTemp - MinTemp) * 0.2121;
b = MinTemp + (MaxTemp - MinTemp) * 0.3182;
c = MinTemp + (MaxTemp - MinTemp) * 0.4242;
d = MinTemp + (MaxTemp - MinTemp) * 0.8182;
}
// 凡例を描画する関数
void DrawLegend() {
// カラー凡例と最大値および最小値のテキスト
j = 0;
float inc = (MaxTemp - MinTemp ) / 160.0;
for (ii = MinTemp; ii < MaxTemp; ii += inc) {
tft.drawFastHLine(260, 200 - j++, 30, GetColor(ii));
}
tft.setTextSize(2);
tft.setCursor(245, 20);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
sprintf(buf, "%2d/%2d", MaxTemp, (int) (MaxTemp * 1.8) + 32);
tft.print(buf);
tft.setTextSize(2);
tft.setCursor(245, 210);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
sprintf(buf, "%2d/%2d", MinTemp, (int) (MinTemp * 1.8) + 32);
tft.print(buf);
}
// コード終了