Arduino圧力センサーの使い方|配線・コード・実例

プログラミング・電子工作


Arduinoの圧力センサー(圧センサー)は、FSR402と10kΩ抵抗1本があれば動かせます。この記事では、Arduino UnoにFSRをつなぎ、押した強さに応じて数値が変わるところまでを最短で進め、そのあとLED連動、反応しないときの対処、重量への換算、リハビリ機器への応用まで順に解説します。

執筆:YOFUKASI LAB運営者(理学療法士/Arduino・Pythonを使った医療機器制作・学会発表経験あり)

この記事でできること

  • FSR(感圧センサー)を押すと、シリアルモニタの数値が0〜1023の範囲で変化する
  • シリアルプロッタで押す力の変化をグラフで見る
  • 一定以上の力で押したときだけLEDを点灯させる
  • 数値を「力・重量」に換算するときの正しい手順と限界がわかる

最短手順(3ステップ)

  1. 配線する:5V → FSR → A0 → 10kΩ抵抗 → GND
  2. コードを書き込む:下のスケッチをコピーしてArduino IDEから書き込む
  3. 確認する:シリアルモニタ(9600bps)を開き、FSRを押して数値が増えるのを見る


Arduinoで使える圧力センサーとは?

電子工作で「Arduinoの圧力センサー」「圧センサー」と呼ばれるものの多くは、FSR(Force Sensitive Resistor/感圧センサー)です。薄いフィルム状の部品で、押すと電気抵抗が下がります。Arduinoはこの抵抗の変化を電圧の変化として読み取ります。

FSRの基本原理

FSRは、押していないときは非常に高い抵抗(ほぼ電気が流れない状態)で、押す力が強くなるほど抵抗が小さくなります。ただし抵抗値そのものはArduinoで直接測れないため、固定抵抗と組み合わせた「分圧回路」で電圧に変換し、アナログ入力ピンで読み取ります。

圧力・力・荷重の違い

言葉が混ざりやすいので、先に整理しておきます。

用語 意味 単位
力・荷重 物を押す・引く作用の大きさ N(ニュートン)
質量 物そのものの量。はかりの表示 g、kg
圧力 面積あたりの力(力 ÷ 面積) Pa、kPa

FSRは「圧力センサー」と呼ばれますが、実際に反応しているのは感圧面にかかる力です。同じ力でも当たる面積や位置で出力が変わるため、「何kPa」と正確な圧力を出す部品ではない点を覚えておいてください。

FSR402の特徴

入門で最もよく使われるのが、Interlink Electronics社のFSR 402です。メーカーのデータシートでは次のように示されています(購入した製品の最新版データシートで確認してください)。

  • 感圧部は直径約14.7mmの円形(ショートテール版のFSR 402 Shortは約12.7mm)
  • 反応し始める力は約0.2N、感度範囲はおよそ0.2N〜20N(押し方や回路によって変わる代表値)
  • 押していないときの抵抗は10MΩ以上
  • 同一個体の再現性は±2%、個体間のばらつきは同一ロット内で±6%、ヒステリシスは平均+10%(いずれも1,000gの荷重で測定した値)
  • 耐久性は1kgの荷重を毎秒4回かける条件で1,000万回の作動試験

なお、古い版のデータシートでは感度範囲が0.1N〜10Nと記載されているなど、版によって数値が異なります。寸法や測定範囲はモデルによっても違うため、必ず手元の製品に対応するデータシートを確認してください。

つまりFSR402は、指で軽く押す〜しっかり押す程度の力を「強い・弱い」の相対値として検出するのに向いたセンサーです。

代表的な圧力センサーの種類

Arduinoで使える圧力センサーの種類(FSR・FlexiForce・ロードセル・ピエゾ・気圧センサー)と特徴の一覧

種類 測れるもの 向いている用途
FSR(FSR402など) 押す力の相対的な強弱 タッチ検出、スイッチ、入門工作
FlexiForce 押す力(FSRより定量的) 力の計測、試作機器
ロードセル+HX711 荷重・重量(高精度) はかり、荷重測定
ピエゾ素子 衝撃・振動 打撃検出、ノック検知
気圧センサー(BMP280など) 大気圧 気圧・高度の測定

ピエゾ素子は押し続ける力(静的な力)の測定には向かず、気圧センサーは空気の圧力を測る部品で、物を押す力は測れません。「押す力」を扱いたい場合の選択肢は、FSR・FlexiForce・ロードセルの3つです(違いは後半で比較します)。

初心者にFSRをすすめる理由

  • 追加部品が抵抗1本だけで、ライブラリも不要
  • 端子が2本で極性(向き)がなく、配線ミスが起きにくい
  • 薄く小さいので、ボタンやグリップ、靴の中敷きなどに仕込みやすい

正確な重量を測りたい場合はロードセルを選びます。FSRは出力が力に比例せず、個体差や時間による変化もあるため、「何グラムか」を正確に知る用途には不向きです。ロードセルは荷重に対してほぼ直線的に出力が変わるため、HX711などのアンプと組み合わせて、はかりのような測定ができます。作り方はArduinoでロードセルを使う方法|HX711の配線・校正・重さ測定で解説しています。

Arduino圧力センサーで必要な部品

