⚖️

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

2021/06/06に公開4

💡やること

ロードセル計量センサを使って、デジタルはかりをつくります。
液晶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です)

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

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を組み合わせて動作を確認します。

コード - 計測と表示

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

しんんすけしんんすけ

大学生です。大学の講義でラズパイを使った作品を作っています。
kotaprojさんのラズパイを使って重さをはかるよという記事を参考にしてやっているのですが、「何も載せていない状態でプログラムを実行」というところのやり方が分かりません。詳しく教えてもらえたら助かります

kotaprojkotaproj

何も載せていない状態とは、分銅などの重さのあるものを載せなていない状態という意味です。
この状態で、プログラムを実行し、出力される値を確認します。
極端に大きな値を行き来したり、値が一定の場合は、接続や筐体を疑うことにつながります。

じんじん

こんにちは!
僕も大学でラズパイを使った作品を作る講義があり参考にさせてもらっているのですが、最後のモジュールのダウンロード際にメールアドレスとパスワード等を聞かれるのですが元のウェブサイトでアカウントを作成しなければならないのでしょうか?

kotaprojkotaproj

gitコマンドのところでしょうか
githubの本レポジトリページより、"Code"からダウンロードできると思います。