yana_lab

ラズベリーパイ関連

最終更新:

miyazaki

- view
だれでも歓迎! 編集

ラズベリーパイ関連


+ 0.はじめに

0.はじめに

ラズベリーパイは非常にいろいろなことができる教育用のシングルボードコンピュータです.生産工学部のRobo-BEでも2,3年生で使用します.

利点としては以下が挙げられます.
  • Pythonでプログラミングできる!
  • GPIOがついているので,モータを制御したり,センサの情報を入力したりできる.
  • Wifi,Bluetoothが簡単に使える.
  • マイクラで遊べる.
欠点として
  • 基本的にはディスプレイとキーボード,マウスを用意しておかないといけない.
  • ノートPCだけで操作するには,事前に設定が必要.
  • スマホやWindowsPCでネットサーフィンやゲームしかしない人には,最初は少し難しいかも?(特にターミナルにコマンドを入力する操作)
  • 意外と電力が必要.

PythonでGPIOを制御するためのライブラリとして「RPi.GPIO」と「GPIOZERO」があります.特に初めて勉強する人には「GPIOZERO」が簡単でおすすめです.

+ 1.SDカードにOSを入れる

1.SDカードにOSを入れる.

※Raspbianが最初からインストールしてあるSDカードならこのステップは不要.


② イメージファイルを書き込む(「 Win32DiskImager」をインストールして使う (http://dekirukana-dekirukana.hatenablog.com/entry/2015/12/08/000942).

+ 2.Robo-BE用 初期設定

2.初期設定(Robo-BE用)

PC側の準備

①授業で使用する自分のノートPCに以下の2つをインストールしておく.
(ラズベリーパイに「ホスト名.local」でアクセスするためにBonjourが必要なのでiTunesをインストールする.)

②Robo-BE部屋(24-211室)のネットワークに接続する.

ラズベリーパイ側の準備

初回①~⑤の操作を行うときは,キーボード,マウス,ディスプレイを接続すること.

①ターミナルに「sudo raspi-config」と入力.

②「2.Network Options」→「N1 Hostname」→「了解」でホスト名を変更する.

③最初に書いてある文字はすべて削除して,ホスト名を「robobeXXXXX」(Xの部分が学生番号)にして決定.

④指示に従い,再起動する.

⑤ネットワーク接続.ノートPCと同じく,Robo-BE部屋(24-211室)のネットワークに接続する。
※少し工夫しないとcit-airnet2には接続できないので注意.Robo-BEでは基本的にはcit-airnet2を使わない.

⑥ターミナルで「sudo date MMDDHHMMYYYY」で時間設定する.
(例2018年4月2日 11:34なら sudo date040211342018)
※⑥の操作は毎回行うこと.

以下,研究室用メモ(Robo-BEの授業ではやらないでOK)
まずは,インターネットに接続し,以下の操作①~④を行う.

① ターミナル(windowsのコマンドプロンプトみたいなやつ)で「sudo raspi-config」して、パスワード、キーボード、タイムゾーンなどを設定する

② 左上のラズベリーマークを押して、オプション?のraspi-configを選ぶ.
ここで必要に応じてSSH,VNC,SPI、I2CをEnableにしておく.その後再起動

③ ターミナルで「 apt-get update 」「 apt-get upgrade 」「 pi-update 」を入力して更新.
(時間超かかることあるから注意!)

④ IPアドレスをメモする.(Bonjourを使って「ホスト名.local」でアクセスしない場合のみ)
ネットワークに入れない環境なら「エレコムWRH-583BK2-S」とかを使う.
こいつの有線LANのコネクタとラズベリーパイを接続,電源用のUSBケーブルもラズベリーパイかモバイルバッテリーなどに接続. (アクセスポイント(AP)モードもできるけど.)

+ 3.ノートPCで遠隔操作する

3.ノートPCで遠隔操作する

①ノートPCのVNC Viewerでラズベリーパイの「ホスト名.local」(ホスト名は「robobe2-X」みたいなやつ)を入力.
ユーザ名,パスワードは授業で説明したものを入力.
ノートPCとラズベリーパイがどちらもRobo-BE部屋のネットワークに接続されていないと動きません.

②うまくいけばノートPCの画面にラズベリーパイの画面が表示される.
ホスト名でログインしているので,ノートPCとラズベリーパイを有線で直接接続する場合も①の操作をすればOK.
※基本的にはほとんどの操作をノートPCからできますが、マイクラはできません.

ノートPCの設定 以下のソフトをインストールしておくと便利.
(Robo-BEの授業では使いません.)
  • Tera Term: シリアル通信の確認やSSH(キャラクタインターフェースで遠隔操作する)で使用する.
  • File zilla:  PCとラズパイとのデータのやり取りができる.

+ PythonでGPIOを制御するにあたって

PythonでGPIOを制御するにあたって

注意:配線の間違いがないことを確認してからラズベリーパイとブレッドボードをつなぐこと.突然電源が落ちた場合は,すぐにラズベリーパイの電源を切り,ブレッドボードを外すこと.ショートすると壊れて2度と起動しなくなることもあります.



+ GPIOZERO

GPIOZEROの使い方

RPi.GPIOと比較して,gpiozeroは少ないコード数でいろいろなことができます.初めて勉強する人には,おすすめのライブラリです.
https://gpiozero.readthedocs.io/en/stable/

ラズベリーパイのピンアサインはこんな感じ.

1.LEDを点灯させる

まず抵抗の計算


GPIO 16番につないだLEDを1秒点灯、その後消灯する場合
from gpiozero import LED 
from time import sleep
 
led =LED(16)
 
led.on()
sleep(1)
led.off()
 
2個以上点灯させたい場合は,「led2=LED(XX)」のような形で接続しているGPIOピン(XX)を指定する].
ちなみに,名前からするとLED専用のように思えるが,RPi.GPIOの「GPIO.output(16,True)」と同じ意味.
そのため,この方法でモータドライバなどを使うこともできる.

Arduinoとは異なり,上記の書き方だと1回しかLEDが点灯しない.繰り返したいときはfor文,while文を使う必要がある.(当たり前だけど・・・)
繰り返す数が決まっているならfor文,ある条件を満たすまでならwhile文を使うのが一般的.
for文,while文の詳しい使い方は,以下を確認してください.
https://www65.atwiki.jp/yana_lab/pages/24.html

from gpiozero import LED 
from time import sleep
 
led =LED(16)
 
for t in range(0,5):
    led.on()
    sleep(1)
    led.off()
    sleep(1)
 

2.LEDの明るさを制御する(アナログ出力)
最初の例では,LEDのONとOFFの二つの状態しか作り出すことができない(デジタル出力).
LEDの明るさを制御するにはアナログ信号を出力する必要がある.
from gpiozero import PWMLED 
from time import sleep
 
led = PWMLED(16)#16番にLEDを接続

led.value = 0.25#デューティ比25%で信号を出力
print("25%")
sleep(3)
 
led.value = 1
print("100%")
sleep(3)
 
led.value = 0.#デューティ比0%で信号を出力
print("0%")
print("end")
 
PWMLEDがアナログ出力を実現する方法.
これもLEDという名前だが,RPi.GPIOの「GPIO.PWM(18,60)」と同じでモータドライバの速度調整などにも使える.
ちなみに,わざわざ周波数を設定しなくても動くが,設定したいときはPWMLED(16,frequency=XXX)で設定できる.

3.モータを動かす

1,2のデジタル信号,アナログ信号の出力方法が理解できていれば,一般的なモータドライバ(例えば, ta7291p)などは制御できる.
以下はta7291pの例.

番号 記号 役割
GND グラウンド
OUT1 モータにつなぐ
NC 使わない
Vref PWMを入力して回転数を制御
IN1 IN2み合わせて,正転,逆転,ストップ,ブレーキを制御
IN2 IN1と組み合わせて,正転,逆転,ストップ,ブレーキを制御
Vcc モータドライバ用電源(ラズベリーパイから取る) 5V
Vs モータ用電源 0~20V
NC 使わない
10 OUT2 モータにつなぐ


3.1 PWMを使わない場合(ONとOFFしかない場合)

from gpiozero import LED
from time import sleep
 
m1 = LED(21)##方向制御1(IN1)
m2 = LED(20)##方向制御2(IN2)
m3 = LED(16)##速度調節(Vref)

print("start")
m1.on()
m2.off()
m3.on()
sleep(2)
 
m1.on()
m2.off()
m3.off()
 
print("end")
 
3.2 PWMを使って速度を調整する場合
下の例では,段階的にモータの速度を早くしています.
from gpiozero import LED
from gpiozero import PWMLED
from time import sleep
 
m1 = LED(21)
m2 = LED(20)
m3 = PWMLED(16)
 
print("start")
m1.on()
m2.off()
for n in range(0,10):
    m3.value=n/10
    sleep(1)
 
m1.on()
m2.off()
m3.value=0
 
print("end")
 
3.3 gpiozeroのMotorを使う場合
gpiozeroには「Motor」という便利なモジュールがある.これを使うとさらに短いコードでモータを制御できる.
ただし,配線を以下のように変更すること.(モータドライバ4番のみ変更する必要がある.)

番号 記号 役割:接続先
GND グラウンド
OUT1 モータにつなぐ
NC 使わない
Vref 10kΩの抵抗を介してモータ用電源に接続
IN1 IN2み合わせて,正転,逆転,ストップ,ブレーキを制御
IN2 IN1と組み合わせて,正転,逆転,ストップ,ブレーキを制御
Vcc モータドライバ用電源(ラズベリーパイから取る) 5V
Vs モータ用電源 0~20V
NC 使わない
10 OUT2 モータにつなぐ

from gpiozero import Motor
from time import sleep
 
motor = Motor(forward=20, backward=21) ##IN1,IN2をそれぞれGPIO20,21番に接続

print("start")
motor.forward()
sleep(2)
 
motor.backward()
sleep(2)
 
motor.stop()
print("end")
 
速度を調整したい(PWMを使いたい)ときは,「motor.forward(0.5)」のように書けばOK!

3.4 Robot

さらには,ロボットで2個のモータを制御して前進,後退,右左折をするような場合は,「Robot」というモジュールを使うことができる!
from gpiozero import Robot
from time import sleep
 
robot = Robot(left=(4, 14), right=(17, 18))
 
for i in range(4):
    robot.forward()
    sleep(10)
    robot.right()
    sleep(1)
    robot.left()
    sleep(1)
    robot.backward()
    sleep(1)
    robot.stop()
 

4.AD変換(SPI通信)

例えばMCP3008を使う場合,gpiozeroでは驚くほど簡単なコードで値を取得できる!
配線は以下を参考にしてください.

MCP3008を使って,センサの値をAD変換して10回分の値を表示する場合.
 
from gpiozero import MCP3008
 
ch1 = MCP3008(channel=0)
 
for n in range(0,10):
    print(ch1.value)
 

+ RPi.GPIO

RPi.GPIOの使い方

import RPi.GPIO as GPIO
import time
 
#初期設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)#26番のピンを出力モードで使う

GPIO.output(26,True)#LEDをONにする
time.sleep(2)#2秒間待つ
GPIO.output(26,False)#LEDをOFFにする
time.sleep(2)#2秒間待つ

print("終了")
GPIO.cleanup()
 
解説
GPIO.setmode(GPIO.BCM)
だと、ピン番号ではなく役割に記載されたGPIOの数字で指定することになる.
GPIO.setmode(GPIO.BOARD)
だと、GPIOピンを選択するにはピン番号(左下から右上までの連番)で指定することになる.

1.2 同じ処理を複数回繰り返す場合

import RPi.GPIO as GPIO
import time
 
#初期設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)#26番のピンを出力モードで使う

#繰り返し
for t in range(0,10): #繰り返したい数を指定する.
    GPIO.output(26,True)#LEDをONにする
    time.sleep(2)#2秒間待つ
    GPIO.output(26,False)#LEDをOFFにする
    time.sleep(2)#2秒間待つ
print("おしまい")
GPIO.cleanup()
 
もし、途中で強制的に処理を停止したい場合は、「Ctr+C」で停止させることができる.

1.3 複数のLEDを制御する場合

#LED2個点灯させる場合
import RPi.GPIO as GPIO
import time
 
#初期設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)#26番のピンを出力モードで使う
GPIO.setup(19, GPIO.OUT)#19番のピンを出力モードで使う

GPIO.output(26,True)#26番のLEDをONにする
GPIO.output(19,False)#19番のLEDをONにする
time.sleep(2)#2秒間待つ
GPIO.output(26,False)#26番のLEDをOFFにする
GPIO.output(19,True)#19番のLEDをOFFにする
time.sleep(2)#2秒間待つ
    
GPIO.cleanup()
 
たくさんのLEDを制御する場合も基本は同じ.

(1)使用するピンのを出力モードで使うことを宣言する (GPIO.setup(使うピン番号, GPIO.OUT))
(2)ONにしたいなら,GPIO.output(26,True),OFFにしたいなら Falseと書く.
(3)最後に GPIO.cleanup()で処理を終える.

練習問題1
ここまでの知識を使って,イルミネーションを作ってみる!
直列接続、並列接続を組み合わせて、たくさんのLEDを制御してみる.
抵抗の計算も忘れずに!
 

2.AD変換
ラズベリーパイはデジタル信号しか扱うことができない.しかし,距離や速度,明るさなどの計測を行うには,アナログ信号を取り扱う必要がある.

ここでは,10bit 8ch ADコンバータ「MCP3008」を使用してAD変換を行う.
配線はGPIOZEROに記載したものと同じ.
例えばフォトセンサを使う場合なら以下のように配線してCh1~8のいずれかに接続.

2.1 AD変換

#AD変換 基本

import spidev#SPI通信を使用するためのライブラリ
import time 
 
spi = spidev.SpiDev() #SPI通信の設定
# spi.open(bus,device)
spi.open(0,0)
spi.max_speed_hz = 1000000
 
def readAdc(channel):#センサの測定値を変換する関数
    adc = spi.xfer2([1,(8+channel)<<4,0])
    # print(adc)
    data = ((adc[1]&3) << 8) + adc[2]
    return data
 
if __name__ == '__main__': # 1ch は0, ..., 8chは 7 と書くこと.
        ch = 0 #この場合は1chの信号をAD変換して値を表示
        
        for t in range(0,100): #100回文値を表示する
            data1 = readAdc(ch) #chの値を上記の関数で数値に変換
            print(data1) #値の表示
            time.sleep(0.5) #0.5秒に1回データを計測する.(サンプリング)
 
「def readAdc(channel)」の部分は関数といい,引数(括弧のなかに入れる変数のこと)を入力すると,「return X」のX(返り値)の値を計算して出力してくれる.同じ処理を何度も繰り返す場合などは,その処理を関数化しておくとプログラムも見やすくなり,エラーも少なくなる.

この場合は,1~8chすべて使用しているときに8回センサの値を変換するプログラムを書かなくていいので関数化している.

2.2 複数のチャンネルの値を表示する場合

import spidev#SPI通信を使用するためのライブラリ
import time 
 
spi = spidev.SpiDev()#SPI通信の設定
# spi.open(bus,device)
spi.open(0,0)
 
 
def readAdc(channel): #センサの測定値を変換する関数
    adc = spi.xfer2([1,(8+channel)<<4,0])
    # print(adc)
    data = ((adc[1]&3) << 8) + adc[2]
    return data
 
if __name__ == '__main__': # 1ch は0, ..., 8chは 7 と書くこと
        for t in range(0,50):
            data1 = readAdc(0) # 1chの値を変換
            data2 = readAdc(1) # 2chの値を変換
            data3 = readAdc(2) # 3chの値を変換
            print(data1,data2,data3) #1~3chの値を表示
            time.sleep(0.5) #サンプリング
 

2.3 センサの値に応じて,LEDのON,OFFを切り替える

センサの値が一定値以上だとLEDが点灯し,一定値以下だと消える.
#AD変換 基本+LED制御
import spidev
import RPi.GPIO as GPIO
import time 
 
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)
 
