稼働中

マイクロビット(e_39)MOVEminiをRadioで操作(1)

MOVE miniをRadio通信で操作1

もう一つのmicro:bitを無線送信用のコントローラーにして、MOVE miniをRadio通信で制御しようと思います。
micro:bitに搭載されているI2Cデバイスの加速度センサの値を使って、前後左右と各々の回転数を変化させます。また、ボタンを使って「開始、終了、Image表示」の副動作をしようと思います。

スクリプト

Move miniが前後左右に進む部分のスクリプトは前記事(e_38)とほぼ同じです。前記事を参照ください。
radioのchanne (デフォルト=7)、queue (デフォルト=3)などconfigはデフォルト状態で使用しています。
ThonnyのメニューからGeneral>「Allow only single Thonny instance」のチェックを外せば複数のThonnyを起動できます。Thonnyを2つ起動して送信用と受信用(MOVE mini)のスクリプトを評価できます。
スクリプト例は以下のようにしてみました。※thonny-microbitのMicroPythonを使っています。

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


radio_send_12-2b.py
from microbit import *
import radio

# 返信待ち 送信したら返信あるまで待機する
def RS():
    while True:
        r_msg = radio.receive()
        if r_msg:
            # from Move recieved
            print(r_msg)
            break
# PUSH Aボタンで開始、イメージ矢印を表示 A+Bで中止
while True:
    print('START Push A')
    display.show('A')    # 乾電池電源で単独動作の時に’A'表示あればわかりやすい
    sleep(1000)
    display.clear()
    sleep(500)
    if button_a.is_pressed() & button_b.is_pressed():
        display.clear()     # displayをクリアして中止
        print('A+B STOP')
        radio.send('B1')    # 開始してから停止させる(受信側の都合)
        sleep(10)
        radio.send('B3')    # 停止送信
        break               # 中止
    if button_a.is_pressed():
        print('GO')         # イメージ矢印を表示、頭文字+数文字'B1'送信
        display.show(Image.ARROW_N)
        radio.send('B1')    # 開始送信
        RS()                # 返信待
        break               # 待機から抜けて 操作 開始
# --MAIN--
radio.on()  # 無線をON    
cnt=0               # 送信した回数
btn='NON'
while True:
    # x:R(+)L(-) Y:B(+)F(-) Z:DN(+)UP(-)
    r_acc=accelerometer.get_values()        # accデータ値を取得

    if button_a.is_pressed() & button_b.is_pressed():
        btn='3'
    elif button_b.is_pressed():
        btn='2'        
    elif button_a.is_pressed():
        btn='1'                 # 数文字
    else :
        btn='NON'
    #print(cnt,msg,btn)
    # ボタン 
    if btn != 'NON':
        msg='B'+btn  # ボタン操作の頭文字’B’+数文字A(1)B(2)A+B(3) 
        radio.send(msg)
        cnt=cnt+1
        print('btn',cnt, btn, msg)
        if btn=='3':                 # A+Bで終了
            radio.off()
            display.clear()
            print('Break-END')
            break
        RS()        # 返信待
        sleep(200)  # 多重防止
    # accのx、y値の255以上で大きい方のデータを送信する
    # x値:r_acc[0] y値:r_acc[1]
    if (abs(r_acc[0])>=abs(r_acc[1])) & (abs(r_acc[0])>=255):
        x_da=r_acc[0]
        x_ms='X'+str(x_da)  # 左右に使うデータ
        radio.send(x_ms)
        cnt=cnt+1
        print(cnt,'x_ms',x_ms)
        RS()   # 返信待
    # 前<>後進    
    if (abs(r_acc[0])<abs(r_acc[1])) & (abs(r_acc[1])>=255):
        y_da=r_acc[1]
        y_ms='Y'+str(y_da)  #  前後に使うデータ
        radio.send(y_ms)
        cnt=cnt+1
        print(cnt,'y_ms',y_ms)
        RS()   # 返信待

受信側(MOVE mini)


move_radio_recieve_12-3b.py
from microbit import *
import radio