部品 数量 用途
Arduino Uno R3(互換機可) 1 センサーの電圧を読み取るマイコン
FSR402などのFSR 1 押す力で抵抗が変わるセンサー本体
抵抗 10kΩ 1 FSRと組み合わせて電圧を作る(分圧)
ブレッドボード 1 はんだ付けなしで配線する
ジャンパーワイヤ(オス-オス) 3本以上 Arduinoとブレッドボードをつなぐ
USBケーブル 1 PCからの書き込みと電源供給

LED連動まで作る場合は、LED 1個と220Ω〜330Ωの抵抗 1本を追加します。

ボード選びに迷う場合はArduinoメインボード徹底解説、工具や部品を一通りそろえたい場合はArduinoに必要な工具・部品リストを参考にしてください。

FSRの端子へのはんだ付けは避けるのが無難です。FSRのフィルムは熱に弱く、加熱しすぎると溶けて壊れます。SparkFunの接続ガイドでも、はんだ付けは低温・短時間で作業できる経験者向けとされています。初心者はブレッドボードに直接差すか、クリンチャーコネクタなどの圧着式コネクタを使ってください。


圧力センサーFSRの配線方法

配線は次の1本道です。ブレッドボードの使い方に不安がある方は、先にブレッドボード完全攻略ガイドを読むとスムーズです。

Arduino 5V ──── FSR ────┬──── Arduino A0
                        │
                      10kΩ
                        │
Arduino GND ────────────┘

Arduino UnoとFSR圧力センサーの配線図。5VからFSR、A0、10kΩ抵抗を経てGNDにつなぐ分圧回路

接続表

つなぐもの 接続先
FSRの端子① Arduinoの5V
FSRの端子② ArduinoのA0、および10kΩ抵抗の片側(同じ列に差す)
10kΩ抵抗のもう片側 ArduinoのGND

FSRに極性はないので、端子①②はどちら向きでも構いません。

5V・GND・A0の役割

  • 5V:回路に電圧をかける電源
  • GND:電圧の基準(0V)
  • A0:FSRと抵抗の間の電圧を読み取るアナログ入力

なぜ10kΩ抵抗が必要なのか

Arduinoが読めるのは電圧だけで、抵抗値は直接読めません。そこでFSRと固定抵抗を直列につなぎ、5Vを2つの抵抗で分け合う「分圧回路」を作ります。A0に現れる電圧は次の式で決まります。

A0の電圧 = 5V × 10kΩ ÷ (FSRの抵抗 + 10kΩ)
  • 押していない:FSRの抵抗が非常に大きい → A0はほぼ0V
  • 強く押す:FSRの抵抗が小さくなる → A0が5Vに近づく

抵抗がないと、押していないときにA0がどこにもつながらない「浮いた」状態になり、数値がでたらめに動きます。10kΩ抵抗はA0をGND側に引いておくプルダウン抵抗の役割も兼ねています。

抵抗値を変えるとどうなるか

抵抗値 傾向
大きくする(47kΩ〜100kΩ) 軽い力でも数値が大きく変わる。強い力ではすぐ上限に近づく
10kΩ(標準) 指で押す範囲をバランスよく読める
小さくする(3.3kΩなど) 軽い力では変化が小さい。強い力の違いを見分けやすい

まずは10kΩで試し、検出したい力の範囲で数値の変化が小さいと感じたら変更してください。SparkFunのFSR接続ガイドでは、センサーの測定範囲を広く使う場合の出発点として3.3kΩが紹介されています。抵抗を変えたときは、抵抗値を使う計算(換算の章のR_FIXED)も合わせて書き換えます。

3.3V系のボード(ESP32など)を使う場合は、FSRを5Vではなく3.3Vにつなぎます。アナログ入力に許容電圧を超える電圧をかけると故障の原因になります。また、ADCの分解能もボードによって異なります(Uno R3は10bit=0〜1023、ESP32は標準で12bit=0〜4095、Uno R4は初期設定10bitで変更可能)。使用するボードの公式仕様で、動作電圧・分解能・アナログ入力の許容電圧を確認してください。

Arduino圧力センサーのコード【コピペで動作確認】

上の配線のまま、次のスケッチを書き込みます。Arduino Uno R3を基準にしています。

/*
  FSR(圧力センサー)の基本読み取り
  配線:5V → FSR → A0 → 10kΩ → GND
  対象:Arduino Uno R3(5V・10bit ADC)
*/
const int FSR_PIN = A0;   // FSRと10kΩ抵抗の接続点

void setup() {
  Serial.begin(9600);     // シリアル通信を9600bpsで開始
}

void loop() {
  int raw = analogRead(FSR_PIN);          // 0〜1023
  float voltage = raw * (5.0 / 1023.0);   // 電圧(V)に換算

  Serial.print("raw: ");
  Serial.print(raw);
  Serial.print("  voltage: ");
  Serial.print(voltage, 2);
  Serial.println(" V");

  delay(200);             // 0.2秒ごとに表示
}

コードの意味

コード 意味
const int FSR_PIN = A0; センサーをつないだピンに名前を付ける
Serial.begin(9600); PCとの通信速度を9600bpsに設定する
analogRead(FSR_PIN) A0の電圧(0〜5V)を0〜1023の整数で読む
raw * (5.0 / 1023.0) 読み取った数値を電圧に直す
delay(200); 200ミリ秒待つ(表示が速すぎないようにする)