spi = spidev.SpiDev()
# spi.open(bus,device)
spi.open(0,0)
 
 
def readAdc(channel):
    adc = spi.xfer2([1,(8+channel)<<4,0])
    # print(adc)
    data = ((adc[1]&3) << 8) + adc[2]
    return data
 
if __name__ == '__main__': # 1ch -> 0, ..., 8ch -> 7
        ch = 0
 
        for t in range(0,100):
            data1 = readAdc(ch)
            print(data1)
            time.sleep(0.1)
 
            if data1 > 300: #センサの値が300以上であるかどうかを判断
                GPIO.output(26,True)#LEDをONにする
            else:
                GPIO.output(26,False)#LEDをONにする
                
        GPIO.cleanup()
 
練習問題2
センサの値に応じて3種類の点灯パターンをするプログラムを考える.練習問題1と組み合わせてもOK.
 

3.モータの制御

DCモータを制御するプログラム.ここでは,モータドライバ ta7291pを使用.接続方法は以下の通り.

番号 記号 役割
GND グラウンド
OUT1 モータにつなぐ
NC 使わない
Vref PWMを入力して回転数を制御
IN1 IN2み合わせて,正転,逆転,ストップ,ブレーキを制御
IN2 IN1と組み合わせて,正転,逆転,ストップ,ブレーキを制御
Vcc モータドライバ用電源(ラズベリーパイから取る) 5V
Vs モータ用電源 0~20V
NC 使わない
10 OUT2 モータにつなぐ


