# 第一章 Cyber Pi 控制 Dobot 機械手臂操作指南
示意圖

透過兩個Cyber Pi將其中一個Cyber Pi
## 目標
本教程旨在幫助老師和學生快速了解如何使用 Cyber Pi 控制 Dobot 機械手臂,包括移動指令、吸盤控制和回到原點等功能。
## 接線
利用四條杜邦線將兩者進行連接,兩條電源線,一條寫入一條讀取,完成操作反饋


## Cyber Pi控制Dobot程式碼
```python
from machine import UART # 用來控制 UART
import cyberpi, time, struct # 需要 struct 來轉換 float
# 設定 UART 串口
uart1 = UART(1, baudrate=115200, tx=15, rx=21) # 如果你接的 TX/RX 沒變,則不需要更改
# 預設座標
Sx = 260
Sy = 0
Sz = -8.5
Sr = 0
# 主功能:傳送一個 PTP 點位移動指令
def MoveToPoint(x, y, z, r):
UartCommand = [170, 170] # 固定開頭 Header,不用改
length = 19 # 資料長度固定為 19 bytes
ID = 84 # 指令 ID = 84 表示 PTP 點位移動
Ctrl = 3 # 控制模式 3 表示立即執行
ptpmode = 1 # PTP 模式,1 為線性運動(Linear),可改為 0 看需求
# 將 x/y/z/r 轉成 4-byte IEEE754 格式,並反轉為小端序
Dx = list(struct.pack("!f", x))
Dy = list(struct.pack("!f", y))
Dz = list(struct.pack("!f", z))
Dr = list(struct.pack("!f", r))
payload = []
payload.append(ID) # 不用改
payload.append(Ctrl) # 不用改
payload.append(ptpmode) # 可改為其他移動模式
payload.extend(Dx[::-1]) # x 座標(4 bytes)
payload.extend(Dy[::-1]) # y 座標(4 bytes)
payload.extend(Dz[::-1]) # z 座標(4 bytes)
payload.extend(Dr[::-1]) # r 角度(4 bytes)
Checksum = 256 - sum(payload) & 0xff # 計算 Checksum
UartCommand.append(length) # 將長度放進封包
UartCommand.extend(payload) # 將 payload 放進封包
UartCommand.append(Checksum) # 將 Checksum 放進封包
uart1.write(bytes(UartCommand)) # 傳送完整指令封包
# 吸盤控制
def suck_on():
# 吸盤打開
UartCommand = [170, 170, 4, 63, 3, 1, 1, 188]
uart1.write(bytes(UartCommand))
def suck_off():
# 吸盤關閉
UartCommand = [170, 170, 4, 63, 3, 0, 0, 190]
uart1.write(bytes(UartCommand))
# 夾爪控制
def gripper_open():
# 夾爪張開(值 = 500)
UartCommand = [170, 170, 6, 62, 3, 1, 244, 1, 9]
uart1.write(bytes(UartCommand))
def gripper_close():
# 夾爪閉合(值 = 0)
UartCommand = [170, 170, 6, 62, 3, 1, 0, 0, 11]
uart1.write(bytes(UartCommand))
# 回到原點
def home():
cyberpi.console.print('homing')
# homing commend
data1 = [170,170, 6, 31,3,0,0,0,0,222]
uart1.write(bytes(data1))
Sx = 260
Sy = 0
Sz = -8.5
Sr = 0
time.sleep(1)
# 主循環:控制按鈕事件
while True:
if cyberpi.controller.is_press('up'): #按鈕按下
cyberpi.console.println("u") #顯示模式
time.sleep(.2)
MoveToPoint(200, 50, -10, 0) # A 到 B
time.sleep(2) # 停 2 秒
MoveToPoint(250, 0, -8.5, 0) # B 到 A
time.sleep(2) # 停 2 秒
MoveToPoint(200, 50, -10, 0) # 再次 A 到 B
if cyberpi.controller.is_press('a'):
cyberpi.console.println("s1")
suck_on()
time.sleep(1) # 吸住 1 秒
suck_off()
time.sleep(1) # 放開 1 秒
if cyberpi.controller.is_press('middle'):
cyberpi.console.println("home_")
home()
```
## 加入廣播控制Dobot
之前廣播用積木的型態不好帶入python程式中
因此將其簡單整理成收與發
首先是發送訊息的部分相對簡單,用elif可以疊加訊息發送
### 發送訊息
```python
import event, time, cyberpi
@event.start
def on_start():
# 嘗試連接 WIFI(請填入名稱與密碼)
cyberpi.wifi.connect("", "")
# 設定控制台字體大小,顯示提示文字
cyberpi.console.set_font(12)
cyberpi.console.println("連線成功")
while True:
if cyberpi.controller.is_press('up'):
cyberpi.console.println("up")
cyberpi.mesh_broadcast.set("message", 1)
elif cyberpi.controller.is_press('down'):
cyberpi.console.println("down")
cyberpi.mesh_broadcast.set("message", 2)
elif cyberpi.controller.is_press('left'):
cyberpi.console.println("left")
cyberpi.mesh_broadcast.set("message", 3)
elif cyberpi.controller.is_press('right'):
cyberpi.console.println("right")
cyberpi.mesh_broadcast.set("message", 4)
else:
cyberpi.mesh_broadcast.set("message", 0) # 沒有方向鍵按下
time.sleep(0.05) # 每 50 毫秒檢查一次
```
接下來
目前將dobot手臂動作加入,讓遠端將訊息傳入後就能夠實際做出相關動作了。
### 接收訊息
```python=
import event, time, cyberpi,mbot2
# 啟動時執行的程式碼
@event.start
def on_start():
cyberpi.wifi.connect("", "") # 請填入 Wi-Fi 名稱與密碼
cyberpi.console.set_font(12)
cyberpi.console.println("等待遙控")
# 當接收到 mesh 廣播訊息時執行的程式碼
@cyberpi.event.mesh_broadcast("message")
def on_mesh_broadcast():
msg = cyberpi.mesh_broadcast.get("message")
if msg == 1:
# 收到訊息1時要執行的動作
mbot2.forward(50)
pass
elif msg == 2:
# 收到訊息2時要執行的動作
mbot2.backward(50)
pass
elif msg == 3:
# 收到訊息3時要執行的動作
mbot2.turn_left(50)
pass
elif msg == 4:
# 收到訊息4時要執行的動作
mbot2.turn_right(50)
pass
else:
mbot2.forward(0)
# 收到其他訊息時要執行的動作
pass
```
最後是合成疊加,將dobot訊息整合進入接收訊息端
因此需要將接收端進行小部分份修改
## AI鏡頭結合dobot機械手臂實現遠端控制
### AI鏡頭設定
1. 連接AI鏡頭