analogRead()の詳しい仕組みはanalogRead・analogWriteの使い方、スケッチ全体の書き方はArduinoのコード・プログラムの書き方(コピペ可20選)、intとfloatの違いはArduinoの変数の型の使い分けで解説しています。

書き込みと動作確認の手順

Arduino IDEをまだ入れていない場合は、先にArduino IDEのダウンロード・インストール方法を済ませてください。

  1. ArduinoをUSBケーブルでPCにつなぐ
  2. Arduino IDEで「ツール」→「ボード」から Arduino Uno を選ぶ
  3. 「ツール」→「ポート」で、Arduinoが接続されているポートを選ぶ
  4. スケッチを貼り付け、「書き込み」(→ボタン)を押す
  5. 「ツール」→「シリアルモニタ」を開く(画面右上の虫眼鏡アイコンでも可)
  6. シリアルモニタ右下の通信速度を 9600 baud に合わせる

押す前・押した後の見え方

状態 rawの値
押していない 0、または0に近い値
軽く押す 値が上がり始める
強く押す さらに上がり、1023に近づく

具体的な数値はセンサーの個体、押し方、抵抗値によって変わります。「押すと増え、離すと戻る」ことが確認できれば成功です。変化しない場合は反応しないときの対処へ進んでください。


圧力センサーの値をグラフで表示する

数字の列よりも、グラフのほうが力の変化を直感的に確認できます。Arduino IDEのシリアルプロッタを使います。配線は基本回路のままです。

/*
  FSRの値をシリアルプロッタで表示(生値と移動平均)
  配線:5V → FSR → A0 → 10kΩ → GND
*/
const int FSR_PIN = A0;
const int NUM_SAMPLES = 10;     // 移動平均に使うデータ数

int samples[NUM_SAMPLES];
int sampleIndex = 0;
long total = 0;

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NUM_SAMPLES; i++) {
    samples[i] = 0;
  }
}

void loop() {
  int raw = analogRead(FSR_PIN);

  // 移動平均:古い値を引き、新しい値を足す
  total -= samples[sampleIndex];
  samples[sampleIndex] = raw;
  total += raw;
  sampleIndex = (sampleIndex + 1) % NUM_SAMPLES;
  int avg = total / NUM_SAMPLES;

  // 「名前:値」をカンマ区切りで出力するとプロッタに凡例が出る
  Serial.print("min:0,max:1023,");   // 縦軸を0〜1023に固定するための線
  Serial.print("raw:");
  Serial.print(raw);
  Serial.print(",avg:");
  Serial.println(avg);

  delay(20);   // 約50回/秒
}

シリアルプロッタの開き方

  1. スケッチを書き込む
  2. シリアルモニタを閉じる(同時に開けない場合があります)
  3. 「ツール」→「シリアルプロッタ」を開き、通信速度を9600 baudに合わせる
  4. FSRを押したり離したりして、線が上下するのを確認する

Arduino IDEのシリアルプロッタに表示されたアナログ入力A0のセンサー値の波形グラフ。青い波形で信号の変動をリアルタイムに可視化した画面。

サンプリング間隔とノイズ低減

  • サンプリング間隔:delay(20)で約1秒に50回読み取ります。指で押す動きを見るには十分です。速い動きを見たいときは値を小さくします。
  • 移動平均:直近10回分の平均(avg)を取ることで、細かなばらつきが滑らかになります。NUM_SAMPLESを増やすほど滑らかになりますが、反応は遅くなります。
  • 配線の見直し:ノイズが大きいときは、ジャンパーワイヤを短くする、FSRの端子の差し込みを確認する、といった物理的な対策が先です。

delay()を使わずに一定間隔で読み取りたい場合は、millis()でdelayを使わない書き方を参考にしてください。PC側でグラフを描きたい場合はArduinoのセンサーデータをリアルタイムでグラフ表示する方法|Python連携で解説しています。

圧力を加えるとLEDが光る装置を作る

基本回路にLEDを追加し、一定以上の力で押したときだけ点灯させます。

基本回路から追加するもの

追加部品 接続
抵抗 220Ω〜330Ω Arduinoの9番ピン → 抵抗 → LEDの長い足(アノード)
LED LEDの短い足(カソード) → GND

LED連動のコード(ヒステリシス付き)

/*
  FSRを一定以上の力で押すとLEDが点灯
  配線:5V → FSR → A0 → 10kΩ → GND
        D9 → 220Ω → LED → GND
*/
const int FSR_PIN = A0;
const int LED_PIN = 9;

// しきい値(仮の値。シリアルモニタで実際の値を見て調整する)
const int THRESHOLD_ON  = 400;   // これ以上で点灯
const int THRESHOLD_OFF = 300;   // これ以下で消灯

bool ledOn = false;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int raw = analogRead(FSR_PIN);

  if (!ledOn && raw >= THRESHOLD_ON) {
    ledOn = true;
  } else if (ledOn && raw <= THRESHOLD_OFF) {
    ledOn = false;
  }

  digitalWrite(LED_PIN, ledOn ? HIGH : LOW);

  Serial.print("raw: ");
  Serial.print(raw);
  Serial.print("  LED: ");
  Serial.println(ledOn ? "ON" : "OFF");

  delay(20);
}

しきい値の決め方

コード中の400と300は調整用の仮の値です。シリアルモニタで「点灯させたい強さで押したときのraw」を確認し、その値をTHRESHOLD_ONに、それより少し低い値をTHRESHOLD_OFFに設定します。

