Arduinoとロードセル(HX711)で電子はかりを作る!Pythonで校正係数を計算して誤差を確かめる方法
「キッチンスケールみたいに重さを量るものを、Arduinoで作れないかな?」「ロードセルを買ってみたけど、表示される数字が大きすぎて何グラムなのか分からない…」
そんなふうに思ったことはありませんか? 実はロードセルとHX711というモジュールを使えば、1g単位で量れる電子はかりを自作できます。ただし、いちばんつまずきやすいのが校正(キャリブレーション)です。
この記事では、Arduinoで生の値を記録し、Pythonで校正係数を計算して誤差まで確かめる流れを一緒に見ていきましょう! 対象は、Lチカやセンサーの読み取りを一度やったことがある電子工作の初〜中級者です😊
ロードセルとHX711の仕組み

ロードセルは、重さがかかるとほんの少しだけ曲がる金属のブロックです。表面に貼られた「ひずみゲージ」が、その曲がり具合を電気抵抗の変化として取り出します。
ただ、この変化はとても小さく、ArduinoのanalogRead()ではほとんど読み取れません。そこで間に入るのがHX711です。イメージとしては「小さな信号を大きくして、細かい数字に変えてくれる通訳係」です。
- ロードセル:重さ → ごくわずかな電圧の変化
- HX711:その変化を増幅して、24ビットの数値に変換
- Arduino:数値を受け取って、グラムに換算して表示
つまり、Arduinoに届くのは「グラム」ではなくただの大きな数字(生の値)です。これを「何グラムか」に直すための割り算の数が、今回求める校正係数です。
用意するものと配線
| 部品 | メモ |
|---|---|
| Arduino UNO など | 5Vで動くボード |
| ロードセル(棒状・1kg用など) | 量りたい重さに合った定格のもの |
| HX711モジュール | ロードセルとセットで売られていることが多い |
| 重さが分かるおもり | キッチンスケールで量った水入りペットボトルなどでもOK |
配線はこんな感じです👇
| HX711側 | つなぎ先 |
|---|---|
| VCC | Arduino 5V |
| GND | Arduino GND |
| DT(DOUT) | Arduino D2 |
| SCK | Arduino D3 |
| E+ / E- / A- / A+ | ロードセルの4本の線(よくある色は 赤=E+、黒=E-、白=A-、緑=A+) |
⚠️ ロードセルの線の色は製品によって違うことがあります。必ず購入したロードセルの説明を確認しましょう。また、ロードセルは片側を台に固定し、反対側に皿を付けて「浮かせる」ように取り付けないと、正しく曲がらず値が安定しません。
ライブラリは、Arduino IDEのライブラリマネージャで「HX711」と検索し、Bogdan Necula氏のHX711 Arduino Libraryをインストールします。
ステップ1:Arduinoで生の値を記録する
まずは校正用のスケッチです。何も載せない状態をゼロにして、おもりを載せたときの生の値をシリアルモニタに表示します。
#include "HX711.h"
const int DOUT_PIN = 2; // HX711 の DT
const int SCK_PIN = 3; // HX711 の SCK
HX711 scale;
void setup() {
Serial.begin(9600);
scale.begin(DOUT_PIN, SCK_PIN);
Serial.println("何も載せないでください。ゼロ点を取ります...");
delay(2000);
scale.tare(20); // 20回平均した値を「0」にする
Serial.println("おもりを載せて、表示された値を記録してください");
}
void loop() {
// HX711 が応答するまで最大1秒待つ
if (scale.wait_ready_timeout(1000)) {
long raw = scale.get_value(10); // 10回平均の生の値(ゼロ点を引いた値)
Serial.println(raw);
} else {
Serial.println("HX711が見つかりません。配線を確認してください");
}
delay(500);
}
ポイントをまとめるとこんな感じです👇
scale.tare(20):何も載せていないときの値を20回平均して「0」として覚えるscale.get_value(10):10回平均した値から、そのゼロ点を引いた生の値を返すwait_ready_timeout(1000):HX711が応答しないときに固まらず、配線ミスに気づける
書き込んだら、シリアルモニタ(9600bps)を開いて、0g → 100g → 200g → 500gのように重さの分かるおもりを順に載せ、それぞれの値をメモします。値が少し揺れるので、落ち着いたところの数字を記録しましょう。
もし重さを増やすと値がマイナス方向に大きくなる場合は、A+とA-の線が逆です。線を入れ替えるか、このあと求める係数がマイナスになるのでそのまま使ってもかまいません。
ステップ2:Pythonで校正係数を計算する
よくある校正方法は「1つのおもりを載せて、生の値 ÷ 重さ」で係数を出すやり方です。でも1点だけだと、その測定がたまたまズレていても気づけませんよね。
そこで、記録した複数の点に直線を当てはめる(最小二乗法)ことにします。Python 3.10以降なら標準ライブラリのstatistics.linear_regressionだけで計算できるので、追加のインストールは不要です。
ここが重要です👇 下のコードのsamplesの数値は説明用の例です。ステップ1で自分が記録した値に置き換えて実行してください。
from statistics import linear_regression
# 校正で記録した値(重さ[g], Arduinoに表示された生の値)
# ※ 数値は説明用の例です。自分のロードセルで測った値に置き換えてください
samples = [
(0, 15),
(100, 42180),
(200, 84290),
(500, 210950),
]
weights = [w for w, _ in samples]
raws = [r for _, r in samples]
# 生の値 = 傾き × 重さ + 切片 の直線を最小二乗法で求める
slope, intercept = linear_regression(weights, raws)
print(f"校正係数(set_scaleに渡す値): {slope:.2f}")
print(f"切片(0gのときのズレ): {intercept:.1f}")
print()
print(" 重さ 生の値 換算 誤差")
for w, r in samples:
grams = (r - intercept) / slope # 生の値 → グラムに換算
print(f"{w:4d}g {r:7d} {grams:7.1f}g {grams - w:+5.1f}g")
# 誤差がいちばん大きい点をチェック
worst = max(abs((r - intercept) / slope - w) for w, r in samples)
print()
print(f"最大誤差: {worst:.1f}g")
if worst > 1.0:
print("⚠️ 直線から外れた点があります。おもりの置き方や配線を確認しましょう")
else:
print("OK: どの点も±1g以内です")

