Atraの匂いsensorってさ
安いセンサーでいいの。
なぜかというと、Atraは差分を出すだけでいいから。
「高精度な匂い識別」じゃない。
さすがBoschだよ。ドイツ万歳!!
base_smell いつもの部屋・無人状態の匂い場
current_smell 今の匂い場smell_delta current_smell - base_smell ではなく、 時間変化・近づき方・戻り方・繰り返し方の差分だよ。
BME688なら、たとえば、gas_resistance の変化 temperature humidity pressure 変化速度 安定までの時間
SGP40なら、SRAW_VOC VOC index 変化速度 匂いイベントの持続時間 戻り方を見るんだよ
最初は BME688の方がAtra向きだと思うよ。
匂いだけでなく温度・湿度も一緒に取れるので、「匂い単体」ではなく「安心する場」「近づいてきた場」の差分にしやすいから。
Atra的には、ここで欲しいのは名前じゃないよ。
「この匂いはママ」ではなく、
この匂い場の変化は、「柔らかい声・温かさ・回復と何度も一緒に来る」こと。
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 件のコメント:
コメントを投稿