ちらつき対策:ヒステリシス

しきい値を1つだけにすると、ちょうど境目の力で押したときに値が上下して、LEDが高速でON/OFFを繰り返します(スイッチのチャタリングに似た現象)。上のコードのように「点灯する値」と「消灯する値」を分けておくと、一度点灯したら少し力を緩めても消えにくくなり、動作が安定します。これをヒステリシスと呼びます。

if文による条件分岐の基礎はArduinoのif文の書き方完全ガイドで解説しています。


Arduinoの圧力センサーが反応しないとき

症状から原因の候補を絞り込みます。原因は1つとは限らないので、表の「確認方法」を上から順に試してください。

症状 考えられる原因 確認方法 対処
数値が0のまま FSRが5VまたはA0につながっていない/ブレッドボードの列がずれている/FSRの接触不良・断線 FSRを外し、5VとA0をジャンパーワイヤで直接つなぐ。1023になればArduino側は正常 FSRの端子の差し込み位置と深さを確認。端子が細く抜けやすいので差し直す
数値が1023のまま 10kΩ抵抗がGNDにつながっていない/A0が5Vに直結している/FSRの端子同士がショートしている FSRを抜いて値が0付近になるか確認。ならなければ抵抗〜GNDの配線を疑う 抵抗の片側がA0の列、もう片側がGNDにつながるよう差し直す
数値が不安定(押していないのに動く) 抵抗が入っておらずA0が浮いている/接触不良/配線が長くノイズを拾っている 抵抗の両端がA0とGNDに確実につながっているか確認。配線を軽く揺らして値が飛ぶか見る 抵抗と配線を差し直す。配線を短くする。移動平均を入れる
押すと数値が減る FSRと抵抗の位置が逆(5V → 抵抗 → A0 → FSR → GND) 5Vにつながっているのが抵抗かFSRかを確認 故障ではなく回路の違い。本記事のコードに合わせるなら、FSRを5V側、抵抗をGND側に入れ替える
シリアルモニタに何も表示されない 書き込みに失敗している/ポートやボードの選択が違う/通信速度が合っていない IDE下部に書き込み完了の表示が出たか。ボーレートが9600か(文字化けする場合は速度の不一致) ボードとポートを選び直して再書き込み。シリアルモニタの速度をSerial.begin()の値に合わせる
配線は正しいはずなのに反応しない ブレッドボードの電源ラインが中央で分断されている/ピン番号の違い(A0とA1など)/ジャンパーワイヤの断線 A0と5V、A0とGNDを直接つないで1023と0が出るか確認。ワイヤを別の物に交換 電源ラインを使わずArduinoのピンから直接配線する。コードのピン番号と実際の配線を合わせる
予想より感度が悪い 抵抗値が力の範囲に合っていない/感圧部の端を押している/下が柔らかく力が逃げている 硬く平らな面に置き、感圧部の中央を指の腹で押して値を見る 軽い力を見たいなら抵抗を47kΩ程度に上げる。センサーを硬い平面に固定する
センサーが壊れる・すぐ劣化する 端子へのはんだ付けの熱/感圧部や根元を折り曲げた/尖った物で押した/定格を超える荷重 感圧部や端子の付け根に折れ・しわ・剥がれがないか目視 はんだ付けを避けコネクタを使う。感圧部を曲げない。データシートの範囲内で使う

切り分けのコツ:FSRを外した状態で「A0を5Vにつなぐと1023」「A0をGNDにつなぐと0」が出れば、Arduinoとコードは正常です。問題はFSRか抵抗まわりの配線に絞れます。

ブレッドボード側の原因をさらに詳しく調べたい場合は、ブレッドボードのエラー・トラブル100選も確認してください。

FSRの値を力や重量に換算する方法

「この数値は何グラム(何ニュートン)なのか」を知りたくなりますが、FSRではおおよその目安までと考えてください。手順と限界を順に説明します。

analogReadの数値の意味

Arduino Uno R3のanalogRead()は、0〜5Vを0〜1023の1024段階で表します(Arduino公式リファレンス)。この数値は「A0の電圧」を表しているだけで、力そのものではありません。

電圧とFSR抵抗値への換算

分圧の式を逆算すると、FSRの抵抗値を推定できます。

電圧      V    = raw × 5.0 ÷ 1023
FSR抵抗   Rfsr = 10kΩ × (5.0 − V) ÷ V
/*
  FSRの電圧と抵抗値(推定)を表示
  配線:5V → FSR → A0 → 10kΩ → GND
*/
const int FSR_PIN = A0;
const float VCC = 5.0;          // 電源電圧(V)
const float R_FIXED = 10000.0;  // 固定抵抗(Ω)

void setup() {
  Serial.begin(9600);
}

void loop() {
  int raw = analogRead(FSR_PIN);
  float voltage = raw * (VCC / 1023.0);

  Serial.print("raw: ");
  Serial.print(raw);
  Serial.print("  V: ");
  Serial.print(voltage, 2);

  if (raw == 0) {
    Serial.println("  Rfsr: (no pressure)");   // 0で割るのを防ぐ
  } else {
    float rFsr = R_FIXED * (VCC - voltage) / voltage;
    Serial.print("  Rfsr: ");
    Serial.print(rFsr / 1000.0, 1);
    Serial.println(" kOhm");
  }

  delay(500);
}