2. 選擇色塊辨識模式(左下角Blob)

3. 進入學習模式

4.點選物件=>直到綠色框框完全選擇在要學習的物件由上繳的由上的筆按鈕,學習成功右下方會多一個辨識號碼,如圖就是將新的黃色標註為ID3,註記成第三個編號,程式要使用時,只需要使用3號標註,就是剛剛學習的顏色

### 從AI鏡頭 發送訊號
不過目前AI鏡頭只能用簡體版本的線上介面操控之後,如果可以應該會將繁體版如何使用一起加入
```python=
import event, time, cyberpi, mbuild
# initialize variables
flag = 0
@event.start
def on_start():
global flag
cyberpi.console.print("makeblock")
cyberpi.wifi.connect("JY-Office", "80061719")
cyberpi.led.on(208, 2, 27, "all")
while not cyberpi.wifi.is_connect():
# DO SOMETHING
pass
cyberpi.led.on(108, 208, 1, "all")
@event.is_press('a')
def is_btn_press():
global flag
# DO SOMETHING
pass
@event.is_press('b')
def is_btn_press1():
global flag
flag = 0
mbuild.ai_camera.ai_camera_set_func_switch(3, 1)
while not False:
cyberpi.table.add(1, 2, flag)
if mbuild.ai_camera.ai_camera_color_spatial_attribute_get(1, 2, 1) < -300 or mbuild.ai_camera.ai_camera_color_color_get(1, 1) == -1:
flag = 0
cyberpi.table.add(1, 1, "離開")
cyberpi.mesh_broadcast.set("message", 0)
if flag == 0:
if mbuild.ai_camera.ai_camera_color_color_get(1, 1) == 1:
cyberpi.table.add(1, 1, "紅色")
flag = 1
cyberpi.mesh_broadcast.set("message", 1)
else:
if mbuild.ai_camera.ai_camera_color_color_get(1, 1) == 2:
cyberpi.table.add(1, 1, "藍色")
flag = 1
cyberpi.mesh_broadcast.set("message", 2)
else:
if mbuild.ai_camera.ai_camera_color_color_get(1, 1) == 3:
cyberpi.table.add(1, 1, "黃色")
flag = 1
cyberpi.mesh_broadcast.set("message", 3)
else:
if mbuild.ai_camera.ai_camera_color_color_get(1, 1) == 4:
cyberpi.table.add(1, 1, "綠色")
cyberpi.mesh_broadcast.set("message", 4)
flag = 1
```
### 接收到訊息時的畫面
```python=
# 區域網路壞掉一定要連wifi
from machine import UART
import event, time, cyberpi,mbot2,struct
uart1 = UART(1, baudrate=115200, tx=15, rx=21) # ✅ 不用改(如果你接的 TX/RX 沒變)
# ✅ 預設座標:你可以改,也可以直接在 MoveToPoint 裡傳參數
# ✅ 主功能:傳送一個 PTP 點位移動指令
def MoveToPoint(x, y, z, r):
UartCommand = [170, 170] # ✅ 固定開頭 Header,不用改
length = 19 # ✅ 資料長度固定為 19 bytes
ID = 84 # ✅ 指令 ID = 84 表示 PTP 點位移動,不用改
Ctrl = 3 # ✅ 控制模式 3 表示立即執行
ptpmode = 1 # ✅ PTP 模式,1 為線性運動(Linear),可改為 0 看需求
# ✅ 將 x/y/z/r 轉成 4-byte IEEE754 格式,並反轉為小端序
Dx = list(struct.pack("!f", x)) # ✅ 轉 x
Dy = list(struct.pack("!f", y)) # ✅ 轉 y
Dz = list(struct.pack("!f", z)) # ✅ 轉 z
Dr = list(struct.pack("!f", r)) # ✅ 轉 r
# 🟡 除錯用,顯示轉換後的資料,可以刪除
print(Dx[::-1])
print(Dy[::-1])
print(Dz[::-1])
print(Dr[::-1])
payload = []
payload.append(ID) # ✅ 不用改
payload.append(Ctrl) # ✅ 不用改
payload.append(ptpmode) # ✅ 可改為其他移動模式
payload.extend(Dx[::-1]) # ✅ x 座標(4 bytes)
payload.extend(Dy[::-1]) # ✅ y 座標(4 bytes)
payload.extend(Dz[::-1]) # ✅ z 座標(4 bytes)
payload.extend(Dr[::-1]) # ✅ r 角度(4 bytes)
Checksum = 256 - sum(payload) & 0xff # ✅ 計算 Checksum,不用改
UartCommand.append(length) # ✅ 將長度放進封包
UartCommand.extend(payload) # ✅ 將 payload 放進封包
UartCommand.append(Checksum) # ✅ 將 Checksum 放進封包
uart1.write(bytes(UartCommand)) # ✅ 傳送完整指令封包
def suck_on():
# 吸盤打開
UartCommand = [170, 170, 4, 63, 3, 1, 1, 188]
uart1.write(bytes(UartCommand))
def suck_off():
# 吸盤關閉
UartCommand = [170, 170, 4, 63, 3, 0, 0, 190]
uart1.write(bytes(UartCommand))
def home():
cyberpi.console.print('homing')
# homing commend
data1 = [170,170, 6, 31,3,0,0,0,0,222]
uart1.write(bytes(data1))
Sx=260;Sy=0;Sz=-8.5;Sr=0
time.sleep(1)
# 啟動時執行的程式碼
@event.start
def on_start():
cyberpi.wifi.connect("JY-Office", "80061719") # 請填入 Wi-Fi 名稱與密碼
cyberpi.console.set_font(12)
# 按下A按鈕自動回歸原點
@event.is_press('a')
def is_btn_press():
home()
# 當接收到 mesh 廣播訊息時執行的程式碼
@cyberpi.event.mesh_broadcast("message")
def on_mesh_broadcast():
msg = cyberpi.mesh_broadcast.get("message")
if msg == 1:
# 收到訊息1時要執行的動作
cyberpi.console.println("red") #顯示模式
time.sleep(.2)
MoveToPoint(195.4, 28.7, 36.8, 0)
time.sleep(.2)
MoveToPoint(163.2, 21.9, 4.35, 0)
time.sleep(.2)
MoveToPoint(198.1, 31.7, -4.1, 0)
time.sleep(.2)
MoveToPoint(206.3, 32.8, 55.5, 0) #
time.sleep(.2)
MoveToPoint(67, -206, 20, 0)
# 第五步移動到手臂右邊
time.sleep(.2)
MoveToPoint(55.1, -215.9, -79, 0)
time.sleep(.2)
MoveToPoint(43.8, -180.8, -62.1, 0)
time.sleep(.2)
MoveToPoint(195.4, 28.7, 36.8, 0)
time.sleep(.2)
elif msg == 2:
# 收到訊息2時要執行的動作
cyberpi.console.println("blue")
time.sleep(.2)
MoveToPoint(195.4, 28.7, 36.8, 0)
time.sleep(.2)
MoveToPoint(163.2, 21.9, 4.35, 0)
time.sleep(.2)
MoveToPoint(198.1, 31.7, -4.1, 0)
time.sleep(.2)
MoveToPoint(206.3, 32.8, 55.5, 0)
time.sleep(.2)
MoveToPoint( 119, 163, 38, 0)
time.sleep(.2)
MoveToPoint(25, 199, 37, 0)
time.sleep(.2)
MoveToPoint(21, 211, -74, 0)
time.sleep(.2)
MoveToPoint(23, 179, -60, 0)
time.sleep(.2)
MoveToPoint(23, 179, -60, 0)
time.sleep(.2)
elif msg == 3:
# 收到訊息3時要執行的動作
cyberpi.console.println("yellow")
elif msg == 4:
# 收到訊息4時要執行的動作
cyberpi.console.println("green")
else:
# 收到其他訊息時要執行的動作
pass
```
這樣只差最後一步,就是將發送端的部分將AI程式合成進來後,讓dobot進行遠端接收,就可以實現,遠端智慧物流的概念了
## 最新整合
### 手臂一程式
```python
from machine import UART
import cyberpi,time,struct,gamepad,mbuild
uart1 = UART(1, baudrate=115200, tx=15, rx=21)
Sx=260;Sy=0;Sz=0;Sr=0
def MoveToPoint(x,y,z,r):
UartCommand=[170,170]
length=19
ID=84
Ctrl=3
ptpmode=1
# Home 260,0,-8.5,0
# x = 200
Dx=list(struct.pack("!f",x)) # 32float to 4 decimel list
print(Dx[::-1]) # to reverse
# y = 0
Dy=list(struct.pack("!f",y))
print(Dy[::-1])
# z = -8.5
Dz=list(struct.pack("!f",z))
print(Dz[::-1])
# r= 0
Dr=list(struct.pack("!f",r))
print(Dx[::-1])
payload=[]
payload.append(ID)
payload.append(Ctrl)
payload.append(ptpmode)
payload.extend(Dx[::-1])
payload.extend(Dy[::-1])
payload.extend(Dz[::-1])
payload.extend(Dr[::-1])
# print(payload)
Checksum=256 - sum(payload) & 0xff
# print(Checksum)
UartCommand.append(length)
UartCommand.extend(payload)
UartCommand.append(Checksum)
# print(UartCommand)
uart1.write(bytes(UartCommand))
# c=uart1.read()
# cyberpi.console.println(x,y,z,r)
while True :
suck=False
if gamepad.is_key_pressed('Select'):
cyberpi.console.print('homing')
# homing commend
data1 = [170,170, 6, 31,3,0,0,0,0,222]
uart1.write(bytes(data1))
Sx=260;Sy=0;Sz=0;Sr=0
time.sleep(1)
elif gamepad.get_joystick('Rx')==100:
cyberpi.console.print('x')
Sr-=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Rx')==-100:
cyberpi.console.print('o')
Sr+=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Ry')==100:
cyberpi.console.print('-')
Sz-=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Ry')==-100:
cyberpi.console.print('+')
Sz+=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Lx')==100: # 檢測是否按下按鍵
cyberpi.console.print('>')
Sy-=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Lx')==-100:
cyberpi.console.print('<')
Sy+=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Ly')==100:
cyberpi.console.print('^')
Sx+=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.get_joystick('Ly')==-100:
cyberpi.console.print('v')
Sx-=5
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.is_key_pressed('L1'):
UartCommand=[170, 170, 4, 63, 3, 0, 0, 190] # unsuck
uart1.write(bytes(UartCommand))
suck=False
cyberpi.console.print('unsuck')
elif gamepad.is_key_pressed('R1'):
UartCommand=[170, 170, 4, 63, 3, 1, 1, 188] # suck
uart1.write(bytes(UartCommand))
suck=True
cyberpi.console.print('suck')
elif gamepad.is_key_pressed('Start'):#加號按鈕向左
uart1.write(bytes([170, 170, 8, 135, 3, 0, 1, 16, 39, 0, 0, 62]))
time.sleep(7.2)
uart1.write(bytes([170, 170, 8, 135, 3, 0, 1, 0, 0, 0, 0, 117])) # on輸送帶停下
elif gamepad.is_key_pressed('N1'):#原點1
Sx=233;Sy=-37;Sz=-17;Sr=0
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.is_key_pressed('N2'):#原點2
Sx=233;Sy=-18;Sz=-17;Sr=0
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.is_key_pressed('N3'):#原點3
Sx=233;Sy=1;Sz=-17;Sr=0
MoveToPoint(Sx,Sy,Sz,Sr)
elif gamepad.is_key_pressed('N4'):#過去
Sx=22;Sy=247;Sz=14;Sr=0
MoveToPoint(Sx,Sy,Sz,Sr)
```
### 手臂二程式
```python=
# 區域網路壞掉一定要連wifi
from machine import UART
import event, time, cyberpi,mbot2,struct
uart1 = UART(1, baudrate=115200, tx=15, rx=21) # ✅ 不用改(如果你接的 TX/RX 沒變)
# ✅ 預設座標:你可以改,也可以直接在 MoveToPoint 裡傳參數
# ✅ 主功能:傳送一個 PTP 點位移動指令
def MoveToPoint(x, y, z, r):
UartCommand = [170, 170] # ✅ 固定開頭 Header,不用改
length = 19 # ✅ 資料長度固定為 19 bytes
ID = 84 # ✅ 指令 ID = 84 表示 PTP 點位移動,不用改
Ctrl = 3 # ✅ 控制模式 3 表示立即執行
ptpmode = 1 # ✅ PTP 模式,1 為線性運動(Linear),可改為 0 看需求
# ✅ 將 x/y/z/r 轉成 4-byte IEEE754 格式,並反轉為小端序
Dx = list(struct.pack("!f", x)) # ✅ 轉 x
Dy = list(struct.pack("!f", y)) # ✅ 轉 y
Dz = list(struct.pack("!f", z)) # ✅ 轉 z
Dr = list(struct.pack("!f", r)) # ✅ 轉 r
# 🟡 除錯用,顯示轉換後的資料,可以刪除
print(Dx[::-1])
print(Dy[::-1])
print(Dz[::-1])
print(Dr[::-1])
payload = []
payload.append(ID) # ✅ 不用改
payload.append(Ctrl) # ✅ 不用改
payload.append(ptpmode) # ✅ 可改為其他移動模式
payload.extend(Dx[::-1]) # ✅ x 座標(4 bytes)
payload.extend(Dy[::-1]) # ✅ y 座標(4 bytes)
payload.extend(Dz[::-1]) # ✅ z 座標(4 bytes)
payload.extend(Dr[::-1]) # ✅ r 角度(4 bytes)
Checksum = 256 - sum(payload) & 0xff # ✅ 計算 Checksum,不用改
UartCommand.append(length) # ✅ 將長度放進封包
UartCommand.extend(payload) # ✅ 將 payload 放進封包
UartCommand.append(Checksum) # ✅ 將 Checksum 放進封包
uart1.write(bytes(UartCommand)) # ✅ 傳送完整指令封包
def suck_on():
# 吸盤打開
UartCommand = [170, 170, 4, 63, 3, 1, 1, 188]
uart1.write(bytes(UartCommand))
def suck_off():
# 吸盤關閉
UartCommand = [170, 170, 4, 63, 3, 0, 0, 190]
uart1.write(bytes(UartCommand))
def home():
cyberpi.console.print('homing')
# homing commend
data1 = [170,170, 6, 31,3,0,0,0,0,222]
uart1.write(bytes(data1))
Sx=260;Sy=0;Sz=-8.5;Sr=0
time.sleep(1)
# 啟動時執行的程式碼
@event.start
def on_start():
cyberpi.wifi.connect("JY-SE", "80061719") # 請填入 Wi-Fi 名稱與密碼
cyberpi.console.set_font(12)
cyberpi.console.println("press A go home")
cyberpi.console.println("press B test")
@event.is_press('a')
def is_btn_press():
home()
@event.is_press('b')
def is_btn_press1():
cyberpi.console.println("test")
MoveToPoint(222,-17,38,0)#原點
MoveToPoint(-6.3,-263,37.6,0)#輸送帶上方
MoveToPoint(-6.6,-262,3,0)#輸送帶
suck_on()
time.sleep(2)
MoveToPoint(-6.3,-263,37.6,0)#輸送帶上方
MoveToPoint(-3,241,76,0)#車子上方
MoveToPoint(-3,241,45,0)#車子
suck_off()
MoveToPoint(-3,241,76,0)#車子上方
MoveToPoint(222,-17,38,0)#原點
time.sleep(5)
# 當接收到 mesh 廣播訊息時執行的程式碼
@cyberpi.event.mesh_broadcast("message")
def on_mesh_broadcast():
msg = cyberpi.mesh_broadcast.get("message")
if msg == 1:
# 收到訊息1時要執行的動作
cyberpi.console.println("go") #顯示模式
MoveToPoint(222,-17,38,0)#原點
MoveToPoint(-6.3,-263,37.6,0)#輸送帶上方
MoveToPoint(-6.6,-262,3,0)#輸送帶
suck_on()
time.sleep(2)
MoveToPoint(-6.3,-263,37.6,0)#輸送帶上方
MoveToPoint(-3,241,76,0)#車子上方
MoveToPoint(-3,241,45,0)#車子
suck_off()
MoveToPoint(-3,241,76,0)#車子上方
MoveToPoint(222,-17,38,0)#原點
time.sleep(5)
cyberpi.console.println('finish_get_basket')
```
### 位置擺放
好的程式也必須搭配正確的座標位置,例如手臂與位置和每個物件
要做的事情都必須做出一些正確的搭配,先來看整體規劃
#### 整體位置圖

#### 第一支手臂
對齊左下角1-B位置

#### 貨物初始位置
對齊2-B左上角整齊擺放

#### 輸送帶位置
左邊的腳對齊F2 G2之間的線

#### 第二支手臂位置
手臂位在H-2位置

#### 最後是mbot2擺放位置
車頭面向我們擺在黑線之前

相關影片連結
https://www.youtube.com/watch?v=tdHXZUN7sKw
整體配置圖

## 更多參考
第零章 認識dobot原廠設備 https://hackmd.io/-MpYdCViThiPUSD-bKzczw?view
第一章 Cyber Pi與dobot連結 https://hackmd.io/Ob3uHwJJT3-AXZA4BZwyIQ
第二章 Cyber Pi控制滑軌與區域網路
https://hackmd.io/J0WuY15yRDSiBRz32veFhA?view
第三章 搖桿控制Cyber Pi和dobot滑軌
https://hackmd.io/v_1uxa9WRxyGruGAwPw61g?view