稼働中

マイクロビット(e_44)2WDカー

スマートカー?

以前に2WDロボットカーの本体(シャーシ)を購入していたので組み立てて動かしてみようと思います。
「ロボットカー」と言うようなレベルではありません。
フォトインタラプタモジュール(GR-YM-227)の記事を具現化したような感じです。距離センサなども搭載してロボットカー的にしたいと思ったのですが、micro:bit V1だとMemorryエラーになってしまうので無線のコントロールだけにしました。

外観

2WDカーのシャーシ組み立てた外観です。いろんなセンサーなどを取付られるように板に穴が沢山あります。
シャーシ組立
DCモータの速度制御用にフォトインタラプタを取り付けました。両面テープで固定しました。
フォトインタラプタ
付属していたエンコーダをモータ軸に取り付けています。フォトインタラプタのLEDが外側になるように取り付けています。
エンコーダ取付

接続例

DCモータの制御用に、micro:bitのpin6、pin7、pin8、pin9出力をレベルコンバータを介してモータードライバー(TA7291P)の制御ピンIN1、IN2へそれぞれ接続します。
フォトインタラプタ(GR-YM-227)の出力をmicro:bitのpin1、pin10に入力します。
pin6、pin7、pin9、pin10はmicro:bitのLED Displayに使われているので、使用の際にはdisplay.off()が必要になります。
赤のLEDは無線動作の確認用です。
※TA7291Pの説明は記事e_41を参照してください。他の詳細はデータシート等を参照ください。
接続例

スクリプト

インタラプタセンサーモジュール(GR-YM-227)の記事e_42とMOVE miniをRadio通信の記事e_39を合わせたような感じになってします。詳細はそれぞれの記事を参照ください。
※thonny-microbitのMicroPythonを使っています。

送信側(コントローラー側)

コントローラー側のmicro:bitの前後の傾きから前後の向きと速度を設定し、左右の傾きから左右のモータ速度を調整し、受信側で使うTGTデータ(目標period値)を送信しています。
無線に関しては、MOVEminiの時はややこしい方法で文字列を送受信していましたが、別の方法に気付きました。
送信する文字列は’F,15,15’のようにコンマ区切りにし、受信側でsplitでリストデータにしました。
この方が簡単です。
’F,15,15’の場合はF:前進、15,15:左右モータの目標period値です。他はB:後進 S:停止にしています。


scar_send_tes_02b.py
from microbit import *
import radio

## 応答待ち CMD送信したら返信あるまで待機する
def RS():
    while True:
        r_msg = radio.receive()
        if r_msg:
            # from Move recieved
            #print(r_msg)
            break
# acc x,y から TGTデータ(目標period値)を作成
def M_TGT(x_acc,y_acc):
    # 10ms-80ms/(800-128) で目標値を計算するが、15msで制限、~128は無視
    # TGT target period speed msec
    tgt_msec=int((abs(y_acc)-128)/(800-128)*(10-80)+80)
    if tgt_msec < 15:
        tgt_msec=15         # tgt_msec mimimal(max speed)

    # 左右の傾き率分を加算して遅くする
    # 0%-100%/(0-1024)
    corr=abs(x_acc)/1024*2.5
    if corr <= 0.2: # 20% は不感帯に corr=0 corr=corr+1 # 遅くする> periodを長くする>曲がる
    tgt_corr=int(tgt_msec*corr) # 倍率したTGT(period)
    if tgt_corr>=80:  # 制限 80msec (mimimal speed) 無くてもよい
       tgt_corr=80
    #print("tgt",tgt_msec,corr,tgt_corr)

    # x:R(+)L(-)  Y:B(+)F(-) Z:DN(+)UP(-)
    # 右傾斜なら右曲進、右periodを増やす=>遅くなる
    if x_acc>=0:
        # Turn  R 
        # 補正後のPWM(L,R)
        d_tgt=(tgt_msec, tgt_corr )
    # 左傾斜
    else:
        # Turn  L 
        d_tgt=(tgt_corr, tgt_msec )
    # カウント、元PWM値、補正値、補正後のPWM(L,R)
    #print(cnt,'pwm,corr,s_pwm',d_pwm,corr,s_pwm)
    return d_tgt         # TGT period ms (R,L)

radio.on()