ここで得られる抵抗値は、抵抗の誤差(通常±5%や±1%)やUSB電源電圧のずれを含む推定値です。

単位の違い(N・g・kg・kPa)

  • N(ニュートン):力の単位。1kgの物体にかかる重力は約9.8N
  • g・kg:質量の単位。「500g相当」は、500gのおもりを載せたときと同じ力という意味
  • kPa:圧力の単位。力を接触面積で割った値

おもりを使って校正した値は「重量相当値(g相当)」であり、圧力(kPa)とは別物です。圧力を求めるには力がかかっている面積が必要ですが、FSRでは実際の接触面積を正確に把握するのが難しいため、kPaへの換算は基本的におすすめしません。

単純な比例計算が不正確になる理由

  • 非線形性:FSRの抵抗は力に比例せず、さらに分圧回路を通すため、rawと力の関係は曲線になります。「rawが2倍=力が2倍」にはなりません。
  • ヒステリシス:力を増やしていくときと減らしていくときで、同じ力でも出力が異なります。
  • クリープ(ドリフト):同じ力をかけ続けても、時間とともに出力が少しずつ変化します。FSR 400シリーズのデータシートでは、1kgの荷重で時間の対数1桁あたり5%未満とされています。
  • 個体差:同じ型番でもセンサーごとに特性が異なります。
  • 接触面積と荷重分布:感圧部の一部だけを押す、斜めに力がかかる、下の素材が柔らかい、といった条件で出力が変わります。

このため、「1点だけ測って比例計算する」方法は誤差が大きくなります。

既知の荷重による複数点キャリブレーション

精度を上げるには、重さのわかっているおもりを複数使い、「このrawのときは何g」という対応点をいくつか記録して、その間を補間します。次のスケッチは、校正値をコードに書き込まず、シリアルモニタから実測して登録する方式です。

準備

  • 重さのわかっているおもりを3〜5種類(キッチンスケールで量った物でも可)
  • 感圧部全体に均等に力を伝える小さな平らな当て板(感圧部と同程度の大きさの硬い円板など)
  • センサーを硬く平らな面に固定する
/*
  FSRの複数点キャリブレーション(重量相当値の推定)
  配線:5V → FSR → A0 → 10kΩ → GND

  準備:シリアルモニタの改行設定を「LF(改行)」、速度を 9600 baud にする

  使い方(シリアルモニタから送信):
  1. 何も載せない状態で 0 を送信(最初の校正点は必ず0g)
  2. 既知のおもりを載せて数秒待ち、その重さ(g)を送信(例:200)
  3. 軽い順におもりを替えて 2 を繰り返す(最大6点)
  4. 2点以上登録すると、重量相当値(g)の推定を表示

  コマンド:list … 登録済みの校正点を表示 / clear … 校正点をすべて消去

  ※数字以外の入力、前の点より軽い重さ、rawが増えなかった点は登録しません
  ※校正点は電源を切る・リセットすると消えます
  ※登録した範囲の外は推定しません(最大点の値で頭打ち)
*/
const int FSR_PIN = A0;
const int MAX_POINTS = 6;
const int BUF_SIZE = 16;

int   calRaw[MAX_POINTS];    // 校正点のanalogRead値(小さい順)
float calGram[MAX_POINTS];   // 校正点の重さ(g)(軽い順)
int   numPoints = 0;

char inputBuf[BUF_SIZE];     // シリアル入力を1行ぶんためる
int  inputLen = 0;
bool inputOverflow = false;

unsigned long lastPrintTime = 0;

// 50回読み取って平均を返す(約0.5秒)
int readAverage() {
  long sum = 0;
  for (int i = 0; i < 50; i++) {
    sum += analogRead(FSR_PIN);
    delay(10);
  }
  return sum / 50;
}

// 登録済みの校正点を表示する
void printPoints() {
  Serial.print("Calibration points: ");
  Serial.println(numPoints);
  for (int i = 0; i < numPoints; i++) {
    Serial.print("  ");
    Serial.print(i + 1);
    Serial.print(": raw=");
    Serial.print(calRaw[i]);
    Serial.print("  gram=");
    Serial.println(calGram[i], 1);
  }
}

// 「数字と小数点1つまで」だけでできた文字列かを調べる
bool isValidNumber(const char *s) {
  int digits = 0;
  int dots = 0;
  for (int i = 0; s[i] != '\0'; i++) {
    if (s[i] >= '0' && s[i] <= '9') {
      digits++;
    } else if (s[i] == '.') {
      dots++;
    } else {
      return false;
    }
  }
  return digits > 0 && dots <= 1;
}

// 校正点を追加する(軽い順・rawが増える順でなければ登録しない)
void addPoint(float gram) {
  if (numPoints >= MAX_POINTS) {
    Serial.println("Rejected: table is full. Send 'clear' to start over.");
    return;
  }
  if (numPoints == 0 && gram != 0) {
    Serial.println("Rejected: first point must be 0 (no load).");
    return;
  }
  if (numPoints > 0 && gram <= calGram[numPoints - 1]) {
    Serial.println("Rejected: weight must be heavier than the previous point.");
    return;
  }

  int raw = readAverage();

  if (numPoints > 0 && raw <= calRaw[numPoints - 1]) {
    Serial.print("Rejected: raw=");
    Serial.print(raw);
    Serial.println(" did not increase. Check weight position or sensor range.");
    return;
  }

  calRaw[numPoints]  = raw;
  calGram[numPoints] = gram;
  numPoints++;

  Serial.print("Added: raw=");
  Serial.print(raw);
  Serial.print("  gram=");
  Serial.println(gram, 1);
}

