[ラズパイ] エアロバイク(スピンバイク)の速度パルスをRaspberry piで読みとる

オシロスコープを手に入れて、いろんなものの波形を図ってみたくなったw。

身近にあるスピンバイクの速度計にはイヤフォンジャックみたいなのがついていて、それを経由してパルスを読み取り車速、時間、距離を計測してくれる。このパルスをセンシングして、ストリートビューとシンクロさせたりすればアウトドアサイクリングofインドアとか、扇風機と組み合わせて速度に合わせて向かい風を出すとかwできるはず!!

早速、オシロスコープで波形を確認する

ほとんど、3V(HIGH)で一回転ごとに上のような0V(LOW)がでる。3.3Vなら直接GPIOにつなごうかと思ったけど、HIGHとLOWも反転させたかったので手元にあったMOSFET(2SK4017)でNOT回路を作り、出力を3.3Vでプルアップした。参考のサイトは以下です



別にMOSFETじゃなくて普通のトランジスタでもよかったんけだと、手元にあってMOSFETをまともに考えて使ったことがなかったから、MOSFETを選んだだけと最初にに言っておくw

MOSFETと一般のトランジスタ(バイポーラトランジスタ)の違いは、ベース(ゲート)での電圧制御(MOSFET)なのか電流制御(バイポーラトランジスタ)なのかという点。なので、バイポーラトランジスタの場合はベース-エミッタ間には電流を流さないとコレクタ-エミッタ間で電流は流れないが、MOSFETはゲート-ソース間に電圧をかけるだけでドレイン-ソース間に電流を流すことができる。このとき、ゲート-ソース間には電流は流れない(消費電力がない)という優れもの。回路の記号を見てもわかるけどゲート-ソースはつながってない。

R1は一般的にプルアップ抵抗と呼ばれるものでGPIOやMOSFETに流れる電流値で決めた。R2はトランジスタでは制限抵抗で必要と思うけど、電流流れないからいらなくないかなぁと考えていたら、上の参考でこう書いてあった。

MOSFETは一般にゲートに抵抗を接続して駆動します。この抵抗は突入電流を抑制し、出力のリンギングを軽減します。おおむね数Ω~1k Ωが使われます。

リンキングとは、出力に電圧の振動が起こってしまうことらしい。これを防止するためにいれるとのこと。オシロで確認してみたけど特にR2を入れても入れなくても変わらなかった。

ゲート-ソース抵抗(10kΩ程度)も必要があれば入れたほうがイイと思う。入力信号のLOWを確実に0Vにする、おまじない的なものらしいけど・・・

オシロをつないでの波形は下。

反転して、見やすくなったけど、なんで2つの山になっているのか。おそらく、スピンバイクの速度表示を見てみると、このパルスを通過すると速度表示が改まることから、波形の時間を計測して速度を出しているんだろうと思う。

右手でペダルを回しながらオシロを確認するという阿保みたいな作業を深夜にするのも疲れ、いつ嫁からクレームがきてもおかしくない!!。なので単純に一回転する時間と一回転する距離から速度を求めることにした。

バイクに乗って、大体22回転で100メートル進むことを確認。一回転100/22メートルでの時間で速度を求める。シグナルが一山ならパルスの立ち上がり間の時間を計測するだけでよかったんだけど・・・2つ目のパルスは無視する方法でいく。

ということで、今回は立ち上がりを検知した後、一定時間検知をしないようにして不応期をもうけることで、2つ目の山の立ち上がりの検知を無視して計測し1回転当たりの時間を算出することとした。難点は、超低速・超高速の場合に速度表示がおかしくなることだけど、とりあえずはこれでやってみる。下がコード

import RPi.GPIO as GPIO
import time
from text_ssd1306 import Text_ssd1306
  
GPIO_SW = 18
N = 22.0

ts = Text_ssd1306(1)
ts.setFont('IPAPGothic-02.ttf')

# Callback
def callback(pin):
    global l_time
    n_time = time.time()
    speed = 360 / ((n_time-l_time)*N)
    ts.textOut('{:.2f}km'.format(speed),line = 1)
    print(speed)
    l_time = n_time

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_SW,GPIO.IN, pull_up_down=GPIO.PUD_UP)

l_time = time.time()
GPIO.add_event_detect(GPIO_SW,GPIO.RISING,callback=callback,bouncetime=500)
#GPIO.add_event_callback(GPIO_SW,callback)


try:
    while True:
        time.sleep(1)

except KeyboardInterrupt:
    GPIO.cleanup()

GPIO18に接続。Nは100mあたりのペダル回転数(実測値)。GPIOに対してLOWからHIGHに信号が変わるとき(GPIO.RISING)にコールバックを発生させる。この時不応期(bouncetime)として500ms設定した。l_timeには最後のCallback時間(パルス立ち上がり時間)を記録しコールバック毎に更新する。

速度の計算は次のように行う。1回転にかかった時間は(n_time – l_time)秒で取得できる。進む距離は100÷N mなので、速度(メートル/秒)は以下の式となる。

$$\frac{100\div N}{n\_time – l\_time}=\frac{100}{N(n\_time – l\_time)}~~m/s$$

更に時速kmに直すと

$$\frac{100}{N(n\_time – l\_time)}\times\frac{60\times60}{1000}=\frac{360}{N(n\_time – l\_time)}~~km/h$$

で算出した。表示に関しては有機EL表示器(ssd1306)を使ってみた。Text_ssd1306クラスなどの詳細は以下の記事を参考にしてね。

上の写真・動画はR2の抵抗がないけど、あとは一緒。片手でペダル回しながら動画撮影なんでブレブレですんません。足で漕ぎながら撮影しようと思ったけどケーブル足らなくて・・・。改善の余地は多々あるも、標準の表示器と大体同じ速度にはなった。