実行結果(上の画像)で見るべきところは2つです。
- 校正係数 421.89:この数でArduinoの生の値を割ると、グラムになります
- 各点の誤差:どの点も±0.2g以内なので、直線にきれいに乗っている=測り方に問題なし
もし1つだけ誤差が大きい点があったら、おもりが皿の端に載っていた、ケーブルが引っぱられていた、などが原因のことが多いです。その点だけ測り直しましょう。
ちなみに切片(-18.4)は、0gのときにほんの少しズレているという意味です。生の値でいうと約0.04g分なので、Arduino側ではtare()のゼロ合わせに任せて大丈夫です。
ステップ3:グラムで表示するはかりを完成させる
求めた係数をスケッチに書き込めば、いよいよ電子はかりの完成です。シリアルモニタからtを送ると、容器を載せたまま0に戻せる「風袋引き」も付けました。
#include "HX711.h"
const int DOUT_PIN = 2;
const int SCK_PIN = 3;
const float CALIBRATION_FACTOR = 421.89; // Pythonで求めた校正係数
HX711 scale;
void setup() {
Serial.begin(9600);
scale.begin(DOUT_PIN, SCK_PIN);
scale.set_scale(CALIBRATION_FACTOR); // 生の値 ÷ 係数 = グラム
delay(2000);
scale.tare(20);
Serial.println("準備OK(t を送るとゼロに戻します)");
}
void loop() {
// シリアルモニタから t が来たら風袋引き(ゼロ戻し)
if (Serial.available() > 0) {
char c = Serial.read();
if (c == 't') {
scale.tare(20);
Serial.println("ゼロに戻しました");
}
}
if (scale.wait_ready_timeout(1000)) {
float grams = scale.get_units(10); // 10回平均してグラムで返す
if (fabs(grams) < 0.5) {
grams = 0.0; // ±0.5g未満のふらつきは0と表示
}
Serial.print(grams, 1);
Serial.println(" g");
}
delay(300);
}
ポイントをまとめるとこんな感じです👇
set_scale(CALIBRATION_FACTOR):これ以降、get_units()がグラムで値を返すようになるtare(20):起動時とtを受け取ったときに、今の重さを0にするfabs(grams) < 0.5:何も載せていないときに「0.3g」「-0.2g」とふらつく表示を0にまとめる
シリアルモニタにはこんなふうに表示されます(表示例)。
準備OK(t を送るとゼロに戻します)
0.0 g
0.0 g
100.1 g
100.0 g
これ、実行せずに答えられますか? 皿にコップ(150g)を載せてtを送り、そのあと水を50g注いだら、何と表示されるでしょう? ……答えは約50gです。風袋引きでコップの重さが0になっているからですね😊
値が安定しないときのチェックポイント
- 値がずっと0のまま・エラー表示:DT/SCKのピン番号と、VCC・GNDの配線を確認
- 値がじわじわ変わり続ける:電源を入れた直後はロードセルが温まって値がずれることがあるので、数分おいてから
tareし直す - 重さによって誤差が違う:ステップ2の表で、どの点がズレているかを見て測り直す
- 表示がガタガタ揺れる:平均回数(
get_units(10)の10)を増やす。ただし多くのHX711モジュールは1秒に約10回しか測れないので、回数を増やすほど表示は遅くなります
まとめ
- ロードセルの小さな信号は、HX711で増幅して数値に変える
- Arduinoに届くのは生の値なので、校正係数で割ってグラムにする
- 複数のおもりで値を記録し、Pythonの
statistics.linear_regressionで直線を当てはめると、係数と各点の誤差がまとめて分かる - 完成したスケッチには風袋引きとふらつき対策を入れると、ぐっと使いやすくなる
「数字がよく分からない」状態から、「自分で校正して1g単位で量れる」状態まで、ざっくりとした流れがつかめるはずです。ぜひ手元のおもりで試してみてください🚀
こちらも読まれています
📡 Arduinoをもっと深く学ぼう!
Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。
Arduinoロボット入門のおすすめコース・部品を見る →📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。




