⚖️

ラズパイを使って、重さをはかるよ

11 min read

💡やること

ロードセル計量センサを使って、デジタルはかりをつくります。
液晶OLEDと連動して、重さを表示します。

🏁デモ

242_420

🔧パーツ一覧

no 部品名 個数 備考
1 ラズベリーパイ 1 今回は4Bで確認
2 電子スケール ロードセル計量センサセット(重量センサ+HX711) 1 Amazon
3 128x64ドット有機ELディスプレイ 1 秋月電子
4 ジャンパー線 適量 -
5 ブレッドボード 1 -
6 分銅 1 代用可能

分銅は、センサーのキャリブレーション用に使用します。
重さが分かるものであれば、なんでも大丈夫です。

接続図

ピンの接続

Pin HX711 ディスプレイ 備考
5V VCC - 赤色ケーブル
GND GND GND 黒色ケーブル
BCM5 DT - 黄色ケーブル
BCM6 SCK - 緑色ケーブル
3.3V - VDD 橙色ケーブル
BCM2(SDA.1) - SDA 紫色ケーブル
BCM3(SCL.1) - SCL 青色ケーブル

💻環境

開発環境

  • ラズベリーパイ
    • Linux rpi 5.10.17-v7l+ #1403 SMP Mon Feb 22 11:33:35 GMT 2021 armv7l GNU/Linux
  • Python
    • Python 3.7.3 (default, Jan 22 2021, 20:04:44)

ラズベリーパイの設定

表示デバイスは、I2C通信を使って制御します。
I2C通信を使用できるように設定する必要があります。
I2Cの有効化は、以下のコマンドから実施できます。

$ sudo raspi-config
  • Interface Optionsを選択
  • I2Cを選択
  • "はい"(or "Yes")を選択
  • これで有効化されます(1回行えばOKです)

モジュールのインストール

OSのクリーンインストールした前提で記載しています。

apt

日本語を表示するためにフォントをインストールします。

$ sudo apt-get install fonts-ipafont

pip

Pythonに関するモジュールをインストールします。

$ python3 -m venv env
$ source env/bin/activate
# 表示機用
(env) $ pip install adafruit-circuitpython-ssd1306
(env) $ pip install smbus2
(env) $ pip install pillow
# 重さ計測用
(env) $ pip install RPi.GPIO
(env) $ git clone https://github.com/tatobari/hx711py

📝手順

  • キャリブレーションの実施
  • ディスプレイと連動させる

キャリブレーションの実施

センサーのばらつきや筐体の差などがあるため、キャリブレーション(校正)を行う必要があります。
tatobariさんが公開されているモジュールのサンプルに従って、キャリブレーションを実施します。

以下の手順で行います。

  • 何も載せていない状態で、プログラムを実行
  • 分銅(重さが分かっているもの)を置いて、データを取得する
  • データからキャリブレーション値(referenceUnit)を算出

何も載せていない状態で、プログラムを実行

以下がキャリブレーション用のコードとなります。

samp_calibration.py
import time
import sys
import RPi.GPIO as GPIO
from hx711py.hx711 import HX711

PIN_DAT = 5
PIN_CLK = 6

referenceUnit = 1 # <=これを決めたい

def cleanAndExit():
    print("Cleaning...")
    GPIO.cleanup()
    print("Bye!")
    sys.exit()

def main():
    hx = HX711(PIN_DAT, PIN_CLK)

    # データの並び順を指定
    hx.set_reading_format("MSB", "MSB")

    # キャリブレーション値を設定
    hx.set_reference_unit(referenceUnit)

    hx.reset()

    hx.tare()

    print("Tare done! Add weight now...")

    while True:
        try:
            # Prints the weight.
            val = hx.get_weight(5)
            print(val)

            hx.power_down()
            hx.power_up()
            time.sleep(0.1)

        except (KeyboardInterrupt, SystemExit):
            panel.display_clear()
            cleanAndExit()


if __name__ == "__main__":
    main()

プログラムを実行します。

(env) $ python samp_calibration.py 
Tare done! Add weight now...
18.333333333328483
-3.6666666666715173
16.333333333328483
...

何も載っていない場合は、小さな値が行ったり来たりするようです。

分銅(重さが分かっているもの)を置いて、データを取得

今回、手持ちの分銅(200g)を使用しました。

分銅を載せると、プログラムの結果が以下のように変わります。

(env) $ python samp_calibration.py 
Tare done! Add weight now...
18.333333333328483
-3.6666666666715173
16.333333333328483
-7.444444444437977
61824.55555555556   # <=分銅を載せた
92595.55555555556
94617.55555555556   # <= データが安定している
94591.55555555556   # <= データが安定している
94584.55555555556   # <= データが安定している
94592.55555555556   # <= データが安定している
94591.55555555556   # <= データが安定している
94613.55555555556   # <= データが安定している
94639.55555555556   # <= データが安定している
94630.55555555556   # <= データが安定している94621.55555555556   # <= データが安定している
94621.55555555556   # <= データが安定している

データからキャリブレーション値(referenceUnit)を算出

"データが安定している"から平均値を算出し、リファレンスの重さ(g)で割ります。