def SEP(sdat):                # 文字データの分離
    pfx=sdat[0]                # 頭文字
    dat=sdat[1: len(sdat)]     # 残りの文字(数文字)
    dat=int(dat)               # 数文字を整数値に
    return pfx,dat             # str,int
# pins
RS=pin1       # servo right(pin1)
LS=pin2       # servo left (pin2)
# right servo
def RSV(pwm):
    RS.set_analog_period(20)      #周期を設定 20msec
    RS.write_analog(pwm)
    #print('pwm_L %3d' %pwm)
# left servo
def LSV(pwm):
    LS.set_analog_period(20)      #周期を設定 20msec
    LS.write_analog(pwm)
    #print('pwm_L %3d' %pwm)
# pwm off
def M_STOP():
    RS.write_analog(0)
    LS.write_analog(0)
# neutral position pwm=77
N_pos=77
def M_NPOS():
    RSV(N_pos) #neutral
    LSV(N_pos) #neutral   
#----------------------------------------FRONT-BACKWARD
# 前進 
def M_FWD(DAT,tm,wt=0):
    LSV(N_pos+DAT[0]) # CW
    RSV(N_pos-DAT[1]) # CCW
    sleep(tm)  # move_time
    M_NPOS()
    sleep(wt)  # wait_time
# 後進
def M_BWD(DAT,tm,wt=0):
    LSV(N_pos-DAT[0]) # CW
    RSV(N_pos+DAT[1]) # CCW   
    sleep(tm) 
    M_NPOS()
    sleep(wt) 
#----------------------------------------FRONT TURN
# 前右回
def M_FTR(DAT,tm,wt=0):
    LSV(N_pos+DAT[0])
    #RSV(N_pos+DAT[1])
    sleep(tm)
    M_NPOS()
    sleep(wt)   
# 前左回
def M_FTL(DAT,tm,wt=0):
    #LSV(N_pos-DAT[0])
    RSV(N_pos-DAT[1])
    sleep(tm)
    M_NPOS()
    sleep(wt)
# dat=r_msg[1]>255  受信するacc-dataは255以上
def M_SVM(dat):
    t_dat=int(abs(800/1023*dat))        # accデータからサーボ動作時間
    d_pwm=int((abs(dat)-255)/96 + 8 )   # accデータからサーボPWM値
    s_pwm=(d_pwm-1,d_pwm)   # 補正 L-R=1
    print(t_dat,s_pwm)      # 動作時間、左右pwm値
    return t_dat,s_pwm

# --MAIN--
radio.on()
M_STOP()
M_NPOS()   # Neutral
sleep(1000)
print('START')
cnt=0
while True: # ボタンAが押されるまで待機
    r_msg = radio.receive()
    print('Ready A',r_msg)
    sleep(2000)
    if r_msg=='B1': # Aボタン押のデータ'B1'受信
        s_msg='recived'
        radio.send(s_msg)           # 返信
        display.show(Image.ARROW_N) # イメージ矢印を表示
        break                       # 待機を抜けて動作開始
while True:    
    r_msg = radio.receive()
    if r_msg:
        cnt=cnt+1
        d_msg=SEP(r_msg)
        print(cnt,d_msg)
        # Button
        if d_msg[0]=='B':
            print('button')
            if d_msg[1]==3:   #'C'(3)
                print('buttonA+B END')
                M_STOP()
                radio.off()
                display.clear()
                break
            if d_msg[1]==2:   #'B'(2)
                print('buttonB')
                display.clear()
            if d_msg[1]==1:   #'A'(1)
                    print('buttonA')
                    display.show(Image.HAPPY)
        # 回転    頭文字が’X'の場合
        if d_msg[0]=='X':
            (t_dat,d_pwm)=M_SVM(d_msg[1]) # d_msg[1]はaccデータで整数
            if int(d_msg[1])>0:
                print('右')
                M_FTR(d_pwm,t_dat,0)
            else:
                print('左')
                M_FTL(d_pwm,t_dat,0)
        # 前後進   頭文字が’Y'の場合
        if d_msg[0]=='Y':
            (t_dat,d_pwm)=M_SVM(d_msg[1]) # d_msg[1]はaccデータで整数
            if d_msg[1]<0:
                print('前')
                M_FWD(d_pwm,t_dat,0)
            else:
                print('後')
                M_BWD(d_pwm,t_dat,0)
        # 返信
        s_msg='recived'
        radio.send(s_msg)
    else:
        pass