3.1 モータの制御(ON,OFFのみ PWM未使用)

import RPi.GPIO as GPIO
import time 
 
GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT)#IN1に接続
GPIO.setup(20, GPIO.OUT)#IN2に接続
GPIO.setup(12, GPIO.OUT)#Vrefに接続

#正転
GPIO.output(21,True)
GPIO.output(20,False)
GPIO.output(12,True)
 
time.sleep(2)
 
#ブレーキ
GPIO.output(21,True)
GPIO.output(20,True)
GPIO.output(12,False)
 
print("おしまい")
GPIO.cleanup()
 
この場合はPWMを使わずにVrefと接続した12番をTrueにして正転させたため,デューティ比100%と同じ.つまり,全力でONとOFFしかできない.

3.2 PWMを使用する場合(速度を調整できる)

import RPi.GPIO as GPIO
import time 
 
GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT)#IN1に接続
GPIO.setup(20, GPIO.OUT)#IN2に接続
GPIO.setup(18, GPIO.OUT)#Vrefに接続

p18 = GPIO.PWM(18,60)#PWMを使用するための宣言(60については気にしなくていい)
p18.start(0)#PWMをつかえる状態にする

#正転
GPIO.output(21,True)
GPIO.output(20,False)
 
p18.ChangeDutyCycle(0)#デューティ比0%
print("0%")
time.sleep(2)
p18.ChangeDutyCycle(25)#デューティ比25%
print("25%")
time.sleep(2)
p18.ChangeDutyCycle(50)#デューティ比50%
print("50%")
time.sleep(2)
p18.ChangeDutyCycle(75)#デューティ比75%
print("75%")
time.sleep(2)
p18.ChangeDutyCycle(100)#デューティ比100%
print("100%")
time.sleep(2)
 
