# Python X Ev3 (EV3DEV) # 部落格建立!!! 請轉移到[這裡](https://www.poyu39.tw/posts/ev3/python-x-ev3-ev3dev/)閱讀文章 ### 使用 Python 來撰寫 EV3 程式~ ###### tags: `EV3` `Python` `EV3DEV` [![](https://i.imgur.com/llFSUtc.png) ](https://www.ev3dev.org/) > 參考資料:[Program in Python with EV3](https://education.lego.com/en-us/product-resources/mindstorms-ev3/teacher-resources/python-for-ev3/) | [www.ev3dev.org](https://www.ev3dev.org/) | [ev3dev](https://github.com/ev3dev) | [EV3DEV API](https://pybricks.com/ev3-micropython/startinstall.html) ## 介紹 ### Visual Studio Code Visual Studio Code 是一款由微軟開發且跨平台的免費原始碼編輯器,支援語法突顯、程式碼自動補全、程式碼重構功能,並且內建了命令列工具和 Git 版本控制系統,也可以透過安裝額外套件增加功能,支援 Windows 、 MacOS 和 Linux 。 ### vscode-ev3dev-browser 與 LEGO® MINDSTORMS EV3Lab 的積木語言不同,使用 Visual Studio Code 進行編寫程式,安裝 EV3DEV 所推出的 [vscode-ev3dev-browser](https://github.com/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](https://education.lego.com/v3/assets/blt293eea581807678a/blt9df409c9a182ab9c/5f88191a6ffd1b42dc42b8af/ev3micropythonv200sdcardimage.zip) 3. 下載燒錄軟體 [Etcher](https://www.balena.io/etcher) 4. 將記憶卡插入電腦後,依照下方步驟將映像檔燒錄至記憶卡。![](https://i.imgur.com/TBjZbxx.png) 5. 燒錄完成後,確認 EV3 在關機狀態下將記憶卡插入到側邊的 MicroSD 插槽並開機。![](https://i.imgur.com/28wkCfG.png) ### VScode 延伸模組 為了讓 Python 程式檔案可以傳送至 EV3 ,需要安裝額外的延伸模組。 1. 點選左邊延伸模組商店,輸入 EV3 點選 LEGO® MINDSTORMS® EV3 MicroPython。![](https://i.imgur.com/rcIA3BV.png) 2. 重啟 IDE。 3. 安裝成功會在左側欄中出現擴充的延伸模組。 ![](https://i.imgur.com/u64SaHe.png) ### Python 和 MicroPython 語法基本一致,可以在燒錄記憶卡時先安裝在電腦上練習撰寫程式~ 1. 首先到官網下載 Python 的安裝檔。 ![](https://i.imgur.com/7QBRLQT.png) 2. 點選 Downloads 進到下載頁面。 ![](https://i.imgur.com/5qPG2Wb.png) 3. 目前最新版本是 3.11,這邊是直接安裝此版本到環境中,因此如果有多個版本會需要額外設定,也有許多版本可下載,不同版本之間的語法有些小差異。 ![](https://i.imgur.com/0q2m7VA.png) 4. 開啟安裝執行檔,並勾選 "Add python.exe to PATH" 自動設定路徑,並點選 Install Now 開始安裝。 ![](https://i.imgur.com/6tIsSc0.png) 5. 安裝完後可以開啟 CMD ,並輸入 `python` ,如果安裝成功就可以進入到 Python, ![](https://i.imgur.com/YgU7HX8.png) 6. 撰寫第一個程式來測試看看吧! ```python= print("Hello World") ``` > 快看! 電腦在向世界說 Hello 呢! (@^0^) ## 連線 EV3 首先將 EV3 開機並且透過數據線連接至電腦,可以看見開機後的 EV3 主畫面也不同了,不過不用擔心,只要關機並且將記憶卡取出後再開機,就可以回到官方系統了。 ![](https://i.imgur.com/qy511GZ.png) 接著創建一個新的專案,並輸入檔名。 ![](https://i.imgur.com/rxzHs2I.png) 接著左下方的可以看到我們剛剛所安裝的 EV3DEV Device Browser,點選 `Click here to connect to a device` 並選擇 ev3dev 。 ![](https://i.imgur.com/TevU4qJ.png) 等待出現綠色圓圈和 `Status` 可以顯示狀態就表示成功了! ![](https://i.imgur.com/BMHmE35.png) ## EV3DEV Device Browser 功能 ### 尋找設備 自動發現任何連接的 ev3dev 主機,不需要額外設定。 ![](https://i.imgur.com/9pBd4bQ.png) ![](https://i.imgur.com/n9lNkzh.png) ### 遠程瀏覽文件 每個設備的文件都列在 Brickman 中。 ![](https://i.imgur.com/BVyct26.png) ### 將文件下載到設備 單擊一下即可將當前 VS Code 項目發送到 ev3dev 設備。 ![](https://i.imgur.com/edAIXKX.png) ### 遠程運行程序 單擊任何可執行文件以運行它。 ![](https://i.imgur.com/299umkz.png) 也可以右鍵點擊選擇 `Run` ![](https://i.imgur.com/6z0bSxD.png) 錯誤消息將顯示在輸出窗格中。 ![](https://i.imgur.com/NjWwUFT.png) ### 單擊一次(或 F5)即可構建、下載和運行 需創建一個 `ev3devBrowser `的 launch.json 文件以使用此功能。 ```json= { "version": "0.2.0", "configurations": [ { "name": "Download and Run", "type": "ev3devBrowser", "request": "launch", "program": "/home/robot/${workspaceRootFolderName}/hello", "preLaunchTask": "build" } ] } ``` ### 啟動遠程 SSH 可以通過右鍵單擊設備在控制台中啟動 SSH 。 ```linux= sudo hostnamectl set-hostname <name> ``` ![](https://i.imgur.com/3Qmaywi.png) ### 營幕截圖 可以通過右鍵單擊設備輕鬆截取營幕。 ![](https://i.imgur.com/Ve6th4X.png) ![](https://i.imgur.com/0yZkFNp.png) ## 來撰寫 EV3 的第一個 Python 程式吧! 首先來介紹自動生成的 `main.py` ,這個檔案室 EV3 程式開始的地方,我們先來了解一下 Python 的語法以及這自動生成的程式碼是甚麼吧! 粉紅色框內所表示的是 `from ... import ...` 這段程式碼是代表匯入「函式庫」,函式庫像是外部的我們之前 EV3Lab 積木語言所學到匯入 MyBlock ,將外部的程式碼匯入就可以在這個檔案中的程式使用。 藍色框內所表示的是設定硬體的物件,當我們需要使用馬達或是感測器時,需要告訴 EV3 主機,我的硬體是插在哪個 port 。 黃色框內就是我們主要撰寫程式碼的地方了! ![](https://i.imgur.com/nJNFvFi.png) 首先來撰寫我們在 EV3 上的第一個 Python 程式吧! 在 `ev3.speaker.beep()` 後方加上 ```python= print("Hello World") ``` 接著點選左側邊欄中的偵測與執行,接著點選上方的開始鍵,就可以將剛剛所撰寫的程式下載至 EV3 並且執行。 ![](https://i.imgur.com/7Q3V21m.png) 可以看到下面的輸出欄位,印出了 `Hello World` ,這次換 EV3 打招呼了喔~ ![](https://i.imgur.com/MqQoEd9.png) 那有沒有辦法將 `Hello World` 顯示到 EV3 螢幕上呢? 試試看剛剛改成下方這段程式碼吧,並且下載到 EV3 執行看看。 ```python= ev3.screen.print("Hello World!") # 在螢幕上顯示 Hello World wait(5000) # 等待 5000 毫秒 ``` 成功了! ![](https://i.imgur.com/MDNsQEZ.png) Q. 為甚麼要加上 `wait(5000)` ,等待 5000 毫秒呢? A. 如果不加上的話,程式會印出Hello World! 結束,程式一結束便會回到主畫面,如此一來就來不及看到是否有印出了。 ## Python 流程控制 ### while 在Python中,while是一種迴圈語句,它允許在滿足特定條件的情況下重複執行程式碼塊。while語句的基本構造是: ```pyhton= while 條件: 程式碼 ``` 在這個語句中,當條件為True時,程式碼將被執行。每次執行完程式碼塊後,Python都會檢查條件是否仍為True,如果是,則再次執行程式碼。這個過程將繼續,直到條件變為False為止。 以下是一個while循環的簡單範例,該範例使用while循環顯示一系列數字,直到達到指定數字: ```python= 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 螢幕上。 ```pyhton= # ans num = 0 while num < 5: ev3.screen.print("Hello World!") # 在螢幕上顯示 Hello World num += 1 wait(1000) # 等待 1 秒 ``` ### for for 迴圈是一個重要的控制流程工具,與剛才介紹的 `while` 相似,新增了用於對序列物件(例如列表、元組、字串等)進行迭代的主要功能。 使用 for 迴圈的語法為: ```python= for 變數 in 序列: # 程式碼 ``` 在上述語法中,變數是用來存放每次迭代中序列中的一個元素,而包裹其中的程式碼是在每次迭代時需要執行的一段程式碼。當迭代完成後,for 迴圈也就結束了。 以下是一個使用 for 迴圈遍歷列表的範例: ```python= fruits = ['蘋果', '香蕉', '橘子'] for fruit in fruits: print(fruit) ``` 在上述範例中,我們使用了一個名為 fruits 的列表,並使用 for 迴圈來遍歷這個列表中的每個元素。在每次迭代中,變數 fruit 會被賦值為列表中的一個元素,然後進入程式碼塊中執行 print 函式,將 fruit 輸出到螢幕上。 for 迴圈也可以和其他控制流程結構(例如 if、else 等)結合使用,來實現更複雜的邏輯運算。使用 for 迴圈,可以輕鬆地對序列物件進行迭代,讓我們可以更加靈活地處理大量資料。 ### if else if else 是最常見的條件判斷工具,可以用來根據不同的條件執行不同的程式碼。使用 if else 的語法為: ```python= if 條件1: # 條件1成立時要執行的程式碼 else: # 條件1不成立時要執行的程式碼 ``` 條件1 是一個 `bool` 布林值(True 或 False),如果條件1 為 True,則執行 if 後的程式碼,否則執行 else 後的程式碼。 以下是一個使用 if else 判斷 `Touch Sensor` 是否按下的程式。 ```python= btn = TouchSensor(Port.S1) # 創建一個連結在Port.S1的TouchSensor while True: # 使用無限迴圈讓程式持續執行 if btn.pressed(): # 當按鈕按下 ev3.screen.print("btn pressed") else: ev3.screen.print("btn not pressed") ``` ![](https://i.imgur.com/Az4ZjOX.png) ![](https://i.imgur.com/ficvPWU.png) ## 程式實作 ### 馬達緩加減 首先我們先來拼裝出簡單的循跡車 ![](https://i.imgur.com/3wYsCef.png) > [模型下載連結](https://drive.google.com/drive/folders/1-OdZlftMtYUOCPeAOonF-JetDtFlFsgs?usp=sharing) | [PDF組裝說明書](https://education.lego.com/v3/assets/blt293eea581807678a/bltc0dd681a7b3bfa8b/5ec7c6a6cd4cf750c888fa6f/ev3-rem-driving-base.pdf) 馬達緩加減是指讓馬達在啟動或停止的時候,逐漸地加速或減速,不是瞬間就開始或停止。這樣做的原因是因為如果馬達瞬間啟動或停止,會對機器和電子元件產生很大的應力,導致損壞或壞掉,更會導致馬達的精準度不佳。 在 `EV3` 的輪型機器人上則是會輪子的打滑,起步和煞車如果過快,機器會因慣性和摩擦力導致行走起來不完整。 - 緩減速 ```python= 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() # 停止馬達 ``` - 緩加減速 ```python= # 緩動函數 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) ``` ### 循跡 ```python= # 初始化馬達 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) ```