cnt=0
while True:
    # A+B button でブレーク
    if button_a.is_pressed() & button_b.is_pressed():
        cnt=cnt+1
        print('A+B Pushed STOP')
        radio.send('S')
        radio.off()
        break

    # x:R(+)L(-) Y:B(+)F(-) Z:DN(+)UP(-)
    r_acc=accelerometer.get_values()        # accデータ値を取得
    #print(cnt,r_acc[0],r_acc[1])   
    # y-axis FWD/BWD
    if r_acc[1]>0:
        drc='B'    # 後進文字'B'
    else:
        drc='F'    # 前進文字'F'

    # 前後傾きでモーター回転(前後)0~255 STOP
    if abs(r_acc[1])>=255: 
        cnt=cnt+1
        r_tgt = M_TGT(r_acc[0],r_acc[1])    # 左右の傾きを加味したtgt計算 
        #print(cnt,r_acc[0],r_acc[1],r_tgt)

        # 送信する文字列    ここで左右を入れ替えしてる(受信側に合わせた)
        msg=drc +','+str(r_tgt[1])+','+str(r_tgt[0])
        radio.send(msg)
        print(cnt, msg)
        #sleep(100)         # 少しある方が良いかも?
        RS()   # 返信待 
    else:
        print('y_acc <255 Pass STOP')
        #pass
        msg='S' +','+'500'+','+'500'
        radio.send(msg)
        print(cnt, msg)
        RS()   # 返信待
        #sleep(50)

実行例
Thoonyのshellに表示されます


>>> %Run scar_send_tes_02b.py
1 B,77,77               # 後進、period(77,77)
2 B,77,77
y_acc <255 Pass STOP   # 傾きが小さいので
2 S,500,500             # STOP 500,500はダミー値
3 F,15,15               # 前進、period(15,15)
4 F,72,72
y_acc <255 Pass STOP
4 S,500,500
y_acc <255 Pass STOP
・・
・・
y_acc <255 Pass STOP
189 S,500,500
190 B,80,80
A+B Pushed STOP         # ブレーク停止

受信側(2WDカー側)

’F,15,15’のようにコンマ区切り文字を受信するのでsplit(‘,’)を使って[F,15,15]のリストデータにしました。
エンコーダで測定した現在のperiod値と送信されてきた目標のperiod値を比較、差分からモータ駆動するpwmを計算しています。
他の処理動作も加えたかったのですが、さらに1~2行追加するとMemorryエラーになってしまうので諦めました。


scar_05_fback_radio_02b.py
from microbit import *
import machine
import radio
# TA7291P TP=[IN1, IN2] 
R=[pin6,pin7]               #[TP7291P IN1,IN2]
L=[pin9,pin8]               # モータの極性を入れ替えておく
EN=[pin1,pin10]             # エンコーダ出力 input pin

# pin6,7,9,10を使うのでdisplayをオフ
display.off()

# 応答を返信
def RS_msg():
    s_msg='recived'
    radio.send(s_msg)
# FWD(pwm value, TP=(R/L)) R=[pin6,pin7] L=[pin9,pin8]
def FWD(pwm,TP):
    TP[0].write_analog(0)            # analogモード、出力は’0’V
    TP[1].set_analog_period(20)      # 周期を設定 20msec
    TP[1].write_analog(pwm)          # 0 ~ 1023  128~
    sleep(5)                         # TP7921P 切替100us~
# BWD IN1(L/0)IN2(H/PWM)
def BWD(pwm,TP):
    TP[1].write_analog(0)
    TP[0].set_analog_period(20)
    TP[0].write_analog(pwm)
    sleep(5)
def STP(TP):
    TP[0].write_digital(0) # IN1
    TP[1].write_digital(0) # IN1
    sleep(5)

# 現在のエンコーダ値(period)から 変更するdat(pwm値)を返す
def period(TGT,DCM):        #DCM R(0)L(1)
    global dat, c_ng, EN
   
    add=4                           # 初期の加算値
    min_pls=192                     # モータがギリ動くpwm-value
    # 加減の倍率を周期20msecで分ける
    if TGT/1000 <= 22:
        mag=5                       # 4 ~ 10 調整
    else:
        mag=1/2   # 1/5 ~ 1.0
    # エンコーダ(period)測定 time_pulse_us
    d = machine.time_pulse_us(EN[DCM],1)   # Ton時間測定
   
    # 目標値とのmsec差分 
    dif=int((TGT - d)/1000)        # usec/1000 -> msecに


    # 差分に対して加減するPWM値
    add=int( abs(dif) * mag)       # mag 減算の倍率

    if ((d==-2 ) or (d==-1)) and c_ng[0]<=3:    # 回転してない PWM値を増やす
        dat[DCM]=min_pls+128*c_ng[DCM]          # エラー回数でPWM値を増やす
        c_ng[DCM]=c_ng[DCM]+1
        # 回数、コメント、period(usec)、PWM値、加減値、差分      ----①
        print(cnt, DCM, c_ng, 'error', d, dat, dif, add)
        return dat

    if abs(dif) < 1:   # 2msec未満なら無視
        # 回数、コメント、period(usec)、PWM値、加減値、差分
        print(cnt, DCM, c_ng, 'cont', d, dat, dif, add)     ----②
        return dat
    # 測定値の方が小(早い)ので遅くする・pwm値を下げる・減算する
    if dif > 0 :
        dat[DCM]=dat[DCM]-add
        if dat[DCM] < min_pls:      # 減算後、下限値以下なら下限値にする
            dat[DCM] = min_pls      # ギリ動く
            c_ng=[0,0]
        print(cnt, DCM, c_ng, 'dif<0', d, dat, dif, add)    ----③
        
    # 測定値の方が遅いので早くする・pwm値を上げる・加算する
    else:
        dat[DCM]=dat[DCM]+add
        if dat[DCM] > 1023:     # 加算後がPWM値1023超えなら上限1023にする
            dat[DCM]=1023
            return dat
        print(cnt, DCM, c_ng, 'dif<0',d, dat, dif, add)     ----④
    return dat