print("おしまい")
GPIO.cleanup()
 
すべてのピンがPWMに対応しているわけではないみたい.
もっとたくさん必要にな場合は,ICを使うしかない.(例えば,TLC5940など)
PWMについてはメカトロニクス演習で勉強して.

練習問題3
モータドライバを2つ使って,2つのモータを制御してみる.配線が複雑になるので,ショートに注意!
 

4. ライントレースカーの基礎

ここではセンサを1つ使って,ON・OFF制御でライントレースするプログラムを紹介する.
#ライントレースカー サンプル
#更新日:2017/5/16
#作成者:栁澤

#基本形
#右左折のみで進む

 
import RPi.GPIO as GPIO
import spidev
import time
 
##モータ制御関連###################################
m_right1 = 16 #右モータ方向制御用pin1
m_right2 = 20 #右モータ方向制御用pin2
m_right_pwm = 12 #右モータ速度制御用

m_left1 = 26  #左モータ方向制御用pin1
m_left2 = 19  #左モータ方向制御用pin2
m_left_pwm = 18 #左モータ速度制御用

#GPIOの設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(m_right1, GPIO.OUT)
GPIO.setup(m_right2, GPIO.OUT)
GPIO.setup(m_right_pwm, GPIO.OUT)
right_pwm = GPIO.PWM(m_right_pwm,60)
right_pwm.start(0)
 
