개 물 마시는 감지 IoT를 라즈파이로 만들어 보았습니다.

우리 집에는 12세가 되는 장난감 푸들이 있습니다.
고령견으로 분류되는 연령이므로, 주인 부재시에 변함없이 보내고 있는지를 가속도 센서로 검지할 수 있는 방법을 생각해 보았습니다.

기법



물 마시기용 페트병에 가속도 센서를 붙이고, 물을 마셨을 때의 진동을 검지하면 주인에게 알리는 방법으로 실현했습니다.
알림은 Webhook을 통해 트위트합니다.



I2C 활성화



센서는 6축 모션 센서(가속도 3축+자이로 3축)로서 MPU-6050을 사용한 GY-521을 사용합니다.
Raspberry Pi와는 I2C라는 통신 방식으로 연결하므로 Raspberry Pi의 메뉴 아이콘에서

Raspberry Pi 설정> 인터페이스 설정

에서 I2C를 활성화합니다.


GY-521을 라즈파이에 연결



그런 다음 GY-521을 라즈파이의 GPIO 핀에 연결합니다.


GY-521
Raspberry Pi


VCC
3.3V

GND
GND

SCL
GPIO2

SDA
GPIO3


연결 후 LXTerminal에서 GY-521이 인식되는지 확인합니다.
MPU-6050의 주소는 "0x68"이므로 정상적으로 인식됩니다.
pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --  

프로그램



베이스의 프로그램 코드는 여기 를 참조해 , 개의 물 마시는 검지용 코드를 추가하고 있습니다.
또, 개의 물 마시는 검지로 사용하는 것은 가속도 센서 뿐이므로, 자이로 센서의 값・기온을 취득하는 코드는 코멘트 아웃하고 있습니다.

mpu-6050_read.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import requests
import smbus            # use I2C
import math
from time import sleep  # time module
import os
import datetime

### define #############################################################
DEV_ADDR = 0x68         # device address
PWR_MGMT_1 = 0x6b       # Power Management 1
ACCEL_XOUT = 0x3b       # Axel X-axis
ACCEL_YOUT = 0x3d       # Axel Y-axis
ACCEL_ZOUT = 0x3f       # Axel Z-axis
TEMP_OUT = 0x41         # Temperature
GYRO_XOUT = 0x43        # Gyro X-axis
GYRO_YOUT = 0x45        # Gyro Y-axis
GYRO_ZOUT = 0x47        # Gyro Z-axis

# 1byte read
def read_byte( addr ):
    return bus.read_byte_data( DEV_ADDR, addr )

# 2byte read
def read_word( addr ):
    high = read_byte( addr   )
    low  = read_byte( addr+1 )
    return (high << 8) + low

# Sensor data read
def read_word_sensor( addr ):
    val = read_word( addr )
    if( val < 0x8000 ):
        return val # positive value
    else:
        return val - 65536 # negative value

# Get Temperature
def get_temp():
    temp = read_word_sensor( TEMP_OUT )
    # offset = -521 @ 35℃
    return ( temp + 521 ) / 340.0 + 35.0

# Get Gyro data (raw value)
def get_gyro_data_lsb():
    x = read_word_sensor( GYRO_XOUT )
    y = read_word_sensor( GYRO_YOUT )
    z = read_word_sensor( GYRO_ZOUT )
    return [ x, y, z ]
# Get Gyro data (deg/s)
def get_gyro_data_deg():
    x,y,z = get_gyro_data_lsb()
    # Sensitivity = 131 LSB/(deg/s), @cf datasheet
    x = x / 131.0
    y = y / 131.0
    z = z / 131.0
    return [ x, y, z ]

# Get Axel data (raw value)
def get_accel_data_lsb():
    x = read_word_sensor( ACCEL_XOUT )
    y = read_word_sensor( ACCEL_YOUT )
    z = read_word_sensor( ACCEL_ZOUT )
    return [ x, y, z ]
# Get Axel data (G)
def get_accel_data_g():
    x,y,z = get_accel_data_lsb()
    # Sensitivity = 16384 LSB/G, @cf datasheet
    x = x / 16384.0
    y = y / 16384.0
    z = z / 16384.0
    return [x, y, z]

def ifttt_webhook():
    payload = {"value1": time,
               "value2": sabun_z}
    url = "https://maker.ifttt.com/trigger/{event}/with/key/{your key}"
    response = requests.post(url, data=payload)

### Main function ######################################################
bus = smbus.SMBus( 1 )
bus.write_byte_data( DEV_ADDR, PWR_MGMT_1, 0 )

###取得したデータを保存するためにcsvファイルを準備する。
fileName = '/home/pi/raspberrypi//mpu_data.csv' 

if not os.path.exists(fileName):
    mpuData = open(fileName,'w')
    mpuData.write('time,accel_x,accel_y,accel_z\n')
    mpuData.close()

###初回計測時の比較用データの取得
accel_x,accel_y,accel_z = get_accel_data_g()
old_accel_z = accel_z

###ここから計測開始
while True:
    ###時刻を取得する
    date = datetime.datetime.now()
    time = date.strftime("%m/%d %H:%M:%S")

    ###気温を取得する(利用しないためコメントアウト)
    #temp = get_temp()

    ###ジャイロセンサーの値を取得する (利用しないためコメントアウト)
    #gyro_x,gyro_y,gyro_z = get_gyro_data_deg()

    ###加速度センサーの値を取得する
    accel_x,accel_y,accel_z = get_accel_data_g()

    ###加速度センサーの値をCSVファイルに保存する
    data = [time,accel_x,accel_y,accel_z]
    mpuData = open(fileName,'a')
    mpuData.write(',' .join(map(str,data)) + '\n')
    mpuData.close()

    ###前回取得した値との比較
    sabun_z = old_accel_z - accel_z

    ###差分が閾値を超えたらwebhookに投げて60秒待つ(水飲後、しばらく振動が続くため)
    if abs(sabun_z) > 0.10:
        ifttt_webhook()
        sleep( 60 )

    ###今回取得した値を前回の値に格納する  
    old_accel_z = accel_z

    #次の計測まで10秒間sleep。その後、    
    sleep( 5 )

취득 데이터로부터의 분석 → 설정



진동 축





취득한 데이터를 보면, z축의 흔들림 폭이 크다.
이것은 센서를 페트병의 측면에 붙이고 있기 때문에, 전후의 진동이 크기 때문이라고 추측됩니다.
x축의 진동이 적은 것은, 페트병이 고정되어 있기 때문에, 상하의 진동이 적기 때문이라고 생각됩니다.


그래서, 우선은 z축만을 대상으로 하기로 했습니다.

진동 폭, 시간



개의 물 마시기를 보고 있으면, 1회의 물 마시고 몇 초간 계속 마시고 있고, 그 후, 잠시 페트병도 흔들리고 있기 때문에, 5초에 1회 계측하기로 했습니다.
진동의 폭은, 계측 결과로부터, 전회의 계측 결과로부터 0.1보다 큰 차분이 있었을 경우에 tweet하도록(듯이) 했습니다.

연속 tweet 방지 방법



물을 마시기 시작하면 5초마다 연속으로 tweet해 버릴 가능성이 있기 때문에, 역치 이상의 흔들림을 검지하면 60초 SLEEP해 연속 tweet를 방지하기로 했습니다.

요약



우선, 상기에서 시험 운용을 개시해, 가족 부재시도 순조롭게 개의 사활 감시가 되어 있습니다.
역치의 설정등은 개선의 여지가 있습니다만, 계측 데이터를 보면서, 맛을 수정해 가려고 생각합니다.

좋은 웹페이지 즐겨찾기