スクリプトの説明

スクリプトの部分説明です。
以下の部分はサーボの動作時間とPWM値を送信されたaccデータから作成しています。


def M_SVM(dat):
    t_dat=int(abs(800/1023*dat))        # accデータからサーボ動作時間
    d_pwm=int((abs(dat)-255)/96 + 8 )   # accデータからサーボPWM値
    s_pwm=(d_pwm-1,d_pwm)   # 補正 L-R=1
    print(t_dat,s_pwm)      # 動作時間、左右pwm値
    return t_dat,s_pwm

t_dat:サーボ動作時間
送信されるaccデータは255以上なので、動作時間は199(800/1023*255)から800msecの値になります。
d_pwm:左右のサーボに送るPWM値
速度を変化させるために前記事のグラフから8~16値を使うようにしました。送信されるaccデータは255~1023に対して、PWM値が8~16になるようにしています。
左右のサーボ回転速度が違うので s_pwm=(d_pwm-1,d_pwm)で調整しています。
それでもかなりズレます。ピッタリにはなりません。同じような特性のサーボをペアにした方がよいと思います。

実行結果

Thonnyのshellに表示された結果です。

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


>>> %Run radio_send_12-2b.py
START Push A
START Push A
GO                  # A ボタンでスタート
recived             # 返信を受ける・・次送信が可能になる
1 x_ms X256         # 送1 左右データ 右
recived
2 x_ms X352
recived
3 x_ms X-256        # 送3 左右データ 左
recived
4 x_ms X-304
recived
5 y_ms Y256         # 送5 前後データ 左
recived
6 y_ms Y272
recived
btn 7 2 B2          # 送7 ボタン B
recived
btn 8 3 B3          # 送8 ボタン A+B
Break-END           # 停止
>>> 

受信側(MOVE mini)


>>> %Run move_radio_recieve_12-3b.py
START
Ready A None
Ready A None
Ready A B1          # 受 A ボタンでスタート
1 ('X', 256)        # 受1 左右データ 右
200 (7, 8)
右
2 ('X', 352)
275 (8, 9)
右
3 ('X', -256)       # 受3 左右データ 左
200 (7, 8)
左
4 ('X', -304)
237 (7, 8)
左
5 ('Y', 256)
200 (7, 8)
後
6 ('Y', 272)
212 (7, 8)
後
7 ('B', 2)          # 受7 ボタン B
button
buttonB
8 ('B', 3)          # 受8 ボタン A+B
button
buttonA+B END       # 停止
>>> 

※メモリーのエラーが発生する場合、ThonnyのメニューからDevice>soft reboot後に実行すると解消できることがあります。

動作確認の動画

手元、パソコンにつないだままで動作確認をしました。コントローラとサーボの動作です。まあまあ動いてます。

実動作の動画

電池駆動で独立させて動かしてみます。
送信側(コントローラー)、受信側(MOVE mini)のスクリプトを各々main.pyとしてuploadします。
micro:bitにuploadするにはThonnyのメニューのDevice>Upload current script as main scriptを選択します。
実際に床面で動かしてみました。Move miniの動画です。

車輪がやや滑り気味です。前進はやっぱり曲がってますが、後進はそこそこまっすぐ進みました。
回転などは思ったより上手く動いてます。

まとめ

micro:bitのRadio通信機能を使ってMOVE miniのリモート動作が出来ました。電池が消耗するとピタッと動かなくなります。壊れてはいないので、動かない時は電池交換して見るとよいと思います。前後左右、回転、簡単な動作しかできませんが、実際に動くと面白いです。