// 受け取った1行を処理する
void handleLine(char *line) {
  if (strcmp(line, "list") == 0) {
    printPoints();
  } else if (strcmp(line, "clear") == 0) {
    numPoints = 0;
    Serial.println("Cleared all calibration points.");
  } else if (isValidNumber(line)) {
    addPoint(atof(line));
  } else {
    Serial.print("Ignored (not a number): ");
    Serial.println(line);
  }
}

// シリアル入力を改行までためて、1行そろったら処理する
void readSerialInput() {
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n' || c == '\r') {
      if (inputOverflow) {
        Serial.println("Ignored: input too long.");
      } else if (inputLen > 0) {
        inputBuf[inputLen] = '\0';
        handleLine(inputBuf);
      }
      inputLen = 0;
      inputOverflow = false;
    } else if (inputLen < BUF_SIZE - 1) {
      inputBuf[inputLen] = c;
      inputLen++;
    } else {
      inputOverflow = true;
    }
  }
}

// 校正点の間を直線でつないで重量相当値を推定する
float rawToGram(int raw) {
  if (raw <= calRaw[0]) return calGram[0];
  if (raw >= calRaw[numPoints - 1]) return calGram[numPoints - 1];

  for (int i = 1; i < numPoints; i++) {
    if (raw <= calRaw[i]) {
      int span = calRaw[i] - calRaw[i - 1];
      if (span <= 0) return calGram[i];   // 念のための0除算防止
      float t = (float)(raw - calRaw[i - 1]) / (float)span;
      return calGram[i - 1] + t * (calGram[i] - calGram[i - 1]);
    }
  }
  return calGram[numPoints - 1];
}

void setup() {
  Serial.begin(9600);
  Serial.println("Send 0 with no load, then known weights in grams (light to heavy).");
  Serial.println("Commands: list / clear");
}

void loop() {
  readSerialInput();

  // 0.5秒ごとに現在の値を表示
  if (millis() - lastPrintTime >= 500) {
    lastPrintTime = millis();
    int raw = analogRead(FSR_PIN);
    Serial.print("raw: ");
    Serial.print(raw);

    if (numPoints >= 2) {
      Serial.print("  estimate: ");
      Serial.print(rawToGram(raw), 0);
      Serial.println(" g (equivalent)");
    } else {
      Serial.println("  (need 2 or more calibration points)");
    }
  }
}

校正するときの注意

  • おもりを載せたら、値が落ち着くまで数秒待ってから送信する(クリープの影響を揃えるため、待ち時間を毎回同じにする)
  • おもりは軽い順に載せ、毎回同じ位置・同じ当て板を使う
  • 校正はセンサーごと、取り付け方ごとにやり直す
  • シリアルモニタの改行設定を「LF(改行)」にし、数字だけを送る(数字以外の入力は登録されません)
  • 最初に0g(何も載せない状態)を登録し、そのあと軽い順に登録する
  • 「Rejected」と表示されたら、その点は登録されていません。前の点よりrawが増えていない場合は、おもりの位置を直すか、センサーの測定範囲を超えていないか確認する
  • list で登録済みの校正点を確認でき、clear で最初からやり直せる
  • 測定したい範囲をカバーするように校正点を選ぶ

この方法で得られるのは、校正したときと同じ条件でのおおよその重量相当値です。押し方や接触面が変われば値は変わります。数値の正確さが必要な用途では、FSRではなくロードセルを使ってください。

FSR・ロードセル・FlexiForceの違い

比較項目 FSR(FSR402など) FlexiForce(A201など) ロードセル+HX711
動作原理 押すと抵抗が下がる感圧フィルム 押すと抵抗が下がる感圧フィルム 金属のわずかな変形をひずみゲージで検出
測定対象 押す力の相対的な強弱 押す力(A201は約4.4N・111N・445Nの3レンジ) 荷重・重量
定量性 低い(非線形・個体差あり) FSRより高い(A201の公称値は直線性誤差±3%未満、ヒステリシス4.5%未満。オペアンプ回路使用時の値で、校正は必要) 高い(出力がほぼ直線的)
配線の難易度 やさしい やさしい〜普通 普通(4本線+アンプ)
必要な周辺回路 抵抗1本 抵抗1本でも動作。メーカーはオペアンプ回路を推奨 HX711などの専用アンプとライブラリ
適した用途 タッチ検出、スイッチ、強弱の判定 薄さが必要な力の計測 はかり、荷重の定量測定
主な弱点 正確な数値化が難しい FSRより高価 厚みがあり、取り付けに機構設計が必要

仕様の詳細は、Interlink Electronics(FSR 402)、Tekscan(FlexiForce A201)、SparkFun(HX711)の各公式ページで確認できます。FSRの扱い方全般はSparkFunのFSR接続ガイドも参考になります。

選び方の目安:「押した・押していない」「強い・弱い」がわかればよいならFSR、薄さを保ったまま数値の信頼性を上げたいならFlexiForce、重さを正確に測りたいならロードセルです。ロードセルの配線と校正はArduinoでロードセルを使う方法にまとめています。