GPIO.setup(m_left1, GPIO.OUT)
GPIO.setup(m_left2, GPIO.OUT)
GPIO.setup(m_left_pwm, GPIO.OUT) 
left_pwm = GPIO.PWM(m_left_pwm,60)
left_pwm.start(0)
 
 
dt = 0 #サンプリングの設定(必要に応じて)
thre_v = 300 #閾値(白と黒の境目を決める)
################################################

##センサ関連(AD変換,SPI通信#######################
spi = spidev.SpiDev()
# spi.open(bus,device)
spi.open(0,0)
 
def readAdc(channel):
    adc = spi.xfer2([1,(8+channel)<<4,0])
    # print(adc)
    data = ((adc[1]&3) << 8) + adc[2]
    return data
################################################

##メインルーチン################################
while(1):
    ##計測(今回は真ん中しか使っていない)
    #data1 = readAdc(0)#LEFT
    data2 = readAdc(1)#CENTER
    #data3 = readAdc(2)#RIGHT
    #print(data1, data2, data3)

     ##ライントレースロボットの判断部分
    if data2 > thre_v: #left
        GPIO.output(m_left1,True)
        GPIO.output(m_left2,True)
        left_pwm.ChangeDutyCycle(25)
 
        GPIO.output(m_right1,False)
        GPIO.output(m_right2,True)
        right_pwm.ChangeDutyCycle(25)
 
        time.sleep(dt)
        #print("left")

    else:
        GPIO.output(m_left1,True)
        GPIO.output(m_left2,False)
        left_pwm.ChangeDutyCycle(25)
 
        GPIO.output(m_right1,True)
        GPIO.output(m_right2,True)
        right_pwm.ChangeDutyCycle(25)
 
        time.sleep(dt)
        #print("right")
################################################

GPIO.cleanup()
 
特にゴール条件を設定していないので注意.Ctr+Cで強制終了させてください.

5.カラーLED

5.1 グラデーションカラーLED

カラーLEDを白→青緑→青→オレンジ→赤の順で変化するプログラムを紹介する。
ここではラズベリーパイZEROとフルカラーLEDを使用しています。

ラズベリーパイZEROとフルカラーLEDの接続法

  • 赤色は22番、青色は23番、緑色は24番のピンに接続をする。
  • フルカラーLEDのマイナス面のコードはGND(6番ピン)に接続する。
  • さらにそのGND(6番ピン)を1番ピンに接続するようにしてください。
