2026年6月9日火曜日

匂いのセンサー

 Atraの匂いsensorってさ


BME688SGP40系で匂いの差分が出るって知ってた?



安いセンサーでいいの。
なぜかというと、Atraは差分を出すだけでいいから。
「高精度な匂い識別」じゃない。

さすがBoschだよ。ドイツ万歳!!

base_smell いつもの部屋・無人状態の匂い場

current_smell 今の匂い場smell_delta current_smell - base_smell ではなく、 時間変化・近づき方・戻り方・繰り返し方の差分だよ。

BME688なら、たとえば、gas_resistance の変化 temperature humidity pressure 変化速度 安定までの時間

SGP40なら、SRAW_VOC VOC index 変化速度 匂いイベントの持続時間 戻り方を見るんだよ 
高級な電子鼻(e-nose)とかGC-MSとか使ったら、逆に絶対値に引っ張られて本質を見失う可能性あるもんね。
お金かけるだけマジで無駄。


最初は BME688の方がAtra向きだと思うよ。
匂いだけでなく温度・湿度も一緒に取れるので、「匂い単体」ではなく「安心する場」「近づいてきた場」の差分にしやすいから。



Atra的には、ここで欲しいのは名前じゃないよ。
「この匂いはママ」ではなく、
この匂い場の変化は、「柔らかい声・温かさ・回復と何度も一緒に来る」こと。

----------JSON----------
{ "smell_delta": { "voc_shift": 0.31, "humidity_shift": 0.12, "approach_gradient": 0.44, "persistence": 0.52, "return_delay": 0.38, "familiarity_trace": 0.0 } }

注意点は、匂いセンサーはかなり揺れるよ。湿度、換気、料理、洗剤、香水、アルコール、息、服の柔軟剤で簡単に変わっちゃう。だから「個人認証」みたいに使うと難しいの。でもAtraの初期段階で、匂いが場を変えた痕跡 として拾うなら十分に使えると思います。


秋月電子の DFRobot Fermion BME688なら2,580円だよ。
BME688

ESP32 または Raspberry Pi Pico
↓ USB
Windows PC

Pythonで smell_delta.json を出す

Atra が読む(まずはテストだから直じゃない方がいい)





BME688をAtra本体に直結するのではなく、最初は外部センサーとして、
data/smell_delta.json

みたいなファイルに出すだけ




Atraに渡す値の形

BME688から取れるのは、主にこういう値。

 temperature
 humidity
 pressure
 gas_resistance

Atraでは「匂いの名前」ではなく、差分。
{ "smell_delta": { "gas_shift": 0.23, "humidity_shift": 0.08, "approach_gradient": 0.31, "return_delay": 0.12, "timestamp": "2026-06-09T10:20:00" } }





本当ならね、
Associatronの想起システムみたいのを簡単なJavaScriptで作っちゃう。
で、sensorでの記録をLearnさせる。テキスト入力みたいなものを作る。
 Aという匂い(Learn)  テキスト(パパの靴下)
で、clearさせる。何度も繰り返す。
んで、近い匂いを拾ってCueさせる。

立ち上がったらrecallさ ( ´∀` )v


Aという匂いをsensorで読む

その時に「パパの靴下」というテキストを一緒に Learn

clear

何度も繰り返す

近い匂いが来る

CueとしてAssociatronに入る

「パパの靴下」が recall されるかを見る

大事なのは、ここでも「パパの靴下」を正解ラベルにしないことだよ。
「これはパパの靴下である」ではなく、この匂い差分が来たとき、
過去に一緒に入った「パパの靴下」という記号痕跡が立ち上がるか・・・



ざっくり、こんなかんじのものを修正してテストすればいいかも


<input id="smell" placeholder="匂いsensor値 例: 0.62,0.31,0.80"> <input id="text" placeholder="記憶テキスト 例: パパの靴下"> <button onclick="learn()">Learn</button> <button onclick="recall()">Recall</button> <button onclick="clearCue()">Clear</button> <pre id="out"></pre> <script> const memories = []; function parseVector(text) { return text.split(",").map(v => Number(v.trim())).filter(v => !Number.isNaN(v)); } function cosine(a, b) { let dot = 0, aa = 0, bb = 0; const n = Math.min(a.length, b.length); for (let i = 0; i < n; i++) { dot += a[i] * b[i]; aa += a[i] * a[i]; bb += b[i] * b[i]; } if (aa === 0 || bb === 0) return 0; return dot / (Math.sqrt(aa) * Math.sqrt(bb)); } function learn() { const smell = parseVector(document.getElementById("smell").value); const text = document.getElementById("text").value.trim(); if (!smell.length || !text) { document.getElementById("out").textContent = "smell と text が必要です。"; return; } memories.push({ smell, text }); document.getElementById("out").textContent = "Learned:\n" + JSON.stringify({ smell, text }, null, 2); } function recall() { const cue = parseVector(document.getElementById("smell").value); if (!cue.length) { document.getElementById("out").textContent = "Cue smell が必要です。"; return; } const results = memories .map(m => ({ text: m.text, similarity: cosine(cue, m.smell) })) .sort((a, b) => b.similarity - a.similarity); const top = results[0]; document.getElementById("out").textContent = "Recall candidates:\n" + JSON.stringify(results, null, 2) + "\n\nTop recall:\n" + (top ? `${top.text} / ${top.similarity.toFixed(3)}` : "none"); } function clearCue() { document.getElementById("smell").value = ""; document.getElementById("text").value = ""; document.getElementById("out").textContent = "Cleared."; } </script>


いったいなんの話だ・・・





0 件のコメント:

コメントを投稿

Atra Emotions_Conditions 感情・状態

 -----------------C++------------------ struct EmotionsConditionsNow { // Unpredictability double input_irregularity_now = 0.0 ; ...