M5Stackで”2足歩行ロボット スタックチャン”
「ミニサイズのサーボモーター」「ミニサイズのサーボドライバ」を手に入れる事が出来ましたので、M5Stackを使って2足歩行できるスタックチャンを作ってみました。
ハードウェア構成
・M5Stack
・サーボモーター × 5
・miniサーボモータドライバ(※PONDAさん作)
・Lipo BT
M5Stack ⇒ i2c ⇒ サーボドライバ ⇒ サーボモーター の構成にします。
miniサーボドライバは、@PONDA_makeさんのお手製サーボドライバを使用しています。
こちら、PCA9685の半分のサイズにしてなんと接続ch数変わらず16chです。素晴らしいです、市販の製品には無かったので非常に助かりました。
ソフトウェア
開発言語はmicropythonを使います。
ロボットの操作については、別途コントローラー( 今回はM5Stack core2 )を用意し、遠隔で操作出来るようにします。
通信手段にはUDP通信を使います。
core2 ⇒ UDP ⇒ M5stack
ボディ部の印刷、部品の格納
ボディの部分をCADでスケッチし、3Dプリンターで印刷、主要部品を格納します。
足の印刷と組立
プログラム
M5Stack core2(コントローラー側)
from m5stack_ui import *
from socket import socket, AF_INET, SOCK_DGRAM
import time
import network
from m5stack import *
#from m5stack import lcd
from machine import Pin
from uiflow import *
screen = M5Screen()
screen.clean_screen()
screen.set_screen_bg_color(0xFFFFFF)
dstip = "m5stackのIP"
dstport = ポート番号
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network...')
wlan.connect('SSID', 'PASS')
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
print('network config:', wlan.ifconfig())
s = socket(AF_INET, SOCK_DGRAM)
#s.bind((dstip, dstport))
touch_button0 = M5Btn(text='Button', x=110, y=10, w=100, h=80, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
touch_button1 = M5Btn(text='Button', x=10, y=80, w=100, h=80, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
touch_button2 = M5Btn(text='Button', x=210, y=80, w=100, h=80, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
touch_button3 = M5Btn(text='Button', x=110, y=150, w=100, h=80, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
touch_button4 = M5Btn(text='Button', x=116, y=100, w=40, h=40, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
touch_button5 = M5Btn(text='Button', x=165, y=100, w=40, h=40, bg_c=0xFFFFFF, text_c=0x000000, font=FONT_MONT_14, parent=None)
def touch_button0_pressed():
global data
data = "F"
def touch_button3_pressed():
global data
data = "B"
def touch_button1_pressed():
global data
data = "L"
def touch_button2_pressed():
global data
data = "R"
#touch_button0.pressed(touch_button0_pressed)
# ボタン"A" が「離された」時の処理
def touch_button0_released():
global data
data = "S"
def touch_button4_pressed():
global data
data = "on"
def touch_button5_pressed():
global data
data = "off"
#data = touch_button0.released(touch_button0_released)
data = "nane"
stamp = 0
while(True):
#global data
touch_button0.pressed(touch_button0_pressed)
touch_button1.pressed(touch_button1_pressed)
touch_button2.pressed(touch_button2_pressed)
touch_button3.pressed(touch_button3_pressed)
touch_button4.pressed(touch_button4_pressed)
touch_button5.pressed(touch_button5_pressed)
#data = "A"
touch_button0.released(touch_button0_released)
touch_button1.released(touch_button0_released)
touch_button2.released(touch_button0_released)
touch_button3.released(touch_button0_released)
s.sendto(data, (dstip, dstport))
print(data)
#print(Slider2.get_value())
time.sleep(0.1)
操作ボタンを配置し、ボタン押下でボタンに紐づいた文字列をUDPでM5Stack側へ送信しています。
M5Stack側(本体側)
from socket import socket, AF_INET, SOCK_DGRAM
import time
import network
from machine import Pin, I2C
import pca9685
import servo
import _thread
from m5stack import *
from m5stack import lcd
#from machine import Pin
posi = {"lx":90, "ly":90, "lr":10, "rx":230, "ry":90, "rr":10, "ux":110, "uy":170}
lcd.clear(lcd.BLACK)
lcd.circle(posi["lx"], posi["ly"], posi["lr"], lcd.WHITE, lcd.WHITE)
lcd.circle(posi["rx"], posi["ry"], posi["rr"], lcd.WHITE, lcd.WHITE)
lcd.rect(posi["ux"], posi["uy"], 100, 5, lcd.WHITE, lcd.WHITE)
a = 92
b = 83
c = 83
d = 95
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
sev = servo.Servos(i2c)
#p0 = Pin(19, Pin.OUT)
#p1 = Pin(23, Pin.OUT)
HOST = "IP"
PORT = ポート番号
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network...')
wlan.connect('SSID', 'PASS')
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
s = socket(AF_INET, SOCK_DGRAM)
s.bind((HOST, PORT))
print("bind")
msg = "nane"
def recieve():
while(True):
global msg
msg = s.recv(64)
msg = msg.decode()
print(msg)
time.sleep(0.01)
_thread.start_new_thread(recieve, ())
while(True):
if msg =="F":
while(b <= 125):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 125:
d = d + 1
while(c >= 75):
sev.position(2, c)
time.sleep(0.009)
c = c - 1
while(b >= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b - 1
if d >= 95:
d = d - 1
while(c <= 95):
sev.position(2, c)
time.sleep(0.009)
c = c + 1
while(d >= 65):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
d = d - 1
if b >= 65:
b = b - 1
while(a <= 110):
sev.position(0, a)
time.sleep(0.009)
a = a + 1
while(b <= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 95:
d = d + 1
while(a >= 90):
sev.position(0, a)
time.sleep(0.009)
a = a - 1
elif msg == "B":
while(b <= 125):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 125:
d = d + 1
while(c <= 115):
sev.position(2, c)
time.sleep(0.009)
c = c + 1
while(b >= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b - 1
if d >= 95:
d = d - 1
while(c >= 95):
sev.position(2, c)
time.sleep(0.009)
c = c - 1
while(d >= 65):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
d = d - 1
if b >= 65:
b = b - 1
while(a >= 70):
sev.position(0, a)
time.sleep(0.009)
a = a - 1
while(b <= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 95:
d = d + 1
while(a <= 90):
sev.position(0, a)
time.sleep(0.009)
a = a + 1
elif msg == "L":
while(d >= 65):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
d = d - 1
if b >= 65:
b = b - 1
while(a >= 70):
sev.position(0, a)
time.sleep(0.009)
a = a - 1
while(b <= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 95:
d = d + 1
while(a <= 90):
sev.position(0, a)
time.sleep(0.009)
a = a + 1
elif msg == "R":
while(b <= 125):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b + 1
if d <= 125:
d = d + 1
while(c <= 115):
sev.position(2, c)
time.sleep(0.009)
c = c + 1
while(b >= 95):
sev.position(1, b)
sev.position(3, d)
time.sleep(0.009)
b = b - 1
if d >= 95:
d = d - 1
while(c >= 95):
sev.position(2, c)
time.sleep(0.009)
c = c - 1
time.sleep(0.01)
UDPで文字列を受信するループと、サーボを駆動させるループを並列で処理させています。(まとめて処理させると遅延が発生する為)
ちなみに、pca9685のライブラリを使っているので、M5Stackのフラッシュにpca9685関連のファイルを保存させておく必要があります。ファイルはgitで公開されています。
余談
歩行テスト時に、足が全く動かないトラブルに見舞われました。
宙に浮かせると動くのですが、床に置くと動かない。。
結論から言えば、もともと足のサーボの軸を本体の外側に配置していたので、重心を外れて力が伝わりづらかったんですよね。こんな感じで、だいぶ外れてました(;^ω^)↓
これの軸を中心に持ってくるように修正した所、足に動力が伝わるようになりました。
完成(仮)
これでほぼ完成なのですが、ツートンカラーがあれなので、ケースを再度印刷し直し、ブラックでペイントします。
パーツの再印刷
完成です。
2足歩行スタックチャン
ブラックスタックチャンの完成です!(^▽^)/
しかし、少し味気ないので、ここからもう一つ、改良を加えたいと思います。
このスタックチャンに少しでもオリジナリティを持たせるために、「メガネ」をかけさせてみようと思います。なんか嫌な予感はするのですが。。
2足歩行でもすでに先駆者がいますので、そこを創造的模範といいますか、同じじゃないですよと言いたいがためでもあります。
それでは、2足歩行メガネスタックチャンを、ご覧ください。
まさかの変態仮面
ブラジャーにしか見えない。。
イヤな予感は想像の斜め上をいきました。
サポート取ってないせいもあるのですが、それを差し引いてもやはりこれはブラジャーにしか見えません。。
こんなはずじゃなかった。。。
対策を考えます
サングラスにしてみる
なんとか逃げ切れました?(;^ω^)多分OKでしょう。
しかし色加えるだけでこうも印象が変わるんですね。メガネって怖いですね。。
それでは最後に、メガネスタックチャンを歩かせて、終了としたいと思います。
以上です。
ありがとうございました。
投稿者の人気記事
-
Ketunorobio
さんが
2022/08/07
に
編集
をしました。
(メッセージ: 初版)
-
Ketunorobio
さんが
2022/08/07
に
編集
をしました。
ログインしてコメントを投稿する