赤の明るさを青や緑に揃えるため、50Ωの抵抗をピンとLEDの間に取り付けてください。
※ピンの位置は配線によって変更して問題ないので対応していれば好きな位置に配置して大丈夫です。


import RPi.GPIO as GPIO
from time import sleep
 
#初期設定
Rpin = 22#22番のピンを赤色LEDの出力モードとして使う
Bpin = 23#23番のピンを青色LEDの出力モードとして使う
Gpin = 24#24番のピンを緑色LEDの出力モードとして使う
GPIO.setmode(GPIO.BCM)
GPIO.setup(Rpin,GPIO.OUT)
GPIO.setup(Bpin,GPIO.OUT)
GPIO.setup(Gpin,GPIO.OUT)
red = GPIO.PWM(Rpin,50)
green = GPIO.PWM(Gpin,50)
blue = GPIO.PWM(Bpin,50)
red.start(100)
green.start(100)
blue.start(100)
 
I = 0#時間経過をIとする。

for i in range (100):#最大のIを100とする

    print(I)
 
    Ir = Ig = Ib = I
 
       if Ir >= 0 and Ir <= 25:#Iが0から25の時、赤色を制御する
            red.ChangeDutyCycle(100-(I/25)*100)#デューティ比100%→0%

       if Ir > 25 and Ir <= 50:#Iが25から50の時、赤色を制御する
            red.ChangeDutyCycle(0)#デューティ比0%

       if Ir > 50 and Ir <= 75:#Iが50から75の時、赤色を制御する
            red.ChangeDutyCycle((I - 50)/25*100)#デューティ比0%→100%
    
       if Ir > 75 and Ir <= 100:#Iが75から100の時、赤色を制御する
            red.ChangeDutyCycle(100)#デューティ比100%
        
       if Ig >= 0 and Ig <= 50:#Iが0から50の時、緑色を制御する
            green.ChangeDutyCycle(100-(I/50)*100)#デューティ比100%→0%
    
       if Ig > 50 and Ig <= 75:#Iが50から75の時、緑色を制御する
            green.ChangeDutyCycle((I-50)/25*50)#デューティ比0%→50%
    
       if Ig > 75 and Ig <= 100:#Iが75から100の時、緑色を制御する
            green.ChangeDutyCycle(50-((I-75)/25)*50)#デューティ比50%→0%
        
       if Ib >= 0 and Ib <= 50:#Iが0から50の時、青色を制御する
            blue.ChangeDutyCycle(100)#デューティ比100%
        
       if Ib > 50 and Ib <= 65:#Iが50から65の時、青色を制御する
            blue.ChangeDutyCycle(100-((I-50)/15)*100)#デューティ比100%→0%

       if Ib > 65 and Ib <= 100:#Iが65から100の時、青色を制御する       
            blue.ChangeDutyCycle(0)#デューティ比0%
    
       if I > 100:#Iが100以上の時、全色を制御する
            red.ChangeDutyCycle(100)#デューティ比100%
            blue.ChangeDutyCycle(0)#デューティ比0%
            green.ChangeDutyCycle(0)#デューティ比0%
 I = I+1#Iをプラス1していく

    sleep(0.01)#0.01秒待ってIをプラス1ずつ行う

 
GPIO.cleanup()#ここでストップ
 
これは例なのでデューティ比のところの数値をいじったりすることで様々な色、パターンを作ることができます。
プログラム自体もまだ改善できるところもあるので時間ある場合チャレンジしてみてください。

5.2 カラーLEDを心拍センサに接続する

グラデーションで動いていたカラーLEDを心拍に対応して動くようにするプログラムです。
このWikiの心拍の欄にあるPolar H10に接続しており、このプログラムは計測を行いながら数値もデータとして残してくれるため解析を行いやすくなっています。
先にFFT心拍などを先に見ておくと扱いやすいかと思います。
接続するデバイスによってアドレスが変わるので注意(ウェアラブルデバイスのアドレス(BLE)という所)

##作成者:栁澤
##更新日:2020/10/20
##内容: PolarのウェアラブルデバイスからBLE通信で拍数を受け取る
##realtime FFT+colorLEDによるBFB
##送信データにRRIがない場合がたまにあるので、その例外対応を追加
##FFTの結果確認用にグラフを保存するように変更

from bluepy.btle import Peripheral
import bluepy.btle as btle
import binascii
import numpy as np
##import Color_LED #LEDの制御用
from scipy import signal
from scipy import interpolate
from time import sleep
import matplotlib.pyplot as plt
import RPi.GPIO as GPIO
 
