Try   HackMD

Python X Ev3 (EV3DEV)

使用 Python 來撰寫 EV3 程式~

tags: EV3 Python EV3DEV


參考資料:Program in Python with EV3 | www.ev3dev.org | ev3dev | EV3DEV API

介紹

Visual Studio Code

Visual Studio Code 是一款由微軟開發且跨平台的免費原始碼編輯器,支援語法突顯、程式碼自動補全、程式碼重構功能,並且內建了命令列工具和 Git 版本控制系統,也可以透過安裝額外套件增加功能,支援 Windows 、 MacOS 和 Linux 。

vscode-ev3dev-browser

與 LEGO® MINDSTORMS EV3Lab 的積木語言不同,使用 Visual Studio Code 進行編寫程式,安裝 EV3DEV 所推出的 vscode-ev3dev-browser 擴充插件後,可以直接上傳程式到 EV3 主機,也支援使用 SSH 等控制台。

EV3DEV

EV3DEV 是基於 Debain Linux-based 系統下運行,可在多個 LEGO® MINDSTORMS 兼容平台上運行,包括 LEGO® MINDSTORMS EV3 和基於 Raspberry Pi 的 BrickPi。

Python

Python 是一種高階程式語言,也是一種解釋型語言,強調程式碼的可讀性、簡潔的語法。Python為了讓程式碼具備高度的可閱讀性,在設計時盡量使用了其它語言常用的符號和英文單字。

Python 支援使用反斜槓作為行接續符,將多個物理行合成為一個邏輯行,作為通用型的程式語言,運用在 YouTube 、 Google 、 Yahoo! 、 Facebook 、 Dropbox 、Gmail 等等知名平台都在大量使用的程式語言,其特色是支援多種編寫方式,包括物件導向、命令式、函數式、程序式,也和Python和Ruby、Perl、Scheme一樣,擁有動態語法、強制縮排、Garbage Collection 和自動記憶體管理等等諸多的優勢,於未來的需求量也越來越多,涉獵的領域像是大數據分析、自動化腳本、人工智慧。

而 EV3DEV 所使用的 MicroPython 是一款小型的 Python 直譯器,它擁有自己的 Python 直譯器,在保留絕大部分 Python 的核心特色同時縮減周邊模組(函式庫),好讓 Python 這種出了名的吃記憶體的語言也能在像是 EV3 這樣的微控制板上運行。

安裝

EV3DEV

EV3DEV 是基於 Debain Linux-based 系統下的 MicroPython ,因此需要另外準備記憶卡當作系統碟,一般容量建議在 4GB 。

  1. 準備一張 microSD 記憶卡,建議容量 4GB 以下。

  2. 下載映像檔 EV3 MicroPython micro SD card image

  3. 下載燒錄軟體 Etcher

  4. 將記憶卡插入電腦後,依照下方步驟將映像檔燒錄至記憶卡。

  5. 燒錄完成後,確認 EV3 在關機狀態下將記憶卡插入到側邊的 MicroSD 插槽並開機。

VScode 延伸模組

為了讓 Python 程式檔案可以傳送至 EV3 ,需要安裝額外的延伸模組。

  1. 點選左邊延伸模組商店,輸入 EV3 點選 LEGO® MINDSTORMS® EV3 MicroPython。

  2. 重啟 IDE。

  3. 安裝成功會在左側欄中出現擴充的延伸模組。

Python

和 MicroPython 語法基本一致,可以在燒錄記憶卡時先安裝在電腦上練習撰寫程式~

  1. 首先到官網下載 Python 的安裝檔。

  2. 點選 Downloads 進到下載頁面。

  3. 目前最新版本是 3.11,這邊是直接安裝此版本到環境中,因此如果有多個版本會需要額外設定,也有許多版本可下載,不同版本之間的語法有些小差異。

  4. 開啟安裝執行檔,並勾選 "Add python.exe to PATH" 自動設定路徑,並點選 Install Now 開始安裝。

  5. 安裝完後可以開啟 CMD ,並輸入 python ,如果安裝成功就可以進入到 Python,

  6. 撰寫第一個程式來測試看看吧!

print("Hello World")

快看! 電腦在向世界說 Hello 呢! (@^0^)

連線 EV3

首先將 EV3 開機並且透過數據線連接至電腦,可以看見開機後的 EV3 主畫面也不同了,不過不用擔心,只要關機並且將記憶卡取出後再開機,就可以回到官方系統了。

接著創建一個新的專案,並輸入檔名。

接著左下方的可以看到我們剛剛所安裝的 EV3DEV Device Browser,點選 Click here to connect to a device 並選擇 ev3dev 。

等待出現綠色圓圈和 Status 可以顯示狀態就表示成功了!