[キャリブレーション値] = [データの平均値] / [リファレンスの重さ(g)]

今回の例だと以下となります。

cal_ref.py
# 分銅の重さ
REF_OMOSA  = 200

# データの取得
SAMP_DATAS = [
    94617.55555555556,  94591.55555555556,  94584.55555555556,  94592.55555555556,
    94591.55555555556,  94613.55555555556,  94639.55555555556,  94630.55555555556,
    94621.55555555556,  94621.55555555556,  94615.55555555556,  94643.55555555556,
    94611.55555555556,  94614.55555555556,  94628.55555555556,  94596.55555555556,
    94626.55555555556,  94600.55555555556,  94589.55555555556,  94591.55555555556,
    94621.55555555556,  94618.55555555556,  94596.55555555556,  94622.55555555556,
    94621.55555555556,  94548.55555555556,  94601.55555555556,  94595.55555555556,
    94627.55555555556,
]

print("referenceUnit is ", sum(SAMP_DATAS) / len(SAMP_DATAS) //REF_OMOSA)

実行すると、

(env) $ python cal_ref.py 
referenceUnit is  473.0

473であることが分かりました。

これでキャリブレーションが完了です。

ディスプレイと連動させる

ディスプレイの詳細は、以前書いた↓を参照してください。

https://zenn.dev/kotaproj/articles/6f08ea43cd4dda8e0d2f

hakari.pyとoled.pyを組み合わせて動作を確認します。

コード - 計測と表示

referenceUnit = 473 を指定しています。

hakari.py
import time
import sys
import RPi.GPIO as GPIO
from hx711py.hx711 import HX711
from oled import Oled

PIN_DAT = 5
PIN_CLK = 6

# referenceUnit = 1
referenceUnit = 473 #<=算出した値 

def cleanAndExit():
    print("Cleaning...")
    GPIO.cleanup()
    print("Bye!")
    sys.exit()

def main():

    # 表示デバイスの作成
    panel = Oled()

    hx = HX711(PIN_DAT, PIN_CLK)

    # データの並び順を指定
    hx.set_reading_format("MSB", "MSB")
    hx.set_reference_unit(referenceUnit)
    hx.reset()
    hx.tare()

    print("Tare done! Add weight now...")

    while True:
        try:
            # Prints the weight.
            val = hx.get_weight(5)
            print(val)
            # データの表示
            panel.display_weights(val)

            hx.power_down()
            hx.power_up()
            time.sleep(0.1)

        except (KeyboardInterrupt, SystemExit):
            panel.display_clear()
            cleanAndExit()


if __name__ == "__main__":
    main()

表示のコードは以下となります。

oled.py
import board
import digitalio
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
from time import sleep
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

# SSD1306のピン設定
DEVICE_ADR = 0x3C
DISP_WIDTH = 128
DISP_HEIGHT = 64


class Oled():
    def __init__(self, dev_adr=DEVICE_ADR, width=DISP_WIDTH, height=DISP_HEIGHT):
        # Settings
        RESET_PIN = digitalio.DigitalInOut(board.D4)
        i2c = board.I2C()
        self._oled = adafruit_ssd1306.SSD1306_I2C(width, height, i2c, addr=dev_adr, reset=RESET_PIN)

        # Clear display.
        self._oled.fill(0)
        self._oled.show()

        # load fonts
        self._fonts = {}
        self._fonts["28"] = ImageFont.truetype("/usr/share/fonts/truetype/fonts-japanese-gothic.ttf", 28)
        self._fonts["14"] = ImageFont.truetype("/usr/share/fonts/truetype/fonts-japanese-gothic.ttf", 14)
        return

    def display_weights(self, gram):
        # Draw the text
        if gram < 1.0:
            moji = "何も載ってないよ!"
            fsize = "14"
        else:
            moji = str(Decimal(str(gram)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
            moji += "gです"
            fsize = "28"
        
        # Create blank image for drawing.
        image = Image.new("1", (self._oled.width, self._oled.height))
        draw = ImageDraw.Draw(image)
        
        # Write moji
        draw.text((0, 0), moji, font=self._fonts[fsize], fill=255)

        # Display image
        self._oled.image(image)
        self._oled.show()

    def display_clear(self):
        # Clear display.
        self._oled.fill(0)
        self._oled.show()
        return

実行手順

(env) $ python hakari.py 

デモのようにモノを置くと、重さが表示されます。

🔎ポイント

ロードセルモジュールの動作

ロードセルとは、力(質量、トルク)を検出するセンサーです。
今回は、ビーム型の5kgのモノを使っています。

を使用することもできます。

精度メモ

今回のリファレンスで確認したところ以下のような結果でした。

分銅の重さ 取得データ
10g 10.01~10.03
20g 19.97~20.05
50g 49.94~50.04
100g 99.92~100.07
200g 199.99~200.06
500g 499.98~500.12

かなりの精度で取れています。
筐体がしっかりしていれば、様々なアプリケーションに応用できそうです。

さいごに

ラズパイの活用方法を

https://zenn.dev/kotaproj/books/raspberrypi-tips

としてまとめ中です。

GitHubで編集を提案

Discussion

ログインするとコメントできます