##初期設定#######################################################################
global N, dt, t_now, count, t, x, save_hr_data, savedata_fft
N = 128 #FFT、128秒ごとに数値が表示される
dt = 1 # サンプリング周期 [s]
t_now = t = 0 #t_now=measure for hear rate, t=measure for RRI(FFT)
count = 1
save_hr_data = save_rri_data = savedata_fft = [0, 0]
RRI_data = time_data = []
Rpin = 22                    #┐
Bpin = 23                    #|
Gpin = 24                    #|
GPIO.setmode(GPIO.BCM)       #|
GPIO.setup(Rpin,GPIO.OUT)    #|
GPIO.setup(Bpin,GPIO.OUT)    #|
GPIO.setup(Gpin,GPIO.OUT)    # 〉ここはグラデーションと同じ
red = GPIO.PWM(Rpin,50)      #|
green = GPIO.PWM(Gpin,50)    #|
blue = GPIO.PWM(Bpin,50)     #|
red.start(100)               #|
green.start(100)             #|
blue.start(100)              #┘
################################################################################

#ウェアラブルデバイスのアドレス(BLE)
ROHM_RAW = "XX:XX:XX:XX:XX:XX"#「sudo hcitool lescan」で確認すること

##データを受信した時に呼ばれるclass################################################
class MyDelegate(btle.DefaultDelegate):
    def __init__(self, params):
        btle.DefaultDelegate.__init__(self)
 
    def handleNotification(self, cHandle, data):
        global N, dt, t_now, count, t, x, save_hr_data, savedata_fft, RRI_data, time_data, save_rri_data
        c_data = binascii.b2a_hex(data)
        ##「c_data」が受信したデータ,デフォルトは16進数標記なので、10進数に戻す必要がある
        hr = int(c_data[2 : 4], 16)
        if len(c_data) > 6:
            v1 = int(c_data[4 : 6], 16)
            v2 = int(c_data[6 : 8], 16)
            rri1 = (v2 << 8) + v1
            RRI_data = np.hstack([RRI_data, rri1])
            t = t + (rri1 / 1000)
            time_data = np.hstack([time_data, t])
 
        if len(c_data) > 8:
            v3 = int(c_data[8:10],16)
            v4 = int(c_data[10:12],16)
            rri2 = (v4 << 8) + v3
            RRI_data = np.hstack([RRI_data, rri2])
            t = t + (rri2 / 1000)
            time_data = np.hstack([time_data, t])
            #print(t, rri2)

        t_now = t_now + 1 ##時間のカウント(心拍数のサンプリング1sなので)
        save_hr_data = np.vstack([save_hr_data, [t_now, hr]])
        x = RRI_data ##FFT用の信号の配列
        save_rri_data = np.vstack([time_data, RRI_data])
        #print(t, count, len(c_data))

        ##FFT###################################################################
        if t >= 128 * count:##128秒で1回FFTをかける
            f = interpolate.interp1d(time_data, x, fill_value='extrapolate')
            tt = np.linspace(1 + 128 * (count - 1), N * count, N)
            x = f(tt)
            x = signal.detrend(x) ##デトレンドする
            F = np.fft.fft(x) # FFT変換結果
            freq = np.fft.fftfreq(N, d=dt) # 周波数
            Amp = np.abs(F/(N/2)) # 振幅
            Freq_data = freq[1:int(N/2)] #後ろ半分はいらないので
            Amp_data = Amp[1:int(N/2)] #後ろ半分はいらないので

            LF = HF = 0 #LF,HFを計算する
            for n in range(0,len(Amp_data)):
                if Freq_data[n] > 0.05 and Freq_data[n] < 0.15: ##LF計算用
                    LF = LF + Amp_data[n]
                if  Freq_data[n] > 0.15 and Freq_data[n] < 0.4: ##HF計算用
                    HF = HF + Amp_data[n]
 
            ##figure save#######################################