EV3DEV Device Browser 功能

尋找設備

自動發現任何連接的 ev3dev 主機,不需要額外設定。

遠程瀏覽文件

每個設備的文件都列在 Brickman 中。

將文件下載到設備

單擊一下即可將當前 VS Code 項目發送到 ev3dev 設備。

遠程運行程序

單擊任何可執行文件以運行它。

也可以右鍵點擊選擇 Run

錯誤消息將顯示在輸出窗格中。

單擊一次(或 F5)即可構建、下載和運行

需創建一個 ev3devBrowser 的 launch.json 文件以使用此功能。

{ "version": "0.2.0", "configurations": [ { "name": "Download and Run", "type": "ev3devBrowser", "request": "launch", "program": "/home/robot/${workspaceRootFolderName}/hello", "preLaunchTask": "build" } ] }

啟動遠程 SSH

可以通過右鍵單擊設備在控制台中啟動 SSH 。

sudo hostnamectl set-hostname <name>

營幕截圖

可以通過右鍵單擊設備輕鬆截取營幕。

來撰寫 EV3 的第一個 Python 程式吧!

首先來介紹自動生成的 main.py ,這個檔案室 EV3 程式開始的地方,我們先來了解一下 Python 的語法以及這自動生成的程式碼是甚麼吧!

粉紅色框內所表示的是 from ... import ... 這段程式碼是代表匯入「函式庫」,函式庫像是外部的我們之前 EV3Lab 積木語言所學到匯入 MyBlock ,將外部的程式碼匯入就可以在這個檔案中的程式使用。

藍色框內所表示的是設定硬體的物件,當我們需要使用馬達或是感測器時,需要告訴 EV3 主機,我的硬體是插在哪個 port 。

黃色框內就是我們主要撰寫程式碼的地方了!

首先來撰寫我們在 EV3 上的第一個 Python 程式吧!

ev3.speaker.beep() 後方加上

print("Hello World")

接著點選左側邊欄中的偵測與執行,接著點選上方的開始鍵,就可以將剛剛所撰寫的程式下載至 EV3 並且執行。

可以看到下面的輸出欄位,印出了 Hello World ,這次換 EV3 打招呼了喔~

那有沒有辦法將 Hello World 顯示到 EV3 螢幕上呢?

試試看剛剛改成下方這段程式碼吧,並且下載到 EV3 執行看看。

ev3.screen.print("Hello World!") # 在螢幕上顯示 Hello World wait(5000) # 等待 5000 毫秒

成功了!

Q. 為甚麼要加上 wait(5000) ,等待 5000 毫秒呢?

A. 如果不加上的話,程式會印出Hello World! 結束,程式一結束便會回到主畫面,如此一來就來不及看到是否有印出了。

Python 流程控制

while

在Python中,while是一種迴圈語句,它允許在滿足特定條件的情況下重複執行程式碼塊。while語句的基本構造是:

while 條件: 程式碼

在這個語句中,當條件為True時,程式碼將被執行。每次執行完程式碼塊後,Python都會檢查條件是否仍為True,如果是,則再次執行程式碼。這個過程將繼續,直到條件變為False為止。

以下是一個while循環的簡單範例,該範例使用while循環顯示一系列數字,直到達到指定數字:

num = 0 while num < 5: print(num) num += 1

這段程式碼將從0開始顯示一系列數字,每個數字之間相差1,直到顯示數字5之前,因為條件“num < 5”仍然為True。在顯示數字5之後,條件變為False,因此while循環將停止執行。

需要注意的是,如果while循環的條件永遠不為False,則程式碼將陷入無限循環,這種情況應盡量避免。

小試身手

Q. 請使用 while 印出五個 Hello World! 在 EV3 螢幕上。

# ans num = 0 while num < 5: ev3.screen.print("Hello World!") # 在螢幕上顯示 Hello World num += 1 wait(1000) # 等待 1 秒

for

for 迴圈是一個重要的控制流程工具,與剛才介紹的 while 相似,新增了用於對序列物件(例如列表、元組、字串等)進行迭代的主要功能。

使用 for 迴圈的語法為:

for 變數 in 序列: # 程式碼

在上述語法中,變數是用來存放每次迭代中序列中的一個元素,而包裹其中的程式碼是在每次迭代時需要執行的一段程式碼。當迭代完成後,for 迴圈也就結束了。

以下是一個使用 for 迴圈遍歷列表的範例:

fruits = ['蘋果', '香蕉', '橘子'] for fruit in fruits: print(fruit)

在上述範例中,我們使用了一個名為 fruits 的列表,並使用 for 迴圈來遍歷這個列表中的每個元素。在每次迭代中,變數 fruit 會被賦值為列表中的一個元素,然後進入程式碼塊中執行 print 函式,將 fruit 輸出到螢幕上。