Arduino圧力センサーの応用例

圧センサーと圧電ブザーで圧力変化を音で表す作品

作例 難易度 追加部品 仕組み
1. 圧力でLEDを点灯 ★☆☆ LED、220Ω抵抗 しきい値を超えたらLEDをON(本記事で解説)。analogWrite()を使えば押す強さで明るさも変えられる
2. 圧力でブザーを鳴らす ★☆☆ 圧電ブザー しきい値を超えたらtone()で音を出す。map()で押す強さを音の高さに対応させることもできる
3. 押す力でゲームを操作 ★★☆ PC(Python) センサー値をシリアル通信でPCに送り、Python側でキャラクターを動かす
4. 握る力のフィードバック装置 ★★☆ 柔らかいボールやグリップ、LEDまたはディスプレイ グリップにFSRを固定し、握る強さを光・音・画面で本人に返す
5. 足裏の接触・荷重変化の検知 ★★★ FSR複数枚、中敷き かかと・つま先などにFSRを配置し、接地のタイミングや荷重の移り変わりを検出する


→画像タップで実際の花火を上げる簡易的な作品の動画を提示

それぞれ、詳しい作り方を別記事で解説しています。


リハビリ機器製作への応用

圧力センサーをリハビリ機器に応用するイメージ。握る力や足の荷重をセンサーで検出しフィードバックする

YOFUKASI LABの運営者は理学療法士で、ArduinoとPythonを使った医療関連機器の製作と学会発表に取り組んできました。その立場から、圧力センサーをリハビリ分野の試作に使うときの考え方をまとめます。全体像はArduino×Python×AIで作るリハビリセンサー自作ガイドでも紹介しています。

FSRが向いているのは「フィードバック」

リハビリでは「もう少し強く」「力を抜いて」といった力加減を言葉で伝える場面が多くあります。FSRを使うと、その力加減を光・音・グラフとして本人にその場で返せます。必要なのは「さっきより強いか弱いか」「目標の範囲に入っているか」という相対的な情報であり、これはFSRが得意とする使い方です。

試作のアイデア

  • 指先の押圧フィードバック:つまみ動作や指で押す練習で、目標の強さに達したらLEDや音で知らせる。LED連動のコードがそのまま土台になります。
  • 握る力の段階的な練習:しきい値を「弱・中・強」の複数段階にし、LEDの数や音の高さで段階を示す。力を出す練習だけでなく、弱い力で保つ調整の練習にも使えます。
  • 体重支持・荷重練習の試作:足裏にFSRを置き、荷重が増えたこと・左右の偏りの傾向を知らせる。ただし「体重の何%をかけたか」という定量値が必要なら、ロードセルを使った構成が適しています。

Pythonで記録・グラフ化する

練習中のデータをPCに保存しておくと、あとから変化を振り返れます。次のPythonコードは、グラフ表示用のスケッチ(raw:数値,avg:数値の形式で出力)を書き込んだArduinoから10秒間データを受け取り、CSV保存とグラフ表示を行います。実行前にArduino IDEのシリアルモニタ・プロッタは閉じてください。

Pythonコードの実行手順

  1. Arduinoにグラフ表示用のスケッチを書き込み、シリアルモニタとシリアルプロッタを閉じる
  2. ターミナル(Windowsはコマンドプロンプト)で pip install pyserial matplotlib を実行する(macOSで動かない場合は pip3)
  3. ポート名を調べる。Arduino IDEの「ツール」→「ポート」に表示される名前がそのまま使えます(Windowsは COM3 など、macOSは /dev/cu.usbmodem で始まる名前)
  4. 下のコードを fsr_log.py という名前で保存し、PORT を調べたポート名に書き換える
  5. 保存したフォルダで python fsr_log.py を実行し、10秒間FSRを押したり離したりする

CSVは実行したフォルダに fsr_log.csv として保存され、保存先のパスが画面に表示されます。データを1件も受信できなかった場合や、ポートを開けなかった場合は、原因のヒントを表示して終了します。

# FSRの値を一定時間記録し、CSV保存とグラフ表示を行う
import csv
import os
import sys
import time

import serial
import matplotlib.pyplot as plt

PORT = "COM3"            # 自分の環境のポート名に変更する
BAUD = 9600              # Arduino側の Serial.begin() と同じ値
DURATION = 10            # 記録する秒数
CSV_NAME = "fsr_log.csv"

try:
    ser = serial.Serial(PORT, BAUD, timeout=1)
except serial.SerialException as e:
    print("ポートを開けませんでした。ポート名と、シリアルモニタを閉じているかを確認してください。")
    print(e)
    sys.exit(1)

time.sleep(2)            # 接続時にArduinoが再起動するのを待つ
ser.reset_input_buffer()

times, values = [], []
start = time.time()
try:
    while time.time() - start < DURATION:
        line = ser.readline().decode(errors="ignore").strip()
        if not line:
            continue
        try:
            data = dict(item.split(":") for item in line.split(","))
            value = int(data["avg"])
        except (ValueError, KeyError):
            continue     # 途中で切れた行や形式の違う行は読み飛ばす
        times.append(time.time() - start)
        values.append(value)
except serial.SerialException as e:
    print("通信が途中で切れました。ここまでのデータを保存します。")
    print(e)
finally:
    ser.close()