#             fig = plt.figure()
#             ax1 = fig.add_subplot(2,1,1)
#             ax1.plot(tt,x)
#             ax1.set_xlabel("Time[s]")
#             ax1.set_ylabel("signal")
#             ax2 = fig.add_subplot(2,1,2)
#             ax2.plot(Freq_data, Amp_data)
#             ax2.set_xlabel("Freqency[Hz]")
#             ax2.set_ylabel("Amplitude")
            #save_fig_name = "FFT_" + str(count) + ".png"
            print(count, LF / HF)
            #fig.savefig(save_fig_name)
            savedata_fft = np.vstack([savedata_fft,[LF, HF]])
            ####################################################

            ##色に変換するための処理
            evaluation_index = LF / HF ##色に変換するための計算(ストレス指標であるLF/HFを求める)
            fb_value = evaluation_index / 2.0 ##最大値を3で考えているが、
            hr_bfb_value = np.clip(fb_value,0,1) ##60未満は0,3.5以上は1にする.
            #Color_LED.colorBarRGB(hr_bfb_value) ##カラーLEDの制御へ
            
            Ir = Ig = Ib = I = fb_value*100
 
            if Ir >= 0 and Ir <= 25:            #┐
                red.ChangeDutyCycle(100-(I/25)*100)       #|
                                                          #|
            if Ir > 25 and Ir <= 50:                      #|
                red.ChangeDutyCycle(0)                    #|
                                                          #|
            if Ir > 50 and Ir <= 75:                      #|
                red.ChangeDutyCycle((I - 50)/25*100)      #|
                                                          #|
            if Ir > 75 and Ir <= 100:                     #|
                red.ChangeDutyCycle(100)                  #|
                                                          #|
            if Ig >= 0 and Ig <= 50:                      #|
                green.ChangeDutyCycle(100-(I/50)*100)     #|
                                                          #|
            if Ig > 50 and Ig <= 75:                      #|
                green.ChangeDutyCycle((I-50)/25*50)       # 〉ここはグラデーションと同じ(LEDの色を管理するところ)
                                                          #|
            if Ig > 75 and Ig <= 100:                     #|
                green.ChangeDutyCycle(50-((I-75)/25)*50)  #|
                                                          #|
            if Ib >= 0 and Ib <= 50:                      #|
                blue.ChangeDutyCycle(100)                 #|
                                                          #|
            if Ib > 50 and Ib <= 65:                      #|
                blue.ChangeDutyCycle(100-((I-50)/15)*100) #|
                                                          #|
            if Ib > 65 and Ib <= 100:                     #|
                blue.ChangeDutyCycle(0)                   #|
                                                          #|
            if I > 100:                                   #|
                red.ChangeDutyCycle(100)                  #|
                blue.ChangeDutyCycle(0)                   #|
                green.ChangeDutyCycle(0)                  #┘

            count = count + 1 ##LF,HFの計算が終わったらカウンタをゼロに戻す
            t = time_data[-1] ##FFT用の時間の配列をリセット
            x = RRI_data[-1] ##FFT用の信号の配列をリセット
        #########################################################################

################################################################################

##センサークラス#################################################################
class SensorBLE(Peripheral):
    def __init__(self, addr):
        Peripheral.__init__(self, addr, addrType="random")
################################################################################

##メインの関数###################################################################
def main():
    # 初期設定
    medal = SensorBLE(ROHM_RAW)
    medal.setDelegate(MyDelegate(btle.DefaultDelegate))
 
    # ノーティフィケーションを有効にする
    # Polar H10デバイスの場合は,0x0011に 0100を送れば有効になる
    medal.writeCharacteristic(0x0011, b"\x01\x00", True)
 
    # データを受信し続ける
    while True:
        try:
            if medal.waitForNotifications(1.0):##1秒間隔でデータを受け取る
                continue
        except btle.BTLEDisconnectError:
            print("retry")
            medal = SensorBLE(ROHM_RAW)
            medal.setDelegate(MyDelegate(btle.DefaultDelegate))
            medal.writeCharacteristic(0x0011, b"\x01\x00", True)
        except ValueError:
            print("There are no try")
            medal = SensorBLE(ROHM_RAW)
            medal.setDelegate(MyDelegate(btle.DefaultDelegate))
            medal.writeCharacteristic(0x0011, b"\x01\x00", True)
        except:
            print("reconnect")
            sleep(5)
            medal = SensorBLE(ROHM_RAW)
            medal.setDelegate(MyDelegate(btle.DefaultDelegate))
            medal.writeCharacteristic(0x0011, b"\x01\x00", True)
 
 
 
################################################################################

##メイン#########################################################################
if __name__ == "__main__":
    print("start")
    try:
        main()
    except KeyboardInterrupt:
        np.savetxt("RRI_result.csv",save_rri_data, delimiter=',')
        np.savetxt("HR_result.csv",save_hr_data, delimiter=',')
        np.savetxt("FFT_result.csv",savedata_fft, delimiter=',')
        #Color_LED.ledoff()
    except:
        np.savetxt("RRI_result.csv",save_rri_data, delimiter=',')
        np.savetxt("HR_result.csv",save_hr_data, delimiter=',')
        np.savetxt("FFT_result.csv",savedata_fft, delimiter=',')
        main()
    finally:
        np.savetxt("RRI_result.csv",save_rri_data, delimiter=',')
        np.savetxt("HR_result.csv",save_hr_data, delimiter=',')
        np.savetxt("FFT_result.csv",savedata_fft, delimiter=',')
        #Color_LED.ledoff()
################################################################################

 
 
+ タグ編集
  • タグ:
  • ラズベリーパイ
  • Python
  • GPIOZERO
  • RPi.GPIO
  • Raspberry pi
ウィキ募集バナー