# period 測定からpwm値を算出してdat[R,L]に入れる
def get_encord(TGT):
    # period(tgt_ms,dcm_num)
    dat[0]=period(TGT[0]*1000, 0)[0]            # TGT(ms)を(us)に
    dat[1]=period(TGT[1]*1000, 1)[1]  
    return dat

## MAIN ---------------------------------------------------
# time_pulse_usを使うpinをPULL設定して予約する
EN[0].set_pull(EN[0].PULL_DOWN)  #EN[0]:R
EN[1].set_pull(EN[1].PULL_DOWN)  #EN[1]:L

c_ng=[0,0]      # 回転エラー回数 time_pulse_usのエラー
dat=[0,0]       # PWM値[R, L]
TGT=[0,0]       # 目標period値 [R, L] 15~70msec

radio.on()      # 無線ON
#STP(R)                             #(①~④printをコメントアウトして有効化)
#STP(L)                             #(①~④printをコメントアウトして有効化)
while True:
    #pin0.write_digital(1)         # 繰返し動作開始LEDをON(①~④printをコメントアウトして有効化)
    r_msg = radio.receive()       # 受信
    # 受信データがあれば以下を処理
    if r_msg:
        cnt=cnt+1
        #pin0.write_digital(0)     # 受信あればLEDをOFF(①~④printをコメントアウトして有効化)
        m_msg=r_msg.split(',')    # r_msg  splitを使うと簡単

        if m_msg[0]=='S': # STOP
            STP(R)
            STP(L)
            # 返信
            RS_msg()
            continue
        # TGT(period) >> dat(pwm)
        TGT=[int(m_msg[1]),int(m_msg[2])]  #TGT[R,L]period 
        dat=get_encord(TGT)
        # 前進
        if m_msg[0]=='F':
            FWD(dat[0],R)
            FWD(dat[1],L)
        # 後進
        if m_msg[0]=='B':
            BWD(dat[0],R)
            BWD(dat[1],L)
        # 返信
        RS_msg()

実行例

Thoonyのshellに表示されます。


print(cnt, DCM, c_ng, 'dif<0',d, dat, dif, add) 
回数、モータ番号、エラー数、コメント、period(us)、dat[R,L]、差分、加減値 
>>> %Run scar_05_fback_radio_02b.py
1 0 [1, 0] error -2 [192, 0] 68 34      # 初動時のエラー(回転してない) pwm=192から開始
1 1 [1, 1] error -1 [192, 192] 68 34
2 0 [2, 1] error -2 [320, 192] 68 34
2 1 [2, 2] error -1 [320, 320] 68 34    # 加算ステップ128 192+128*1=320
3 0 [3, 2] error -2 [448, 320] 68 34
3 1 [3, 3] error -1 [448, 448] 68 34    # 192+128*2=448
4 0 [3, 3] dif<0 53094 [441, 448] 14 7  # 回転
4 1 [3, 3] dif<0 42072 [441, 436] 25 12
5 0 [3, 3] dif<0 21558 [418, 436] 46 23 # 441-23=418

ちなみに、2WDカーに付属していたモーターの特性を(GR-YM-227)の記事と同じように測定しました。
無負荷状態です。
モータ特性

動作確認

送信文字列を’F,15,80’で固定して動作を確認しました。
先のようにThonnyのshellに表示されたデータを表計算でまとめてみました。
個別データは大きくばらついていますが、平均的には15,80msec近くになっているように見えます。それなりに制御できているようです。
また、コントローラを前後、左右に傾けると前後、左右の回転数も変化する事を確認しました。机上ではモータの制御はそれなりに上手く動いているようです。
制御確認

実動結果

タイヤも付けて実際に動かしてみました。
micro:bitやTA2917Pなどのブレッドボードを無理矢理載せた状態です。フォトインタラプタはmicro:bitの下にあり隠れています。電源はmicro:bit用とモーター用の2つが載ってます。
実体写真
送受信それぞれスクリプトをmain.pyとしてアップロードして実動作を確認しました。受信側のprintはコメントアウトしてます。
三輪のコマが動かない、滑るようで動作がパッとしません。そのため、コマを固定して動作させた動画です。

どこかが上手く動いていないような気もするのですが・・とりあえず動いてます。
もっと広いところなら、もっと早く、左右に曲がったり、回転など上手く動かせたのではないかと思います。

まとめ

micro:bitの無線機能を使って2WDカーを動作させました。前後、左右、速度調整の動作が出来たと思います。