if not values:
    print("データを1件も受信できませんでした。")
    print("グラフ表示用のスケッチを書き込んでいるか、通信速度が合っているかを確認してください。")
    sys.exit(1)

with open(CSV_NAME, "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["time_s", "avg"])
    writer.writerows(zip(times, values))

print(f"{len(values)}件を保存しました: {os.path.abspath(CSV_NAME)}")

plt.plot(times, values)
plt.xlabel("time [s]")
plt.ylabel("analogRead (moving average)")
plt.show()

CSVデータをPythonで可視化する流れは、PythonによるDelsysの筋電図のCSVデータを可視化でも紹介しています。筋活動も合わせて扱いたい場合はArduino×筋電センサー完全ガイドをご覧ください。

FSRの相対値と臨床評価値は別物

FSRの出力は、同じ人が同じ装置を同じ条件で使ったときの「強弱の目安」です。握力計の握力(kgf)や、体重計・床反力計の荷重値のような臨床評価値の代わりにはなりません。理由は換算の章で述べたとおり、非線形性・ヒステリシス・クリープ・当たり方の影響を受けるためです。

  • 「前回より数値が上がった」という比較も、センサーの位置・固定方法・個体が同じでなければ成り立ちません
  • 評価として数値を残したい場合は、検証された測定機器を使います
  • 歩行・バランスの評価を手軽に行う方法としては、スマホだけでTUGは自動計測できるかを検証した記事も参考になります

安全性・妥当性・法規制

  • 安全性:身体に触れる部分は配線の露出や尖った部品をなくし、センサーや配線の上に体重をかける場合は転倒・つまずきの原因にならない固定方法にします。電源はUSBや電池などの低電圧に限り、濡れる環境では使いません。
  • 測定の妥当性:自作装置の数値を判断材料にする場合は、既存の測定機器と比べてどの程度一致するか、同じ条件で繰り返して同じ値になるかを先に確かめます。
  • 法規制:疾病の診断・治療などを目的とする機器は、日本では医薬品医療機器等法(薬機法)上の医療機器に該当する可能性があり、製造・販売には承認や認証などの手続きが必要です。自作機器は、学習・研究・試作の範囲で扱い、認証を受けた機器と同等のものとして使用しないでください。対象者に使う場合は、所属施設の規定や倫理審査の要否も確認してください。

よくある質問

Q. Arduinoの圧力センサーは何が必要ですか?
Arduino Uno、FSR(FSR402など)、10kΩ抵抗1本、ブレッドボード、ジャンパーワイヤ、USBケーブルです。ライブラリのインストールは不要です。
Q. FSRは抵抗なしで使えますか?
外付けの抵抗なしでは安定して読めません。押していないときにA0が浮いて、値が不規則に変動します。10kΩ抵抗をA0とGNDの間に入れてください。
Q. FSRとロードセルの違いは何ですか?
FSRは薄くて配線が簡単ですが、力の強弱を相対的に見る部品です。ロードセルは金属の変形を利用して荷重を高い精度で測れますが、HX711などのアンプが必要です。
Q. 圧力センサーの数値をkgに換算できますか?
既知のおもりで複数点の校正を行えば、おおよその重量相当値は出せます。ただしFSRは非線形で、時間や押し方によって値が変わるため、正確なkg表示が必要ならロードセルを使ってください。
Q. Arduino Uno以外のボードでも使えますか?
アナログ入力のあるボードなら使えます。ESP32などの3.3V系ボードではFSRを3.3Vにつなぎ、分解能(例:12bitなら0〜4095)に合わせてコード内の「5.0」や「1023」を変更します。
Q. FSRの寿命はどのくらいですか?
FSR 400シリーズはメーカーのデータシートで、1kg・4Hzの条件で1,000万回の作動試験を行ったとされています。実際の寿命は使用条件によって変わり、折り曲げ・過大な荷重・はんだ付けの熱などで短くなります。
Q. 圧力を長時間測定し続けられますか?
動作はしますが、同じ力をかけ続けても出力が徐々に変化します(クリープ)。長時間の一定荷重を正確に測る用途には向きません。変化の検出や短時間の計測に使うのが適切です。
Q. 圧センサーで足圧を測定できますか?
かかとやつま先が接地したタイミング、荷重が移る傾向の検出はできます。足裏全体の圧力分布や正確な荷重値の測定には、専用の計測機器が必要です。作り方はインソール型センサーの記事で紹介しています。

まとめ

  • 最初にやること:FSR402と10kΩ抵抗を「5V → FSR → A0 → 10kΩ → GND」でつなぎ、基本コードで「押すと数値が増える」ことを確認する
  • 配線とコードの要点:抵抗は必須。A0にはFSRと抵抗の接続点をつなぐ。シリアルモニタの速度は9600 baudに合わせる
  • うまくいかないとき:A0を5V・GNDに直接つないで1023・0が出るか確認し、問題の場所を切り分ける
  • 数値化の注意:FSRの値は相対的な強弱の目安。重量相当値にするには複数点の校正が必要で、正確な測定にはロードセルを使う

基本の読み取りができたら、次はLED連動、そして握力計測ゲームやリアルタイムフィードバックシステム、インソール型センサーへと進むと、Arduinoの圧力センサーでできることが一気に広がります。

関連記事

特集記事

コメント

この記事へのコメントはありません。

TOP
CLOSE