for 迴圈也可以和其他控制流程結構(例如 if、else 等)結合使用,來實現更複雜的邏輯運算。使用 for 迴圈,可以輕鬆地對序列物件進行迭代,讓我們可以更加靈活地處理大量資料。

if else

if else 是最常見的條件判斷工具,可以用來根據不同的條件執行不同的程式碼。使用 if else 的語法為:

if 條件1: # 條件1成立時要執行的程式碼 else: # 條件1不成立時要執行的程式碼

條件1 是一個 bool 布林值(True 或 False),如果條件1 為 True,則執行 if 後的程式碼,否則執行 else 後的程式碼。

以下是一個使用 if else 判斷 Touch Sensor 是否按下的程式。

btn = TouchSensor(Port.S1) # 創建一個連結在Port.S1的TouchSensor while True: # 使用無限迴圈讓程式持續執行 if btn.pressed(): # 當按鈕按下 ev3.screen.print("btn pressed") else: ev3.screen.print("btn not pressed")

程式實作

馬達緩加減

首先我們先來拼裝出簡單的循跡車

模型下載連結 | PDF組裝說明書

馬達緩加減是指讓馬達在啟動或停止的時候,逐漸地加速或減速,不是瞬間就開始或停止。這樣做的原因是因為如果馬達瞬間啟動或停止,會對機器和電子元件產生很大的應力,導致損壞或壞掉,更會導致馬達的精準度不佳。

EV3 的輪型機器人上則是會輪子的打滑,起步和煞車如果過快,機器會因慣性和摩擦力導致行走起來不完整。

  • 緩減速
bMotor = Motor(Port.B) target = 1000 # 目標角度 bMotor.reset_angle(0) # 將現在的角度歸零 while bMotor.angle() < target: # 當現在的角度小於目標角度時 speed = (target - bMotor.angle()) * 0.5 # 計算速度 if speed > 100: # 限制速度在 10~100 之間 speed = 100 elif speed < 10: speed = 10 bMotor.dc(speed) # 設定速度 ev3.screen.print(bMotor.angle()) # 顯示現在的角度 bMotor.hold() # 停止馬達
  • 緩加減速
# 緩動函數 cos def easeInOutSine(x): return -(math.cos(math.pi * x) - 1) / 2 # 彈跳函數 def easeOutBounce(x): n1 = 7.5625 d1 = 2.75 if x < 1 / d1: return n1 * x * x elif x < 2 / d1: x -= 1.5 / d1 return n1 * x * x + 0.75 elif x < 2.5 / d1: x -= 2.25 / d1 return n1 * x * x + 0.9375 else: x -= 2.625 / d1 return n1 * x * x + 0.984375 # 映射區間 def mapping(x, in_min, in_max, out_min, out_max): return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min # 緩加減速 def smoothMove(time, distance, minSpeed, maxSpeed): i = 0 step = time / 100 / 2 target = bMotor.angle() + distance while i <= 1: t_angle = mapping(easeInOutSine(i), 0, 1, 0, target) error = t_angle - bMotor.angle() t_speed = error * 1.5 bMotor.dc(t_speed) i += 0.01 wait(step) while bMotor.angle() < target: bMotor.dc(20) bMotor.stop() bMotor.hold() # 使用範例 在3秒內移動到1000度 smoothMove(3000, 1000)

循跡

# 初始化馬達 left_motor = Motor(Port.B) right_motor = Motor(Port.C) # 初始化顏色感應器 line_sensor = ColorSensor(Port.S3) # 初始化Drive robot = DriveBase(left_motor, right_motor, wheel_diameter=55.5, axle_track=104) # 計算反射光閥值 BLACK = 9 WHITE = 85 threshold = (BLACK + WHITE) / 2 # 設定移動速度為每秒 100 毫米。 DRIVE_SPEED = 100 # 設定比例控制器的增益。這意味著對於每一個光強度百分比偏離閥值, # 我們就將駕駛座的轉向速度設置為 1.2 度/秒。 # 例如,如果反光值偏離閥值 10,則機器人的轉向速度為 10*1.2 = 12 度/秒。 PROPORTIONAL_GAIN = 1.2 # 開始無限地跟隨線條。 while True: # 計算光線強度與閥值之間的偏差。 # 加負號可以走在線的另一邊 deviation = line_sensor.reflection() - threshold # 計算轉向速度。 turn_rate = PROPORTIONAL_GAIN * deviation # 設置駕駛座速度和轉向速度。 robot.drive(DRIVE_SPEED, turn_rate) # 您可以在此循環中等待一小段時間或做其他事情。 wait(10)