Quantcast
Channel: CAVEDU教育團隊技術部落格
Viewing all 678 articles
Browse latest View live

[ZED深度攝影機系列] C++ 開發環境與範例

$
0
0
本文為 [ZED深度攝影機系列] 的第四篇(),帶您安裝 ZED 的 Python3 SDK,包含環境建置並帶您順利執行範例程式。
請先回顧先前文章:
1. [ZED深度攝影機系列] 認識 ZED 2i 深度攝影機
2. [ZED深度攝影機系列] 安裝 ZED SDK
3. [ZED深度攝影機系列] Python 開發環境與範例

安裝ZED Python API

本文章將說明如何在 Windows 與 Linux 作業系統上使用 ZED 的 Python3 SDK。


於 Windows 平台上建置 C++ 應用程式

本段將說明如何在 Windows OS 上使用 ZED SDK 來建置應用程式。

設定專案

我們要用 ZED SDK 與 CMake 來建置 Hello ZED 這個簡易教學範例。CMake 是一個跨平台的專案生成工具,可用於您的編譯環境中來建置專案。例如 CMake 腳本可產生 Visual Studio 專案與相關檔案。

  • 下載並安裝最新的 ZED SDK
  • 下載 ZED 範例程式,也可由 ZED GitHub 找到更多相關套件與範例程式。
  • 開啟 Tutorials/Tutorial - Hello ZED 資料夾找到要建置的程式碼,資料夾中應包含以下檔案:
    • CMakeLists.txt
    • main.cpp
    • Readme.md

於 Windows 建置專案

於 Windows 使用 ZED SDK 來建置程式需要 Cmake (3.5.0 或更高版本) 與 Visual Studio 2015 (或更高版本)。應用程式必須以 64 位元來編譯。

注意:安裝 Visual Studio 時,請記得勾選 Visual C++ 選項。

  • 開啟 cmake-gui
  • 在 “Where is the source code“ 欄位中輸入專案資料夾路徑,也就是 CMakeLists.txt 所在位置。在此要建立一個名為 ZED_Tutorial_1 的專案。
  • 在 “Where to build the binaries“ 欄位中輸入上述路徑並加入 /build
  • 點選左下角的 [Configure]

  • 會跳出一個視窗,詢問是否讓 CMake 建立 build 資料夾,請按 yes
  • 接著會有另一個視窗,要求為專案指定一個生成器,請選擇 Visual Studio,點選 [Finish].

CMAKE 需要將目標平台設定為 x64。新版本的預設值就是 x64,如果不是的話請手動修改。

  • CMake 需要一點時間來設定專案。
  • 點選 [Generate] 來建置 Visual Studio 專案。

  • CMake 已經在 build 目錄下建置專案完成。請點選 [Open Project] 來開啟專案,或在關閉 cmake 視窗之後再開啟 build 目錄。

  • 現在 Visual Studio solution 已經生成完畢,請開啟 Project.sln 檔案,並將其設定為 Release 模式。

  • 請由 Build 選單或用快捷鍵來進行建置,請把本專案 ZED_Tutorial_1 設定為 startup project,如下圖。

  • 現在可以在 Visual Studio IDE 編輯與編譯專案了。請按下 Ctrl+F5 來啟動程式。

於 Linux 與 Jetson 平台上建置 C++ 應用程式

本段將說明如何在 Linux Ubuntu OS 上使用 ZED SDK 來建置應用程式。

設定專案

與前段相同,這次要用 ZED SDK 與 CMake 在 Linux OS 中來建置 Hello ZED 教學範例。CMake 是一個跨平台的專案生成工具,可用於您的編譯環境中來建置專案。

  • 下載並安裝最新的 ZED SDK
  • 下載 ZED 範例程式,也可由 ZED GitHub 找到更多相關套件與範例程式。
  • 開啟 Tutorials/Tutorial - Hello ZED 資料夾找到要建置的程式碼,資料夾中應包含以下檔案:
    • CMakeLists.txt
    • main.cpp
    • Readme.md

於 Linux 與 Jetson 建置專案

在 Linux 上建置具備 ZED SDK 的應用程式需要 GCC (5, 6) 與 CMake (3.5.0 最低) 的 toolchain,請用以下指令來安裝:

$sudo apt-get install build-essential cmake 

現在可以建置 Hello ZED 範例了,請根據以下步驟操作:
  • 開啟終端機,進入專案資料夾
$cd path/to/your/project/ZED_Tutorial_1
  • 新增一個 bulid 資料夾
$mkdir build && cd build
  • 指向 CMakeLists.txt 所在的資料夾(上層資料夾),使用 CMake 來生成專案:
$cmake ..
...
-- Configuring done
-- Generating done
-- Build files have been written to: path/to/your/project/ZED_Tutorial_1/build
  • 使用 ls 指令查看 bulid資料夾內容,應包含以下檔案:
$ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake Makefile
  • 使用 make 指令來編譯:
$make
  • 程式編譯完成,請用以下指令來執行:
$./ZED_Tutorial_1  

程式執行結果,會顯示您的攝影機序號。

動態 / 靜態鏈結

ZED SDK 在 Linux OS 中提供靜態與動態函式庫。預設為動態,可減少應用程式大小,但會強迫使用者安裝所有相依套件。靜態鏈結則會打包所有相依套件到最終的執行檔中。

如需要切換,只要在執行  cmake 時啟用以下選項即可:

$cmake -DLINK_SHARED_ZED=OFF ..  

接著再次建置程式:
$make 

應用程式會變大很多,但在部署應用程式會省很多功夫!

註:本文經原廠授權之後翻譯自 https://www.stereolabs.com/docs/app-development/cpp/windows/


ZED 景深攝影機範例#1/8 – Hello ZED

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。

範例01 – Hello ZED

本範例單純說明如何設定並開啟 ZED 攝影機,並在顯示自身序號之後關閉攝影機。這個範例非常簡單,適合做為您操作 ZED SDK 的起點。

註:原廠頁面每段都提供了 C++ / Python / C# 的範例程式,在此只列出 Python 範例

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Hello ZED 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)。

範例總覽

ZED API 提供了對於自家攝影機在控制與設定的低階存取設定。如果要在您的程式中操作 ZED 硬體,您需要建立並開啟一個 Camera 物件。API 支援兩種影像輸入:ZED live video (即時模式),或 .svo 影片檔 (回放模式)。

攝影機設定

設定攝影機時,須建立一個 Camera 物件並指定 InitParameters ,後者可用於調整解析度、FPS、深度感測參數等等。這些參數只能在攝影機開啟後才能調整,且當攝影機在執行時無法修改。

# Create a ZED camera object
zed = sl.Camera()

# Set configuration parameters
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD1080
init_params.camera_fps = 30
camera configuration - python

InitParameters 包含了一些預設設定,您可設定以下初始參數:

  • Camera 設定參數:camera_* (解析度、影像翻轉等)
  • SDK 設定參數:sdk_* (GPU裝置等)
  • 深度設定參數:depth_* (深度模式、最小距離等)
  • 座標幀數設定參數:coordinate_* (座標系統、座標單位等)
  • SVO參數:使用ZED SDK 來操作Stereolabs影片檔(檔名、即時模式等)

所有可用的參數請參考 API 文件。

開啟攝影機

初始設定完成之後,請開啟攝影機:

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    exit()
open the camera - python

取得攝影機資訊

左右兩鏡頭與解析度下的)影機參數,包含焦長(focal length)、視野(fov)或立體影像校正等等都可取得。這些數值都整合在 CalibrationParameters 中,並可由 getCameraInformation()語法來存取。您也可透過 Sensors API 來存取 IMU與感測器。

本範例不會開啟影像,只會取得攝影機序號而已:

# Get camera information (serial number)
zed_serial = zed.get_camera_information().serial_number
print("Hello! This is my serial number: {}".format(zed_serial))
retrieve camera info - python
您應可在終端機輸出看到這台攝影機的序號。

關閉攝影機

使用 zed.close() 來正確關閉攝影機並退出程式。

# Close the camera
zed.close()
close the camera - python
執行畫面

執行後,可在終端機中看到攝影機序號

下一步

下一個範例要說明如何從  ZED 攝影機取得深度影像。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/hello-zed/

ZED 景深攝影機範例#2/8 – Image Capture

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python 兩種語言的範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。

範例02 – Image Capture

本範例將說明如何截取ZED攝影機左側鏡頭的影像,並將影像之時間戳記與尺寸顯示於終端機中,程式會不斷執行直到成功截取50張影像為止。我們假設您已經順利完成上一個 Hello ZED 範例。

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

範例總覽

   開啟攝影機

如上一個 Hello ZED範例,在此會建立、設定並開啟ZED攝影機,我們會把它的解析度設定為 dual HD 1080,30 fps。

# Create a ZED camera object
zed = sl.Camera()

# Set configuration parameters
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD1080  # Use HD1080 video mode
init_params.camera_fps = 30  # Set fps at 30

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
Create a ZED camera object - python

取得影像資料

ZED 攝影機開啟之後,就可以擷取來自攝影機的即時串流影像了,在此建立一個迴圈來擷取50張影像,完成之後退出。

在此會呼叫 zed.grab 函式來擷取單張影像並處理。本函式也可接受執行階段參數,但本範例為求簡便採用預設設定。

如果回傳結果為 SUCCESS代表成功擷取一張新的影像並已可用,您也可檢視 grab() 狀態來得知擷取影像過程中是否發生錯誤。

# Grab an image
runtime_parameters = sl.RuntimeParameters()
if zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
    # A new image is available if grab() returns ERROR_CODE.SUCCESS
capture image data - python

擷取完成之後,就可以取得新的影像與其後設資料(metadata),本範例會使用retrieveImage()getTimestamp() 語法來取得左側鏡頭影像與其時間戳記。

retrieveImage() 會接受 image = sl.Mat() 以及 VIEW 模式作為參數。建立 Mat, 還不需要分配記憶體。首次呼叫retrieveImage() 時,sl.Mat() 會被填入影像資料且自動分配記憶體。

# Capture 50 frames and stop
i = 0
image = sl.Mat()
runtime_parameters = sl.RuntimeParameters()
while i < 50:
    # Grab an image, a RuntimeParameters object must be given to grab()
    if zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
        # A new image is available if grab() returns ERROR_CODE.SUCCESS
        zed.retrieve_image(image, sl.VIEW.LEFT) # Get the left image
        timestamp = zed.get_timestamp(sl.TIME_REFERENCE.IMAGE)  # Get the image timestamp
        print("Image resolution: {0} x {1} || Image timestamp: {2}\n".format(image.get_width(), image.get_height(), timestamp.get_milliseconds()))
        i = i + 1
Capture 50 frames and stop - python

注意:影像的時間戳記單位為奈秒,且為 Epoch 格式。比較兩次 zed.grab 函式之間的時間戳記,應該相當接近幀率 (前提是未掉幀)。

更多關於攝影機與影片參數請參考原廠文件 Using the Video API.

關閉攝影機

擷取50張影像之後,關閉攝影機並退出程式。

# Close the camera
zed.close()
close the camera - python

更多範例

想學習曝光、增益、對比與銳利度這類相機設定,並顯示於結果影像的話,請參考 Camera Control 範例。

下一步

您現在已經知道如何開啟/存取/設定攝影機了,也知道如何取得單張影像與其後設資料。

下一個範例要說明如何從  ZED 攝影機取得深度地圖。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/image-capture/

[福虎生豐~ 新年快樂!]CAVEDU 2021 走向 2022 感言

$
0
0

CAVEDU 2021 走向 2022 感言

今天是農曆春節大年初一,照例要回顧一下已過一年的工作進度,以及對於新一年的展望。2021對所有人來說都不容易,給自己一個掌聲吧!由疫情觸發的這波低接觸經濟,對大家的生活、學習、工作與交通方式都發生了根本性的變化。CAVEDU也遭受到了衝擊而有一段辛苦的日子。感謝許多師長好友的支持,我們還在。
 
大環境變化正是檢視自我的好時機。除了不斷精煉原有的商品服務與實體課程之外,我們在這兩年內針對不同需求的客戶錄製了超過兩百隻教學影片。也很高興已是第三年參與[AIGO高中職生AI扎根系列活動],與全台們諸多優秀同學一同學習AI的重要觀念。
同時也為大家介紹目前的四大事業群(講的好像人很多…):
⚡放課後 FunSchool:持續參與許多maker、科技教育相關活動。曾經在華山文創園區舉辦過同名專場活動,之後就深深覺得千萬不要自己辦活動QQ。
⚡機器人王國購物商城:由腦波弱老闆領軍,各種奇奇怪怪的東西都在這裡一次買齊!特色是售後服務與強大推坑力,還有按圖施工的教學文章。
⚡翰吉出版:由阿吉老師負責的懶呼呼出版團隊,2021年基本上只寫了一本[Jetson Nano不說No,CAVEDU教你一次懂]。但這十多年下來也堂堂邁入六十本書囉!
⚡CAVEDU教育團隊:提供優質教學內容是初衷,也是前進的動力。除原有的[Arduino首次接觸就上手]、[BOSON電子互動套件]等入門maker課程之外,我們也一直在教學現場嘗試各種教學可能性,例如結合LINE通訊軟體、Processing互動介面、tinyML微型機器學習框架以及各種新的開發板等。為了讓各項課程更落地,我們也於今年年底於中區教師社群舉辦了一系列的研習培訓,終於在12月於后綜高中舉辦了Jetbot機器學習道路辨識競賽,精彩比賽回顧請點我
綜觀這幾年的發展,從IoT漸漸走向了AIoT,而台灣確實在AI、物(各類聯網邊緣運算裝置)、聯(4G/5G網路通訊)以及網(雲端服務)方面都有非常好的產業優勢與學習資源。細數成果如下:
➡AI方面,多虧近年來coding free ML tools的能見度以及AI as a service/product的意識抬頭,初學者也能快速理解並把相關技術整合在既有專案中。另外為接軌更好的原廠教學資源,由阿吉老師率先取得NVIDIA Jetson AI大使資格之後,諸多同仁也陸續取得NVIDIA深度學習機構(Deep Learning Institute, DLI)講師資格,可舉行多種NVIDIA AI深度學習、邊緣運算與認證課程,2021年全年度光是NVIDIA系列課程就舉辦了超過30場,800人次!當然啦,東西一起玩才好玩,如果您也想成為AI講師,歡迎和我們聯絡。阿吉老師也在今年的GTC2021分享[使用Jetson Nano在高中教授深度學習機器人實作課程] (照片有點驚悚,修圖修太大了… 阿吉老師感到抱歉)
➡5G方面,我們與知名網通廠商合作,把5G的三大特性(高網速、低延遲與超大連線數)帶到教學現場,讓學員體驗各種可能性。為此曾經舉辦過Azure智慧商店、NVIDIA DeepStream影像串流研習以及LinkIt智慧居家等研習。
➡各類教學用開發板是我們的老本行,持續為您研究如何各種有趣的開發板整合到課程中。
CAVEDU 12年了,真的很快,從最早的樂高機器人開始,科技教育的發展已超出我們的想像。期許自己保持好奇心、勇於嘗試、親身體驗,才能擁抱這有趣的未來!
最後感謝您看到這裡,阿吉老師在上個月底喜獲愛女(有興趣看他感性文章的可以到他臉書走走,但權限是好友…),也歡迎在留言寫下您的祝福,他會一一回復喔~

 

 

 

 

ZED 景深攝影機範例#3/8 – Depth Perception

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例03 – 深度感知

本範例將說明如何從立體影像與點雲取得深度資訊,並可在終端機中顯示指定像素的距離。程式會擷取 50 張畫面之後結束。在此假設您已完成前兩個範例:Hello ZEDImage Capture

註:原廠頁面每段都提供了 C++ / Python / C# 的範例程式,在此只列出 Python 範例

前置作業

範例總覽

開啟攝影機

如先前範例,首先要建立、設定與開啟 ZED 攝影機。在此把攝影機設定為 HD720 / 60fps 並在 PERFORMANCE 模式中啟用深度偵測,更多資訊請參考 Depth Modes

# Create a ZED camera
zed = sl.Camera()
init_params = sl.InitParameters()
init_params.sdk_verbose = True # Enable verbose logging
init_params.depth_mode = sl.DEPTH_MODE.PERFORMANCE # Set the depth mode to performance (fastest)
init_params.coordinate_units = sl.UNIT.MILLIMETER  # Use millimeter units

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    print("Error {}, exit program".format(err)) # Display the error
    exit()
open the camera - python
注意:深度感知預設是在 ULTRA 模式。如果您是使用這個模式的話,應該不必在 InitParameters 中設定深度模式。

擷取影像與深度

ZED 開啟之後就可以擷取影像與深度了,在此程式會持續執行,直到成功擷取 50 張影像(畫面)為止。取得深度地圖的作法與取得影像相當類似,後者作法請回顧 範例2

  • 建立 sl.Mat() 來儲存深度地圖
  • 呼叫 retrieveMeasure() 來取得深度地圖
# Capture 50 images and depth, then stop
i = 0
image = sl.Mat()
depth = sl.Mat()
point_cloud = sl.Mat()
runtime_parameters = sl.RuntimeParameters()
while i < 50:
    # Grab an image
    if zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
        # A new image is available if grab() returns sl.ERROR_CODE.SUCCESS
        zed.retrieve_image(image, sl.VIEW.LEFT) # Get the left image
        zed.retrieve_measure(depth, sl.MEASURE.DEPTH) # Retrieve depth matrix. Depth is aligned on the left RGB image
        zed.retrieve_measure(point_cloud, sl.MEASURE.XYZRGBA) # Retrieve colored point cloud
        i = i + 1
capture image and depth - python

更多關於深度與點雲參數請參考 Using the Depth API

在點雲中測量距離

取得點雲之後,就可取得特定像素的深度(距離)了。本範例會取得畫面中心(寬/2, 高/2)像素的距離資訊。

# Get and print distance value in mm at the center of the image
# We measure the distance camera - object using Euclidean distance
x = round(image.get_width() / 2)
y = round(image.get_height() / 2)
err, point_cloud_value = point_cloud.get_value(x, y)
distance = math.sqrt(point_cloud_value[0] * point_cloud_value[0] +
                     point_cloud_value[1] * point_cloud_value[1] +
                     point_cloud_value[2] * point_cloud_value[2])
print("Distance to Camera at ({0}, {1}): {2} mm".format(x, y, distance), end="\r")
measure distance in point cloud - python

不太習慣點雲的話,也可直接使用深度地圖來取得指定像素的深度值。

x = round(image.get_width() / 2)
y = round(image.get_height() / 2)
err, depth_value = depth.get_value(x, y)
print("Distance to Camera at ({0}, {1}): {2} mm".format(x, y, depth_value), end="\r")
use the depth map - python
關閉攝影機

擷取50張影像之後,關閉攝影機並退出程式。

# Close the camera
zed.close()
close the camera - python

更多範例

想學習如何由相機取得/顯示即時點雲,並調整 depth confidence filters, check the Point Cloud Viewer 範例。

下一步

您現在已經知道如何從 ZED 立體攝影機取得影像與深度資料了。根據您的應用,可參考一下文章:

  • 範例07 Sensor – 取得裝置上的感測器資料,例如 IMU、氣壓與磁力感測器等。
  • 範例04 Camera Tracking – 於3D空間中追蹤攝影機的動作。
  • 範例06 Object Detection – 於3D空間中偵測與追蹤人體。

註:本文經授權之後翻譯自:https://www.stereolabs.com/docs/tutorials/depth-sensing/

影像辨識技術結合機器手臂Q1ARM進行物品分類的實作

$
0
0

本篇的專題教學主要是透過Microsoft LOBE.ai的影像分類神經網路訓練平台進行影像分類模型的訓練,匯出的影像分類神經網路模型將會以Tensorflow-Lite格式進行後續延伸的機器手臂影像分類辨識的操作,本篇教學採用的機器手臂是採用香港的一個Maker所設計的Q1-ARM機器手臂進行機械的運作,這位香港Maker朋友所設計的Q1-ARM機構非常簡單可靠,機械的動作也非常地確實,非常適合所有想嘗試製作機器手臂控制的初學者進行實作,本篇教學也會針對Q1-ARM的機構簡單介紹,而Q1-ARM相關的實作教學基於尊重原作者的智產權,以下提供原作者分享的連結https://wikifactory.com/@jason-workshop/q1-arm-mark-2,請各位有興趣的朋友可以至該原廠網站參考相關實作教學文件,本篇教學的主題將會區分成兩個部份的章節主題進行教學,分別如下:

  1. Q1-ARM的控制核心Linkit-7697微控器的程式設計
  2. 嵌入式(或個人電腦)Python控制核心的程式設計

希望透過以上兩個章節主題,讓每個有興趣實作影像辨識機器手臂控制的朋友都能夠實作出專屬於自己的「人工智慧機器手臂」。完整程式碼請由 cavedu github 取得

分類 技術教學文 標籤 LOBE.ai、Tensorflow-Lite、Classification、Python、Q1ARM
撰寫/攝影 曾俊霖
前情提要 在此之前CAVEDU部落格有一篇「Raspberry Pi 4 之輸送帶影像辨識分類機電整合專題」,該篇主要是透過Teachable Machine進行影像分類神經網路的訓練,並且匯出Tensorflow-Lite模型至樹莓派進行輸送帶的分類操作,透過樹莓派的GPIO的控制,進行伺服馬達與直流輸送帶馬達的控制。
時間 6小時以上 材料表 1.      Q1-ARM

2.      Linkit-7697

3.      Robot Shield V2 Servo Control Board for LinkIt 7697

4.      嵌入式系統或個人電腦

 


本篇專題教學,主要是要針對影像辨識的結果進行機器手臂的動作操作,由於機器手臂是透過Linkit-7697微控器進行控制,本身受限於微控器的運算效能不足以進行影像辨識運算,因此必須結合能夠進行神經網路模型運算的嵌入式系統(或個人電腦)運算能力較強大的平台進行操作,因此本篇專題教學是一個跨平台操作的系統,以下列出這個跨平台操作的系統架構圖。

一、Q1-ARM的操作概念說明

Q1-ARM是一個具有5個自由度的機器手臂,五個自由度分別是:底座(左右旋轉)、下臂(上下旋轉)、上臂(上下旋轉)、腕部(上下旋轉)、夾爪(開閉旋轉),基本上都是透過SG-90伺服馬達進行機構的運動,因此無法乘載過重的負載,但用於機器手臂基礎控制教學是非常合用的,相關機器手臂的組裝檔案連結如下:組裝說明

Q1-ARM是透過聯發科的Linkit-7697微控器進行控制,在本篇專題教學,我們將會採用Arduino IDE開發環境進行程式的設計,完整的Arduino IDE的原始程式碼為 q1arm_serial.ino,請由此取得

以下簡要介紹重要程式片段:

#include <Servo.h>
Servo __myservo16;
Servo __myservo11;
Servo __myservo9;
Servo __myservo8;
Servo __myservo7;
透過標準的Servo.h程式庫進行伺服馬達的設定,總共有5個伺服馬達,控制信號分別接至Linkit-7697的D16、D11、D9、D8、D7數位信號接腳。 D16:底座旋轉 D11:下臂 D9:上臂 D8:腕部 D7:抓爪
if (Serial.available()) {
    // 讀取傳入的字串直到"\n"結尾
    str = Serial.readStringUntil('\n');

    String_to_Int(str, 2);
    Serial.println(SA[0]);
    Serial.println(A[1]);
  }
透過Serial.readStringUntil函數擷取UART串列資料,判斷 決定命令字串是否結束。 String_to_Int(str, 2):擷取字串2欄資料(分別是第0欄與第1欄,第0欄設定「馬達編號」,第1欄設定「馬達轉動角度」。
  if (SA[0] == "數值") {
    int PRE_angle = __myservo16.read();
    if (A[1] >= PRE_angle) {
      for (int angle=PRE_angle;angle<=A[1];angle++){
         __myservo16.write(angle);
         delay(20);
      }
      Serial.println("Servo_OK");
    }
    if (A[1] < PRE_angle) {
      for (int angle=PRE_angle;angle>=A[1];angle--){
         __myservo16.write(angle);
         delay(20);
      }
      Serial.println("Servo_OK");
    }    
  }

擷取字串第0欄位(SA[0]),判斷控制哪個伺服馬達。擷取字串第1欄位(A[1]),判斷伺服馬達的轉動角度。程式會讀取伺服馬達目前的初始角度狀態,決定增加或減少角度。角度控制透過for迴圈逐漸增減伺服馬達轉動角度,防止機器手臂因伺服馬達轉動速度太快導致機器手臂整體晃動不穩的狀況發生。

void String_to_Int(String temp, int count)
{
  int index;

  index = temp.indexOf(',');
  SA[0] = temp.substring(0, index);

  for (int i = 1; i < count; i++) {
    temp = temp.substring(index + 1, temp.length());
    index = temp.indexOf(',');
    SA[i] = temp.substring(0, index);
    A[i] = SA[i].toInt();
  }
}

判斷來自UART接收的字串內容,透過.indexOf(‘,’)判斷各字串的分隔符號。
透過.substring(0, index)取出字串欄位內的資料。

由上述的程式運作可知,Linkit-7697透過USB-UART介面進行串列資料的接收,這樣的操作方式,其實非常適合進行跨平台的操作,只要能夠收發UART資料便可以進行操控。以下便要介紹命令機器手臂動作的運算中心的操作概念。

 

二、嵌入式系統(或個人電腦)運算操作的概念說明

本篇專題教學採用的Python程式運算主要是處理影像分類神經網路模型運算,這部分的運算由於有著較大的運算負荷,因此需要由具有較佳運算較能的嵌入式系統(如:樹莓派、Jetson系列、ASUS-Tinker系列等)或是個人電腦進行操作,此外,為了降低運算的負荷,對於訓練後匯出的神經網路模型,本篇專題教學將以Tensorflow-Lite模型進行後續的影像推論應用。

以下列出整個操作影像辨識與控制機器手臂的流程:

對於影像分類神經網路模型的訓練,本篇專題教學將是以Microsoft LOBE.ai平台進行神經網路模型的訓練,LOBE.ai的操作教學請參考本篇教學:微軟 LOBE ai – 離線訓練影像辨識模型!

此外,由於本篇專題教學為了提高訓練用影像資料集的品質,因此設計了一個蒐集影像照片的Python程式,這個Python主要的功能是能夠在即時影像畫面中指定一個特定的區域,或稱ROI(Region of Interest)區域,進行影像資料的蒐集,可以大幅降低訓練用資料集的照片不必要的影像干擾,使用者可以透過鍵盤的上下左右鍵進行ROI區域的位置,也可以透過鍵盤的「+」與「-」按鍵進行ROI區域的大小調整,在選定好特定的監控區域後按下「Enter」鍵確定拍照區域,接下來便可以按「空白鍵」進行拍照的動作,依照LOBE.ai的運作特性,我們建議針對每個要辨識的類別,約莫進行30張照片的蒐集,並且指定正確的分類資料夾名稱,拍照程式的程式碼為PHOTO_ARM.py,請由此取得。

請注意,操作拍照程式時,必須要確定機器手臂也確實連接至嵌入式系統(或個人電腦)上,並且確定攝影機連接的編號,拍照的檔案都是以Photo開頭命名的JPG圖檔,拍照完成後使用者必須確實轉存到指定的資料夾中。

嵌入式系統Python程式操作的方式:

python3 PHOTO_ARM.py --video 攝影機編號 --com UART連接埠編號
#例如
python3 PHOTO_ARM.py --video 0 --com 5

個人電腦透過Anaconda虛擬環境的操作方式:

 

python PHOTO_ARM.py --video 攝影機編號 --com UART連接埠編號
#例如
python PHOTO_ARM.py --video 0 --com 5

在完成拍照後,請將拍照的照片轉存到自行定義的類別資料夾中,類別資料夾名稱可以自行定義,但必須採用英文或數字,且名稱不可以有空格,在進行LOBE.ai平台運作的時候,也建議以資料集(Data-Set)的方式進行資料集匯入,請參考以下三張畫面截圖。

LOBE.ai在完成匯入訓練資料集與神經網路模型訓練後,本篇專題教學採用Tensorflow-Lite的模型進行匯出模型應用。

在完成匯出Tensorflow-Lite神經網路模型後,便可以進行後續影像辨識控制機器手臂的動作,這部分的程式必須確定以下三件事情都有確實完成連接或設定:

  1. Webcam攝影機完成連接於嵌入式系統(或個人電腦),並且確認攝影機連接編號。
  2. Q1-ARM完成連接於嵌入式系統(或個人電腦),並且確認UART串列通訊埠的編號。
  3. ai訓練後的神經網路模型資料夾與Python程式存放在同一個資料夾中。

 

影像辨識控制機器手臂Python程式,主要是由4個部分構成,分別是:

  1. UART串列資料傳輸設定
  2. Tensorflow-Lite模型匯入
  3. 影像分類推論
  4. 推論結果控制機器手臂

上述的4個部分,系統運算負荷最大的部分主要是在影像分類推論,主要原因在於程式需要多次確認影像辨識的結果,在穩定辨識多次(次數可以由程式內指定)後,才會進行機器手臂的控制操作,以避免機器手臂的錯誤動作。影像辨識控制機器手臂的程式碼為LOBE_WEBCAM_ARM.py,請由此取得。

嵌入式系統Python程式操作的方式:

python3 LOBE_WEBCAM_ARM.py --video 攝影機編號 --com UART連接埠編號 --model "模型資料夾"

例如:

python3 PHOTO_ARM.py --video 0 --com 5 --model "ARM_MODEL_LOBE TFLite"

 

個人電腦透過Anaconda虛擬環境的操作方式:
python LOBE_WEBCAM_ARM.py --video 攝影機編號 --com UART連接埠編號 --model "模型資料夾"

例如:

python PHOTO_ARM.py --video 0 --com 5 --model "ARM_MODEL_LOBE TFLite"

以下針對LOBE_WEBCAM_ARM.py的程式內容,進行簡要說明:

Condition_1 = 'OBJ_1'
Condition_2 = 'OBJ_2'
Condition_3 = 'OBJ_3'
Condition_4 = 'OBJ_4'
Condition_Other = 'Other'
設定影像辨識分類的名稱,這部分必須要和LOBE.ai訓練神經網路模型時,各指定的類別名稱一致。
def get_prediction(image, interpreter, signature):
    影像推論的程式片段(略)
    return outputs
#image是即時影像資料
#interpreter是神經網路模型
#signature是模型參數檔內容
get_prediction是影像推論的函式
def process_image(image, input_shape):
    #影像前處理的程式片段(略)
    return image.reshape(input_shape).astype(np.float32)
#image是即時影像資料
#input_shape是輸入影像的解析度資訊
process_image是影像預處理函式
    COM_PORT = args.com  #COM_PORT變數設定UART通訊連接埠的編號
    BAUD_RATES = 9600  #BAUD_RATES變數設定RAUD為9600BPS
    ser = serial.Serial(COM_PORT, BAUD_RATES)  # 建立 ser 物件啟動序列通訊
if (times==1) and (key_flag == 0) and (Motion_Busy ==0):
    prediction = get_prediction(image, interpreter, signature)
進行影像辨識
if (classification_flag == 1) and (Label_name == Condition_1) and (prob > 0.5):
    Condition_1_count = Condition_1_count + 1
    Condition_2_count = 0
    Condition_3_count = 0
    Condition_4_count = 0
針對Condition_1條件1進行穩定辨識次數的計數。Condition_n條件n,以此類推。
if (Condition_1_count == 50):
    Motion_Busy = 1
    伺服馬達動作角度設定片段程式(略)
     Motion_Busy = 0
Condition_1連續穩定辨識次數達50次時開始進行機器手臂伺服馬達的動作設定。 Condition_n,以此類推。
”降低影像辨識推論的運算負荷,times>=10:表示每10個Frame進行一次影像辨識推論。”
read_dir_key = cv2.waitKeyEx(1)
if (read_dir_key != -1):
    print(read_dir_key)
    #進行各種按鍵偵測的動作程式片段(略)
    # 按 q鍵 或 Esc鍵 結束程式    
elif ((read_dir_key == 113) or (read_dir_key == 27)):
    key_detect = 1
偵測各種按鍵
if (key_flag == 1):
    cv2.rectangle(image_src,
        (box_left,box_top),(box_left+scale,box_top+scale),
        (0,255,0),2)
elif (key_flag == 0):
    cv2.rectangle(image_src,
         (box_left,box_top),(box_left+scale,box_top+scale),
        (0,0,255),2)
即時繪製影像辨識框
box_center_x = box_left + int(scale / 2)
box_center_y = box_top + int(scale / 2)

dw = int(frame_width / 2) - box_center_x
dh = frame_height - box_center_y
angle = (math.atan(dw / dh)) / math.pi * 180

cv2.line(image_src,
     (int(frame_width / 2),frame_height),
     (box_center_x,box_center_y),
     (0,255,255),2)

 cv2.putText(image_src, str(round(angle,3)),
     (int(frame_width * 0.6),30), cv2.FONT_HERSHEY_SIMPLEX, 1,
     (0,255,255), 6, cv2.LINE_AA)
cv2.putText(image_src, str(round(angle,3)),
    (int(frame_width * 0.6),30), cv2.FONT_HERSHEY_SIMPLEX, 1,
    (0,0,0), 2, cv2.LINE_AA)
繪製辨識框中心的連線,並且標示角度值。
cv2.imshow('Detecting_1 ....',image_src)
顯示即時影像畫面

即時影像辨識與機器手臂的控制,影片連結如下

 


相關文章

 

 

ZED 景深攝影機範例#4/8 – Depth Perception

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例04 – Camera Tracking

本範例說明如何取得即時的相機的位置與方位,程式會在成功擷取 1000 筆位置資訊後結束。在此假設您已完成先前的範例。

註:原廠頁面每段都提供了 C++ / Python / C# 的範例程式,在此只列出 Python 範例

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

程式總覽

開啟攝影機

如先前所有範例,在此會建立、設定並開啟ZED攝影機。

# Create a ZED camera object
zed = sl.Camera()

# Set configuration parameters
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720  # Use HD720 video mode (default fps: 60)
# Use a right-handed Y-up coordinate system
init_params.coordinate_system = sl.COORDINATE_SYSTEM.RIGHT_HANDED_Y_UP
init_params.coordinate_units = sl.UNIT.METER  # Set units in meters

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
open the camera

啟用位置追蹤

攝影機開啟之後,還需要透過 enablePositionalTracking() 來啟用位置追蹤模組,這樣才能取得 ZED 攝影機的位置與方位。

# Enable positional tracking with default parameters
tracking_parameters = sl.PositionalTrackingParameters()
err = zed.enable_positional_tracking(tracking_parameters)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
enable positional tracking

先前的範例只使用了ZED SDK預設的追蹤參數。完整參數清單請參考 Tracking API

擷取姿勢資料

現在動作追蹤已經啟用了,在此透過一個迴圈來擷取攝影機位置。攝影機的位置是由 Pose 類別所給定,該類別已包含了攝影機的位移與方位狀態,以及影像的時間戳記與追蹤信心指數。

每個位置都會聯於某個參考坐標系,ZED SDK 提供了兩種參考坐標系:REFERENCE_FRAME::WORLDREFERENCE_FRAME::CAMERA。更多資訊請參考Coordinate Frames 文件。ZED 的三軸方位示意如下:

本範例會在 World Frame 中取得攝影機位置。

# Track the camera position during 1000 frames
i = 0
zed_pose = sl.Pose()
runtime_parameters = sl.RuntimeParameters()
while i < 1000:
    if zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
        # Get the pose of the left eye of the camera with reference to the world frame
        zed.get_position(zed_pose, sl.REFERENCE_FRAME.WORLD)

        # Display the translation and timestamp
        py_translation = sl.Translation()
        tx = round(zed_pose.get_translation(py_translation).get()[0], 3)
        ty = round(zed_pose.get_translation(py_translation).get()[1], 3)
        tz = round(zed_pose.get_translation(py_translation).get()[2], 3)
        print("Translation: Tx: {0}, Ty: {1}, Tz {2}, Timestamp: {3}\n".format(tx, ty, tz, zed_pose.timestamp.get_milliseconds()))

        # Display the orientation quaternion
        py_orientation = sl.Orientation()
        ox = round(zed_pose.get_orientation(py_orientation).get()[0], 3)
        oy = round(zed_pose.get_orientation(py_orientation).get()[1], 3)
        oz = round(zed_pose.get_orientation(py_orientation).get()[2], 3)
        ow = round(zed_pose.get_orientation(py_orientation).get()[3], 3)
        print("Orientation: Ox: {0}, Oy: {1}, Oz {2}, Ow: {3}\n".format(ox, oy, oz, ow))
retrieve camera position

慣性資料

如果您使用的型號包含了IMU (例如 ZED 2 與 ZED Mini),位置追蹤模組會融合視覺與慣性資料來提供改良後的位置追蹤。

存取 IMU 感測器資料的程式碼如下:

sensors_data = sl.SensorsData()
zed.get_sensors_data(sensors_data, sl.TIME_REFERENCE.IMAGE)
zed_imu = zed_sensors.get_imu_data()
# Get IMU orientation
zed_imu_pose = sl.Transform()
ox = round(zed_imu.get_pose(zed_imu_pose).get_orientation().get()[0], 3)
oy = round(zed_imu.get_pose(zed_imu_pose).get_orientation().get()[1], 3)
oz = round(zed_imu.get_pose(zed_imu_pose).get_orientation().get()[2], 3)
ow = round(zed_imu.get_pose(zed_imu_pose).get_orientation().get()[3], 3)
print("IMU Orientation: Ox: {0}, Oy: {1}, Oz {2}, Ow: {3}\n".format(ox, oy, oz, ow))
# Get IMU acceleration
acceleration = [0,0,0]
zed_imu.get_linear_acceleration(acceleration)
ax = round(acceleration[0], 3)
ay = round(acceleration[1], 3)
az = round(acceleration[2], 3)
print("IMU Acceleration: Ax: {0}, Ay: {1}, Az {2}\n".format(ax, ay, az))
access IMU sensor data

更多關於 Camera-IMU 與其他板載感測器請參考 Sensors section.

關閉攝影機

追蹤 ZED 攝影機中的姿勢狀態 1000 個畫面(幀)之後,停用追蹤模組並關閉攝影機。

# Disable positional tracking and close the camera
zed.disable_positional_tracking();
zed.close()
close the camera
更多範例

如果您想知道如何在3D視窗中取得與顯示攝影機的即時位置與方位、位置資料轉換以及如何改變坐標系與坐標單位,請參考 Motion Tracking 範例程式。

下一步

請接續閱讀[範例05 Spatial Mapping]與 [範例06 Object Detection] 來深入學習。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/positional-tracking/

ASUS Tinker Board 2S 安裝 Android-11 V2.0.3版作業系統與應用軟體

$
0
0

華碩AIoT團隊開發的Tinker Board 2S除了可以安裝Linux作業系統進行各種應用外,開發團隊亦針對Android的使用者開發了適用於Tinker Board 2S嵌入式系統的Android作業系統,目前華碩開發團隊在原廠網站上可供使用者進行下載的Android作業系統版本是Android-11 V2.0.3版。

本篇教學文主要針對Tinker Board 2S進行Android-11 V2.0.3版進行安裝教學,並且簡單介紹在Tinker Board 2S在Android-11下,基本系統設定與預設環境下的APP操作環境介紹。
本篇教學文也會透過APP Inventor 2設計幾個簡單的人機介面測試APP,並且將這些APP傳至Tinker Board 2S進行測試。

此外,Google Tensorflow亦為了Android作業系統設計了一系列的測試範例程式,給所有想針對Android作業系統開發相關人工智慧主題的開發者,我們將會在下一篇教學文當中詳細介紹如何將Tensorflow-Lite影像辨識的範例程式編譯並傳輸至Tinker Board 2S中進行測試。

本篇教學文將以下列項目逐項說明介紹相關操作流程:

一、 下載與燒錄映像檔
二、 基本環境操作介紹
三、 系統設定
四、 預設APP介紹
五、 自行設計APP傳輸至Tinker Board 2S操作

撰寫/攝影 曾俊霖
前情提要 Tinker Board 2 / 2S 推出至今,搭配Debian作業系統操作的版本也從先前介紹的2.0.0版更新到了2.0.3版,上次的安裝教學內容連結如下:https://blog.cavedu.com/2021/09/01/tinker-board-2s-2/
時間 6小時以上 材料表 ASUS  Tinker Board 2S / 2GB

 


一、下載與燒錄映像檔

1. 華碩AIoT團隊提供各種作業系統燒錄映像檔的下載連結

2. 請選擇 Tinker Board 2 Android 11 V2.0.3,接著下載。
3. 下載完成後,可以透過balenaEtcher軟體進行SD卡的燒錄,本篇教學文採用32GB的SD卡進行燒錄。
整個Android作業系統大約會占用SD卡4.29GB。

4. 將燒錄好的SD卡重新安裝到Tinker Board 2S進行開機,首次開機因為會有許多硬體設定的過程,所以首次開機會需要較久的時間,待完成所有的開機設定完成後,之後的完成開機的時間就會較短了。

完成開機進入桌面操作環境

二、基本環境操作介紹

1. 滑鼠操作的概念,和一般Linux GUI或是Windows GUI操作概念是不一樣的,因為Android大多是安裝在行動裝置上,所以滑鼠的操作就必須模仿手指按壓滑動觸控螢幕的操作方式,以下提出幾種操作方式讓大家知道如何操作Android桌面環境:
(1). 滑鼠至於顯示畫面上緣 > 按住左鍵 > 往下滑動滑鼠游標:開啟「設定」選單

(2). 滑鼠至於顯示畫面下緣 > 按住左鍵 > 往上滑動滑鼠游標:開啟「程式集」選單

(3). 主頁按鈕區操作


三、 系統設定

1. 開啟程式集畫面

2. 各項Android設定

(1). Wi-Fi設定

點選Network & Internet選項


<1> 確定Wi-Fi開關開啟 > <2>點選Wi-Fi選項


<1>確認Use Wi-Fi開關開啟 > <2>點選適當Wi-Fi的SSID


<1>輸入SSID的密碼 > <2>點選CONNECT連線即可


(2). 設定與個人電腦之間的連線關係

點選 Connected devices 選項

點選USB選項


以下為 USB 選項中的功能說明:

  • File Transfer:把Tinker Board 2S當成硬碟進行存取
  • USB tethering:透過USB連線將Tinker Board 2S當成網路熱點
  • PTP:把Tinker Board 2S當成攝影機僅存取多媒體檔案

四、預設APP介紹

首頁預設APP

1. 照相機

2. 通訊錄

3. 媒體撥放器(Music)

音樂播放

4. 媒體撥放器(Video)

影片播放 

5. 相簿

6. 網頁瀏覽

五、自行設計APP傳輸至Tinker Board 2S操作

若要自行設計APP並且要傳輸至Tinker Board 2S時,則必須先透過個人電腦PC進行APP的程式設計,並且將APP程式編譯成APK檔,透過USB Type-C傳輸線連接至Tinker Board 2S, 透過Tinker Board 2S的Android作業系統進行APK檔的安裝即可,但在此之前必須先確定Tinker Board 2S的USB傳輸模式要先切換「File Transfer」模式才能夠進行檔案的傳輸。

1. 設定Tinker Board 2S的USB傳輸模式為「File Transfer」
(1). 設定與個人電腦之間的連線關係

點選Connected devices選項

點選USB選項

點選File Transfer選項

(2). 個人電腦透過APP Inventor 2設計簡單人機介面程式,有關APP Inventor 2相關教學內容,可參考「App Inventor 中文學習網」,網址如下:http://www.appinventor.tw/

(3). 將APP Inventor 2 程式編譯打包成APK檔,在這裡我們採用以有關人工智慧影像分類這個主題進行示範,請根據這篇文章使用 PIC 網站訓練一個影像分類模型吧!相關教學文連結如下:http://www.appinventor.tw/ai_pic_2

選擇Build > Android App (.apk)


選擇 Download .apk now


PC本機,找到 Tinker Board 2 裝置,點兩下進入。

將APK檔放置於Tinker Board 2S的Downloads資料夾中

(4). 在Tinker Board 2S的Downloads資料夾上安裝APK檔。

找到 File

至Downloads > 點選欲安裝的APK檔


選擇CONTINUE

選擇 INSTALL

選擇OPEN

水果影像分類測試APP,正常運作!開心啊

後記:

本篇教學文主要是針對Tinker Board 2S安裝Android-11 V2.0.3版進行各種基礎環境的操作進行說明,並且透過APP Inventor 2設計APP安裝至Tinker Board 2S進行測試,我們將在下一篇教學文分享如何在Tinker Board 2S Android作業系統上安裝並執行Tensorflow-Lite各種範例,就請各位拭目以待囉!

相關文章


ZED 景深攝影機範例#5/8 – Spatial mapping

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例05 – 空間映射

本範例將說明如何操作 ZED 立體攝影機來取得您所在環境的即時 3D 重建結果。程式會對 500 張畫面進行空間映射、取得網格、濾波並將其存成一個 OBJ 檔。

註:原廠頁面每段都提供了 C++ / Python / C# 的範例程式,在此只列出 Python 範例

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

程式總覽

開啟攝影機

首先要初始化攝影機。本範例將使用 HD720 / 60 fps 模式 (這樣的視野較廣) 來確認攝影機追蹤結果是否可靠。另外也選定右手坐標系統,Y軸朝上為正,這也是 Meshlab 這類 3D 檢視軟體較常採用的坐標系統。

# Create a ZED camera object
zed = sl.Camera()

# Set configuration parameters
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720  # Use HD720 video mode (default fps: 60)
init_params.coordinate_system = sl.COORDINATE_SYSTEM.RIGHT_HANDED_Y_UP # Use a right-handed Y-up coordinate system
init_params.coordinate_units = sl.UNIT.METER  # Set units in meters

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
open the camera

啟用位置追蹤

首先用 enablePositionalTracking() 來啟用位置追蹤,接著才能啟用空間映射。

tracking_parameters = sl.PositionalTrackingParameters()
err = zed.enable_positional_tracking(tracking_parameters)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
enable positional tracking

啟用空間映射

空間映射的前置作業與位置追蹤類似:先建立 SpatialMappingParameters 類別,並以此作為參數來呼叫 enableSpatialMapping()

mapping_parameters = sl.SpatialMappingParameters()
err = zed.enable_spatial_mapping(mapping_parameters)
if err != sl.ERROR_CODE.SUCCESS:
    exit(1)
enable spatial mapping

本範例無法詳細介紹 SpatialMappingParameters 類別,更多資訊請參考 API 文件。

執行即時 3D 重建

對指定區域進行空間映射時,不需要在 grab() 迴圈中呼叫任何函式。ZED SDK 會在背景檢查是否有新的影像、深度與位置資訊,並根據這些資料以非同步方式自動建立 3D 地圖。

本範例只會擷取 500 張畫面 (frame)、檢查 3D 映射狀態,接著停止迴圈來取得最終的網格。

# Grab data during 3000 frames
i = 0
py_mesh = sl.Mesh()  # Create a Mesh object
runtime_parameters = sl.RuntimeParameters()
while i < 3000:
    if zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
        # In background, spatial mapping will use new images, depth and pose to create and update the mesh. No specific functions are required here.
        mapping_state = zed.get_spatial_mapping_state()
        # Print spatial mapping state
        print("\rImages captured: {0} / 3000 || {1}".format(i, mapping_state))
        i = i + 1
run live 3D reconstruction

取得網格

取得 500 張畫面的 3D 地圖之後,即可透過 extractWholeSpatialMap() 語法取得儲存於Mesh 物件中的網格。本函式會持續執行直到取得完整的網格為止。

err = zed.extract_whole_spatial_map(py_mesh) # Extract the whole mesh
extract mesh

注意:映射過程中可能是以非同步方式來取得網格,詳細資訊請參考 Using Spatial Mapping API

This mesh can be filtered (如有必要) 來移除重複的點與不必要的面。接著將網格存為 OBJ 檔以便後續使用。

filter_params = sl.MeshFilterParameters()
filter_params.set(sl.MESH_FILTER.LOW)
py_mesh.filter(filter_params) # Filter the mesh (remove unnecessary vertices and faces)
py_mesh.save("mesh.obj") # Save the mesh in an obj file
filter and save as .obj file

停用模組並離開程式

網格取得並存檔完成之後,記得要依序停用映射與追蹤模組(順序要對!),並在結束程式之前關閉攝影機。

# Disable tracking and mapping and close the camera
zed.disable_spatial_mapping()
zed.disable_positional_tracking()
zed.close()
disable modules and exit

更多範例

如果您想知道如何擷取周遭環境的即時 3D 網格,並將其作為圖層顯示於攝影機影像上的畫,請參考 Live 3D Reconstruction 範例。

下一步

您現在已經知道如何取得來自 ZED 立體攝影機的 image, depth, odometry and 3D mapping data。後續的物件偵測範例將說明如何在 3D 環境中偵測與追蹤人體。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/spatial-mapping/

Tinker Board 2S Android-11 V2.0.3 執行 Tensorflow-Lite 範例程式教學

$
0
0

華碩Tinker Board 2S的Android作業系統除了可以運行一般的圖形化人機介面GUI的APP之外,透過其強大的CPU功能,其實也能夠運作相關人工智慧影像辨識的各種應用,本篇教學將以Tensorflow-Lite模型在Tinker Board 2S上的運作進行說明。

Tensorflow是Google公司開發與維護的神經網路模型設計工具,Google針對不同的應用情境也開發出各種神經網路模型格式,而一般應用於嵌入式系統(如:Tinker Board  2S或是各類行動裝置)的神經網路模型運作,Google Tensorflow則是建議採用Tensorflow-Lite的模型進行神經網路的運算。

Tensorflow-Lite是一種輕量化神經網路模型,與標準的Tensorflow模型相較之下,Tensorflow-Lite 輕微犧牲了精確度的表現,而縮短了推論運算時所需的時間,相關Tensorflow-Lite的基本運作原理,請參考原廠連結:
https://www.tensorflow.org/lite/guide

在Google Tensorflow-Lite原廠的規劃裡,為了強化Android行動裝置操作各種人工智慧應用,將Tensorflow-Lite的各種應用全面加入了在Android作業系統的操作支援,為了讓使用者能夠快速上手,因此在所有的範例程式,都加入適用Android的操作範例,本篇教學將會帶領每個使用者操作這些範例,相關範例的連結如下:
https://www.tensorflow.org/lite/examples

本篇教學文將以下列項目逐項說明介紹相關操作流程:
一、 Tinker Board 2S USB 傳輸模式設定
二、 個人電腦Android Studio 3.2的安裝與設定
三、 Android Studio 3.2載入範例程式並編譯程式為APK檔
四、 傳輸APK檔至Tinker Board 2S並且執行範例程式APP

如何在 ASUS Tinker 2S 上安裝 Android 作業系統請點我

撰寫/攝影 曾俊霖
前情提要 Tinker Board 2 / 2S 推出至今,搭配Debian作業系統操作的版本也從先前介紹的2.0.0版更新到了2.0.3版,上次的安裝教學內容連結如下:https://blog.cavedu.com/2021/09/01/tinker-board-2s-2/
時間 6小時以上 材料表 ASUS  Tinker Board 2S / 2GB

一、Tinker Board 2S USB 傳輸模式設定

若要將Tensorflow-Lite範例程式APP傳輸至Tinker Board 2S時,則必須先透過個人電腦PC進行APP的程式設計,並且將APP程式編譯成APK檔,透過USB Type-C傳輸線連接至Tinker Board 2S, 透過Tinker Board 2S的Android作業系統進行APK檔的安裝即可,但在此之前必須先確定Tinker Board 2S的USB傳輸模式要先切換「File Transfer」模式才能夠進行檔案的傳輸。

1. 開啟程式集畫面

2. 點選Connected devices選項

3. 點選USB選項

4. 點選File Transfer選項

二、個人電腦Android Studio 3.2的安裝與設定

按照Tensorflow-Lite原廠網站的教學文件連結: https://www.tensorflow.org/lite/examples/image_classification/overview
當中有提到一個很重要的操作工具「Android Studio 3.2」必須事先安裝到個人電腦中,Android Studio是一個Android Java程式編譯工具,其功能可以將程式進行編譯與整合包裝成可安裝的APK程式套件包,Android Studio 3.2相關安裝教學請參考以下連結:https://developer.android.com/studio
在下載相關安裝檔後,便依預設的設定(包含路徑設定)進行安裝即可,以下不再贅述。

三、Android Studio 3.2載入範例程式並編譯程式為APK檔

1. 啟動Android Studio 3.2

2. 設定範例程式GitHub取得連結路徑
(1). 點選Get from VCS

(2). Version Control設定為:Git
URL設定為:https://github.com/tensorflow/examples.git

(3). 點選Open

(4). 選取想要操作的專案

(5). 在彈出的訊息欄點選upgraded

(6). 點選Begin Upgrade
(7). 建議的更新項目「全部打勾」->  點選Run selected steps

(8). 工作列點選 Build -> 子選項點選Build Bundle(s) / APK(s) ->
(9). 點開右下角Build APK(s)訊息框 -> 點選 locate

(10). 將app-debug.apk檔案移至Tinker Board 2S的Downloads資料夾中

四、傳輸APK檔至Tinker Board 2S並且執行範例程式APP

1. 個人電腦進入本機 -> 進入Tinker Board 2

2. 將APK檔放置於Tinker Board 2S的Downloads資料夾中

3. 在Tinker Board 2S 的 File app 中,找到 Downloads資料夾,並安裝方才傳輸的APK檔。

4. 在Downloads資料夾中,點選欲安裝的APK檔

5. 選擇CONTINUE

6. 點選INSTALL

7. 點選OPEN

8. 點選WHILE USING THE APP選項,允許本 app 在執行期間使用攝影機

9. 開啟影像辨識程式完成
可看到即時影像的辨識推論結果,讚吧~

後記

Tinker Board 2S在運在多媒體運算方面真的是充分發揮了Tinker Board 2S的強項,接下來Cavedu教育團隊將以Tinker Board 2S為基礎開發各種「車裝或居家多媒體播放系統」的各種專案,屆時會針對Linux與Android作業系統進行多媒體播放系統的程式與電路設計,而這個多媒體播放系統也會結合相關物聯網通訊方面的技術進行整合,希望能夠將Tinker Board 2S的性能發揮到淋漓盡致,屆時也歡迎各位有興趣的朋友們隨時關注CAVEDU教育團隊技術部落格吧!

相關文章

 

Blynk IoT 物聯網服務結合 Wio Terminal 開發板:建立裝置、設定儀表板、測試連線

$
0
0

本文將說明如何操作 Blynk IoT 物聯網雲服務,包含建立樣板、建立裝置與測試連線。在找資料的過程中,發現 Blynk 大改版了,包含智慧型手機 app 也推出了新版,當然這是指功能更豐富,支援板子更多,連線也更方便穩定。就來看看怎麼做吧!

Wio Terminal Arduino IDE 設定

請參考本文下載 Arduino 1.8 並安裝 Wio Terminal 完成,也歡迎參考阿吉老師(就是我)所錄製的  Wio terminal tinyML 影片,使用光感測器搭配 Edge Impulse 網站來完成手勢辨識專案,很好玩。

Wio Terminal Wi-Fi 設定

根據原廠說明,如要使用 Wio Terminal 的 Wi-Fi 與藍牙功能,需要使用 ambd_flash_tool 來刷新韌體。詳細作法請參考 https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview/

完成之後,請用 Arduino Board Manager 來更新 wio terminal 板子,並用 Library Manager 來安裝以下函式庫。這一系列步驟有點繁瑣,請耐心完成喔。

  1. Seeed_Arduino_rpcWiFi
  2. Seeed_Arduino_rpcUnified
  3. Seeed_Arduino_mbedtls
  4. Seeed_Arduino_FS
  5. Seeed_Arduino_SFUD

pir

pir

註冊 Blynk 帳號

Blynk 是一個好用且功能多多的雲端服務,有免費也有付費服務。可讓您輕鬆控制位於世界各地的聯網裝置。請由此申請 Blynk 免費帳號: https://blynk.io/

順利登入後即可看到本畫面,點選 [+ New Template]

網站上可以看到 Free / Plus / Pro 三個不同等級的收費標準,當然就是一分錢一分貨。免費只能有兩個裝置而已呢。

建立樣板 template

點選[+ New Template]之後,請在跳出視窗中輸入以下資訊,NAME欄位請隨意取名,HARDWARE欄位請選擇 Seeed Wio TerminalCONNECTION TYPE請選擇 WiFi,完成之後請按 Done

樣板完成,可以看到樣板的基本設定頁面,這邊先不用太細緻設定,直接進入下一個步驟來建立裝置。

建立裝置 device

回到主頁(左側放大鏡符號),按下 [+ New Device]。

在跳出視窗中,點選 [From template] 來從上一部所建立的樣板來延伸出裝置,您也可以試試看其他建立裝置的方式。

接著選擇 myWio (方才建立的樣板),並設定 DEVICE NAME (隨意取名),完成之後按下 Create

完成之後,即可看到裝置主頁,右側可以看到三項重要的資訊,後續要填在您開發板的程式中:BLYNK_TEMPLATE_ID , BLYNK_DEVICE_NAMEBLYNK_AUTH_TOKEN

設定儀表板

點選 Web Dashboard (網路儀表板) 標籤,可看到以下畫面。稍候使用的 Blynk Blink 範例只是單純測試連線,所以無需設定。後續範例會介紹如何設定,這也是我們與裝置互動的介面,非常重要。

空白的 web dashboard

測試連線

使用 Example Blynk 網站來產生程式樣板,您只要修改相關設定即可。如下圖,Board 請選擇 Wio TerminalConnection 請選擇 rpcWiFi,會自動在左下的 Example 選單帶入對應的範例程式。

請選擇 Blynk Blink 範例,在此的 Blink 應該相當於 Hello world 的意思,所以程式碼中可看到無任何硬體腳位控制的程式碼。重點在於讓 Wio terminal 開發板順利透過 Wi-Fi連上 Blynk 伺服器。

/*************************************************************

  You’ll need:
   - Blynk IoT app (download from App Store or Google Play)
   - Wio Terminal board
   - Decide how to connect to Blynk
     (USB, Ethernet, Wi-Fi, Bluetooth, ...)

  There is a bunch of great example sketches included to show you how to get
  started. Think of them as LEGO bricks  and combine them as you wish.
  For example, take the Ethernet Shield sketch and combine it with the
  Servo example, or choose a USB sketch and add a code from SendData
  example.
 *************************************************************/

// Template ID, Device Name and Auth Token are provided by the Blynk.Cloud
// See the Device Info tab, or Template settings
#define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Device"
#define BLYNK_AUTH_TOKEN            "YourAuthToken"


// Comment this out to disable prints and save space
#define BLYNK_PRINT Serial


#include <rpcWiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleWioTerminal.h>

char auth[] = BLYNK_AUTH_TOKEN;

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

void setup()
{
  // Debug console
  Serial.begin(115200);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
}

void loop()
{
  Blynk.run();
  // You can inject your own code or combine it with other sketches.
  // Check other examples on how to communicate with Blynk. Remember
  // to avoid delay() function!
}
Blynk Blink

設定好相關資訊(Blynk以及 Wi-Fi相關資訊,已於上述程式碼中強調之後,請透過 Arduino IDE 將程式碼上傳到 Wio Temminal,即可在 Serial monitor 中看到板子順利連上 Blynk 伺服器。讚喔,我們後續會用智慧植栽的主題,各用一篇來分別說明 Blynk 控制 Wio Terminal 所連接的澆水馬達,以及 Blynk 接收 Wio Terminal 感測器資料。

Wio 試著連上 Blynk server

ZED 景深攝影機範例#6/8 – Object detection

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例06 – 3D 物件偵測

本範例將說明如何操作 ZED 3D 攝影機對空間(僅適用 ZED2 / ZED2i) 中的人體進行偵測、分類與定位。偵測與定位可於靜止或移動的攝影機狀態下進行。

註:原廠頁面每段都提供了 C++ / Python / C# 的範例程式,在此只列出 Python 範例。

以下執行畫面來自 ZED 原廠文件,除了可看到正確框住人體之外,也有標示不同ID與移動軌跡線(下右圖):

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

程式總覽

開啟攝影機

本範例將使用 ZED SDK 中的 Object Detection AI 模組。如先前範例,首先要建立、設定並開啟攝影機。

# Create ZED objects
zed = sl.Camera()
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
init_params.sdk_verbose = True

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    # Quit if an error occurred
    exit()
open the camera

啟用 3D 物件偵測

在啟用物件偵測之前要先指定 AI 模組的 ObjectDetectionParameters。本範例使用設定如下:

# Define the Object Detection module parameters
detection_parameters = sl.ObjectDetectionParameters()
detection_parameters.image_sync = True
detection_parameters.enable_tracking = True
detection_parameters.enable_mask_output = True

# Object tracking requires camera tracking to be enabled
if detection_parameters.enable_tracking:
    zed.enable_positional_tracking()
enable 3D object detection
  • image_sync:決定要在每一張畫面中都偵測物件,或在獨立執行緒中以非同步方式執行。
  • enable_tracking:決定是否要在不同畫面中追蹤物件,並盡可能保留相同的 ID。一定要先啟用 positional tracking 才能從不同的攝影機動態中獨立偵測物件的動作。
  • enable_mask_output:對已偵測到的物件輸出 2D 遮罩。由於這需要額外的運算,如果不會用到的話請將其停用。

啟用物件偵測時會一併載入 AI 模型,這項操作需要幾秒鐘。首次使用 AI 模型時,模型會根據您的硬體進行大約數分鐘的最佳化作業,不過別擔心,這個神經網路模型最佳化作業只要執行一次就好。

print("Object Detection: Loading Module...")
err = zed.enable_object_detection(detection_parameters)
if err != sl.ERROR_CODE.SUCCESS:
    print("Error {}, exit program".format(err))
    zed.close()
    exit()
load module and check errors

取得物件資料

使用 retrieveObjects() 函式搭配用於儲存物件資料的 Objects 參數,即可取得影像中已偵測到的物件相關資料。

由於已啟用 image_sync,因此對於每個 grab 呼叫而言,影像都會被送入 AI 模組中並輸出各 frame 中已偵測到的物件。另外也把信心閾值設為 40,來保留一定信心程度以上的偵測結果。

# Set runtime parameter confidence to 40
detection_parameters_runtime = sl.ObjectDetectionRuntimeParameters()
detection_parameters_runtime.detection_confidence_threshold = 40

objects = sl.Objects()

# Grab new frames and detect objects
while zed.grab() == sl.ERROR_CODE.SUCCESS:
    err = zed.retrieve_objects(objects, detection_parameters_runtime)

    if objects.is_new:
        # Count the number of objects detected
        print("{} Object(s) detected".format(len(objects.object_list)))

        if len(objects.object_list):
            # Display the 3D location of an object
            first_object = objects.object_list[0]
            position = first_object.position
            print(" 3D position : [{0},{1},{2}]".format(position[0],position[1],position[2]))

            # Display its 3D bounding box coordinates
            bounding_box = first_object.bounding_box
            print(" Bounding box 3D :")
            for it in bounding_box:            
                print(" " + str(it),end='')
detect objects with AI module

停用模組並離開程式

退出程式之前需要先停用各模組並關閉攝影機。請注意也可透過 zed.close() 語法來正確停用所有啟用中的模組。有必要的話,也可透過 destructor 自動呼叫close() 函式。

# Disable object detection and close the camera
zed.disable_object_detection()
zed.close()
disable modules and exit

這樣就完成啦!

下一步

您現在已經知道如何取得來自 ZED 立體攝影機的影像、深度與 3D 物件資料。如果想要偵測場景中的不同物體,並在即時點雲中顯示該物體的 3D 邊界框的話,請參考 3D Object Detection 範例。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/3d-object-detection/

ZED 景深攝影機範例#7/8 – Using sensors

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例07 – 取得感測器資料

本範例將說明如何取得 ZED 3D cameras (ZED 2, ZED Mini) 上的 IMU、氣壓與磁力感測器資料。本範例會持續執行5秒鐘來取得感測器資料,並將相關數值顯示於終端機。以下圖片來自 ZED 原廠文件

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

程式總覽

開啟攝影機

如先前範例,首先要建立、設定並開啟 ZED 3D攝影機。由於本範例不會用到深度資訊,請用 DEPTH_MODE::NONE 來停用深度擷取功能。

# Create a ZED camera object
zed = sl.Camera()

# Set configuration parameters
init_params = sl.InitParameters()
# No depth computation required here
init_params.depth_mode = sl.DEPTH_MODE.NONE

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS :
    print(repr(err))
    zed.close()
    exit(1)
open the camera

取得感測器資料

您可由 SensorsData 類別來取得攝影機上各感測器的資料。請用 getSensorsData() 語法取得感測器資料,如下:

sensors_data = sl.SensorsData()
while zed.grab() == sl.ERROR_CODE_SUCCESS:
    zed.get_sensors_data(sensors_data, sl.TIME_REFERENCE.CURRENT)
capture sensor data

在此使用 sl.TIME_REFERENCE.CURRENT 來取得各感測器的最新可用資料。也可用其他語法取得同時接近攝影機最新影像時間點的感測器資料。

更多關於參考時間請參考 Time Synchronization 文件。

更新感測器資料

由於攝影機上的各感測器的採樣速度各有不同,因此資料更新頻率當然也不同。為了得知指定感測器是否已更新,就會比較其時間戳記(作為唯一辨識子)。如果相同代表感測器資料未更新。

本範例將使用 TimestampHandler 基礎類別來儲存時間戳記並檢查資料更新狀態。

ts_handler = TimestampHandler()
if ts_handler.is_new(sensors_data.get_imu_data()):
    # sensors_data.get_imu_data() contains new data
store timestamps and check for data update

如果有新的資料,就顯示於終端機:

# Check if IMU data has been updated
if ts_handler.is_new(sensors_data.get_imu_data()):
    quaternion = sensors_data.get_imu_data().get_pose().get_orientation().get()
    print("IMU Orientation: {}".format(quaternion))
    linear_acceleration = sensors_data.get_imu_data().get_linear_acceleration()
    print("IMU Acceleration: {} [m/sec^2]".format(linear_acceleration))
    angular_velocity = sensors_data.get_imu_data().get_angular_velocity()
    print("IMU Angular Velocity: {} [deg/sec]".format(angular_velocity))

# Check if Magnetometer data has been updated
if ts_handler.is_new(sensors_data.get_magnetometer_data()):
    magnetic_field_calibrated = sensors_data.get_magnetometer_data().get_magnetic_field_calibrated()
    print("Magnetometer Magnetic Field: {} [uT]".format(magnetic_field_calibrated))

# Check if Barometer data has been updated 
if ts_handler.is_new(sensors_data.get_barometer_data()):
    magnetic_field_calibrated = sensors_data.get_barometer_data().pressure
    print("Barometer Atmospheric pressure: {} [hPa]".format(sensors_data.get_barometer_data().pressure))
display new data in console

一般來說,您應該不需要檢查感測器是否可用。如果感測器不可用的話,其資料會包含 NAN 且時間戳記為 0

關閉攝影機

順利取得感測器資料之後,別忘了在結束程式之前關閉攝影機。

# Close the camera
zed.close()
close the camera

下一步

您現在已經知道如何由 ZED 2 / ZED Mini 立體攝影機取得感測器資料了,更多資訊請參考以下文件:

Blynk IoT 物聯網服務2 –使用 Blynk 控制 Wio Terminal 與沉水馬達

$
0
0

本篇將延續上一篇[Blynk IoT 物聯網服務結合 Wio Terminal 開發板:建立裝置、設定儀表板、測試連線],進一步說明如何使用Blynk IoT 物聯網服務控制 Wio Terminal 沉水馬達,這相當於透過雲服務來達到跨網段控制裝置的效果。

Blynk IoT 物聯網服務結合 Wio Terminal 開發板:建立裝置、設定儀表板、測試連線

材料清單

智慧植栽套件材料清單如下,其中  5 6 項會在下一篇範例登場,本篇先用不到,在此先列出。智慧植栽嘛,一定要有相關感測器,當然也希望能透過雲服務來監控植栽的狀況囉!

  1. Wio Terminal 開發板
  2. 繼電器MOSFET模組
  3. 被實驗的盆裁跟水源
  4. 抽水馬達及對應的電源
  5. 土壤濕度感測器(建議買防水的,如果你只打算用一次,就準備便宜的就好)
  6. 溫溼度感測器(DHT11、20、22)都可以,本範例使用DHT20

示意圖如下,請注意土壤溼度感測器因為拍照關係所以拉比較高,實際使用時請插入到上下箭頭區間之間的位置。

 

情境說明

本文包含兩個範例

  1. 土壤溼度感測器控制沉水馬達
  2. Blynk 雲服務控制沉水馬達

使用設備如下,您可以依照方便選用相容的裝置,但如果不想修改程式的話,開發板還是選 Wio terminal 最快囉。

Wio Terminal

請參考本文完成 Wio Terminal 的相關設定,您需要下載 Arduino 1.8 並安裝 Wio Terminal 完成,也歡迎參考阿吉老師(就是我)所錄製的  Wio terminal tinyML 影片,使用光感測器搭配 Edge Impulse 網站來完成手勢辨識專案,很好玩。

Dfrobot 土壤溼度感測器

產品型號

Seeed MOSFET 控制模組與沉水馬達

產品wiki請參考,以本範例來說,Vin 不需要外接電源也可以順利驅動沉水馬達,請把馬達的紅黑線分別接到 MOSFET 模組的正負螺絲端子即可,請務必做好防水,操作上還蠻容易有水噴濺出來的。沉水馬達顧名思義,使用時須放在水中,否則空轉很容易燒毀。

 

 

Blynk web dashboard 設定

請根據以下資訊來完成 Blynk dashbaord 與相關 widget 設定,我們也整理出了彼此之間的關係,熟悉之後您可以自行改用。當然也可以課金解鎖(標註有 ugrade 的 wdiget 就須付費)更多功能啦!

 

Datastream – 虛擬腳位 – 實體裝置(Wio terminal 腳位) – dashboard widget 的關係說明如下:

指定虛擬腳位時請注意,虛擬腳位編號不重要也不需要與實體腳位編號對應,但須注意不可重複。由下圖可知即便溫度濕度來自同一個感測器,但還是要分別為溫度與濕度各自指定一個虛擬腳位,後續才能分別對應到獨立的 widget。

switch – V2 – MOSFET (右側 Grove port) – Switch

soil_moisture – V5 – 土壤溼度感測器(A2 類比輸入腳位) – Gauge 與 Label (多個 widget 取得同一筆資料來源是可以的)

temperture – V6 – DHT20感測器的溫度(左側 Grove port) – Gauge

humidity – V7 – DHT20感測器的溼度(左側 Grove port) – Gauge

有一個 slider 新增了但是沒用到,忽略即可。

範例1 土壤溼度感測器控制沉水馬達

執行時請確認土壤溼度感測器往下插到正確的位置(上下箭頭之間)、相關電路防水都做好了,水管不會亂噴等等,再上傳程式。這個程式很簡單,就是根據土壤濕度(類比讀取)來控制 MOSFET模組,進而做到控制沉水馬達。請根據實際狀況(土質、馬達種類與您使用的植栽)來調整相關數值。

注意,不要為了測試就瘋狂澆水,植物會受不了的喔!

程式碼

XXX

範例2 Blynk 雲服務控制沉水馬達

Blynk 相關設定請點我

相關注意事項如上,執行之後,您可以看到 Blynk dashboard 顯示您的裝置為 [online],這時候就可以按下 switch 控制馬達來澆水了,很酷吧!

程式碼如下

#define BLYNK_TEMPLATE_ID           ""
#define BLYNK_DEVICE_NAME           ""
#define BLYNK_AUTH_TOKEN            ""

// Comment this out to disable prints and save space
#define BLYNK_PRINT Serial

#include <rpcWiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleWioTerminal.h>

char auth[] = BLYNK_AUTH_TOKEN;

char ssid[] = "";      // wifi settings
char pass[] = "";

// built-in blynk functions when specified Vpin is changed
BLYNK_WRITE(V1) // slider
{
  int pin1Value = param.asInt(); // assigning incoming value from pin V1 to a variable
  Serial.print("V1 Slider value is: ");
  Serial.println(pin1Value);
}

BLYNK_WRITE(V2)  //switch
{
  int pin2Value = param.asInt(); 
  Serial.print("V2 switch value is: ");
  Serial.println(pin2Value);
}

void setup()
{
  // Debug console
  Serial.begin(115200);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
}

void loop()
{
  Blynk.run();
}
control MOSFET module by Blynk switch widget

[手勢辨識應用] Google Mediapipe 手勢控制LED呼吸燈

$
0
0

MediaPipe是一款由Google於2019年開發並開源處理機器學習應用框架專案,提供了跨平台的相關應用,我們之前已經介紹了 Mediapipe 豐富的範例,並於 Raspberry pi Jetson Nano 單板電腦上執行Mediapipe的例子,有興趣的讀者歡迎看看與分享喔!

本篇應用的是 Mediapipe 的 Hand API,由下圖可看到手部各點的定義。

程式會在手掌上標記21個點,本篇是將大拇指和食指的標記抓出來,也就是第4點和第8點,並計算兩點的距離,進而控制「Arduino首次接觸就上手」套件的LED燈,使LED燈產生呼吸燈的效果。

撰寫/攝影 許鈺莨
前情提要
時間 30分鐘 材料表 Arduino首次接觸就上手教學套件 x 1

 


本次專案程式主要是來自 Murtaza 這位 Youtuber ,只要在他的網站上CVZONE中註冊就可以免費得到程式碼。建議大家可以觀看 Murtaza的手部追蹤 手勢控制 的影片。本範例的實際執行影片如下

本文分成以下步驟:
1. 電腦虛擬環境安裝。
2. 手勢控制程式套件安裝。
3. 「首次接觸就上手」的硬體接線。
4. 「首次接觸就上手」的程式燒錄。
5. 電腦端執行手勢控制程式。

第1步 電腦虛擬環境安裝

在執行手勢控制的程式前,需先在電腦中安裝Anaconda軟體並在其軟體中再安裝虛擬環境,安裝步驟請參考本文:AI人工智慧-神經運算】環境建置:安裝Anaconda、Tensorflow、Keras與openCV(Windows篇)

在此所建立的虛擬環境名為 AI_7697,您可以隨意命名。

第2步 手勢控制程式套件安裝

2-1安裝Mediapipe套件0.8.7.1版:

pip install Mediapipe==0.8.7.1
install mediapipe

p.s.筆者試過,若Mediapipe安裝最新版,會無法執行!

2-2 安裝pyserial套件

此套件是為了讓電腦透過 USB 序列埠與「Arduino首次接觸就上手」套件溝通。

pip install pyserial
install pyserial

第3步 「Arduino首次接觸就上手」的硬體接線

請將「首次接觸就上手」的LED燈,用Grove的連接線另外接到Arduino開發板的D3,因為D3腳位才支援PWM 控制 (預設的D4 無法 PWM)。當然也可以另外找一顆LED來接。

p.s. PWM腳位除可以接D3外,還有D5、D6、D9、D10、D11,但Arduino程式要改腳位。

第4步 「Arduino首次接觸就上手」的程式燒錄

4-1 下載程式碼

請由本連結中下載相關程式,解壓縮後在 Arduino_code 資料夾中找到 Arduino_LED.ino

4-2  燒錄程式

請用 Hangeekduino 壓縮檔中的Arduino IDE 1.8.5上傳Arduino_LED.ino程式。Hangeekduino 軟體請點我下下載,教學請參考:【Arduino首次接觸就上手】快速執行AI圖像辨識

第5步 電腦端執行手勢控制程式

下載程式後,在Python_code資料夾中找到 MediaPipeHandPose.py

執行前請先用 micro USB 傳輸線連接「首次接觸就上手」套件接上電腦,並確定 Arduino USB Com Port 編號,再輸入指令:

python MediaPipeHandPose.py --video 2 --com 3
run python script

執行成果如本文開頭的影片

重要程式段落說明

Python程式

#主要程式來源來自:https://www.youtube.com/c/MurtazasWorkshopRoboticsandAI/featured

import serial
import argparse
import cv2
import time
import numpy as np
import math
import mediapipe as mp

########## 手部追蹤偵測 #############
class handDetector():
    def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):
        self.mode = mode
        self.maxHands = maxHands
        self.detectionCon = detectionCon
        self.trackCon = trackCon

        self.mpHands = mp.solutions.hands
        self.hands = self.mpHands.Hands(self.mode, self.maxHands,
                                        self.detectionCon, self.trackCon)
        self.mpDraw = mp.solutions.drawing_utils

    def findHands(self, img, draw=True):
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.results = self.hands.process(imgRGB)
        # print(results.multi_hand_landmarks)

        if self.results.multi_hand_landmarks:
            for handLms in self.results.multi_hand_landmarks:
                if draw:
                    self.mpDraw.draw_landmarks(img, handLms,
                                               self.mpHands.HAND_CONNECTIONS)
        return img

    def findPosition(self, img, handNo=0, draw=True):

        lmList = []
        if self.results.multi_hand_landmarks:
            myHand = self.results.multi_hand_landmarks[handNo]
            for id, lm in enumerate(myHand.landmark):
                # print(id, lm)
                h, w, c = img.shape
                cx, cy = int(lm.x * w), int(lm.y * h)
                # print(id, cx, cy)
                lmList.append( [id, cx, cy])
                if draw:
                    cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED)

        return lmList

def main():
    
############## 各參數設定 ##################
    pTime  = 0
    minPwm = 0
    maxPwm = 255
    briArd = 0
    briBar = 400
    briPer = 0
    
############## 指定WEBCAM和Arduino Serial Port編號的指令 ##################
    
    parser = argparse.ArgumentParser(
      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
    '--video', help='Video number', required=False, type=int, default=0)
    parser.add_argument(
      '--com', help='Number of UART prot.', required=True)
    args = parser.parse_args()
    
    COM_PORT = 'COM'+str(args.com)
    BAUD_RATES = 9600
    ser = serial.Serial(COM_PORT, BAUD_RATES)
    
    args = parser.parse_args()
    
    
############## WEBCAM相關參數定義 ##################
    wCam, hCam = 640, 480   
    cap = cv2.VideoCapture(args.video) # 攝影機編號預設為0,也可以輸入其他編號!
    cap.set(3, wCam)
    cap.set(4, hCam)
    detector = handDetector(detectionCon=0.7)

    try:
        while True:
            success, img = cap.read()
            img = detector.findHands(img)
            lmList = detector.findPosition(img, draw=False)
            #print(lmList)
            
            
            if len(lmList) != 0:
                x1, y1 = lmList[4][1], lmList[4][2]
                x2, y2 = lmList[8][1], lmList[8][2]
                cx, cy = (x1 + x2) // 2, (y1 + y2) // 2

                cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED)
                cv2.circle(img, (x2, y2), 15, (255, 0, 255), cv2.FILLED)
                
                #計算大拇指和食指的直線中點距離
                cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), 3)
                cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED)
                
                #計算大拇指和食指的直線距離
                length = math.hypot(x2 - x1, y2 - y1)
                #print(length)

                #將大拇指和食指的直線距離換算成0~255,Arduino PWM控制數值亦為0~255
                brightness = np.interp(length, [50, 300], [minPwm, maxPwm])
                
                briBar = np.interp(length, [50, 300], [400, 150])  
                briArd = np.around(brightness,2)
               
                
                if length < 50:
                    cv2.circle(img, (cx, cy), 15, (0, 255, 0), cv2.FILLED)
                         
            #畫出直方圖
            cv2.rectangle(img,(50,150),(85,400),(255, 0, 255),3)    
            cv2.rectangle(img,(50, int(briBar)),(85,400),(255, 0, 255),cv2.FILLED)
            cv2.putText(img, f'brightness: {int(briArd)}', (15, 140), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 255), 3)
            
            #送出數值給Arduino
            ser.write(str(briArd).encode())
      
      
            #計算每秒跑幾張
            cTime = time.time()
            fps = 1 / (cTime - pTime)
            pTime = cTime
            cv2.putText(img, f'FPS: {int(fps)}', (40, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 3)

            #顯示畫面 
            cv2.imshow("HandDetector", img)
           
                      
            #按q停止程式          
            if cv2.waitKey(10) & 0xFF == ord('q'):
                break
                
    except KeyboardInterrupt:
        ser.close()
        cap.release()
        cv2.destroyAllWindows()
    
if __name__ == '__main__' :
    main()
  • 12~50:手部偵測動作,偵測手勢並標記手部的節點,一次可以偵測到兩隻手。
  • 64~76:執行程式時所指定的Arduino Serial Com Port 編號,和攝影機編號。若沒指定攝影機編號,則預設為0。
  • 94~123 抓出大拇指和食指的節點位置後,算出兩指節點的距離,將距離轉換成數值0~255,再將數值傳送至Arduino,並用直方圖顯示。

 

Arduino程式

//Arduno D3腳位
int LEDPin = 3;
String number = "" ;
int i = 0 ;
long pwm_val ;

void setup()
{
  //各協定通訊初始化
  Serial.begin(9600);
  pinMode(LEDPin, OUTPUT);
}

void loop()
{
  //執行command副函式
  command();
}

long command() {
  while (Serial.available()) {
    if (i == 0) {
      number = "";
    }
    // 扣除ASCII碼值
    number += Serial.read() - 48; 
    i++;
  }
  // 字串轉換成整數值
  pwm_val = number.toInt(); 
  i = 0 ;
  
  Serial.println(pwm_val);

  //PWM控制LED
  analogWrite(LEDPin, (pwm_val));
  delay(100);
}
  • 2:指定 LED 接在 LED D3 腳位,若要換其他PWM腳位,請在此行程式修改。
  • 20~38:接收從電腦傳來的數值,但由於 Arduino 會以 ASCII 來處理,所以要減去 48 才是正確數字。最後由於這時的”數字”其實還是字串型態,所以還需要將其轉成整數型態,才可以當作控制LED的PWM數值。

本篇到此結束,也歡迎參考阿吉老師的 Arduino 首次接觸就上手全系列教學影片喔!

 

 

 


[Wio Terminal] 組裝!智慧植栽實驗套件

$
0
0

本文將說明如何使用 Wio Terminal 搭配相關周邊裝置來組裝智慧植栽系統,材料清單請參考以下:

撰寫/攝影 許鈺莨
前情提要
時間 2小時 材料表
  • Wio Terminal 開發板 x1
  • Grove – 溫溼度感測器(DHT20) x1
  • Grove – MOSFET模組 x1
  • Gravity: 類比式防水電容式土壤濕度感測器 x1
  • 直流沉水式抽水馬達 x1
  • PVC管(已挖洞)25公分
  • 矽膠管50公分
  • 雷射切割壓克力背板
  • 杜邦線(公公) 3條
  • 金屬螺絲M2*8 2個
  • 塑膠螺絲M2*5 3個
  • 塑膠六角隔離柱205 6個
  • 塑膠M2螺帽 3個
  • 小束線帶 2條
  • 大束線帶 1條
成本  請到機器人王國商城選購 難度  **** (最高10顆星)

 

先前已經介紹過許多 wio terminal 智慧植栽的應用,但最重要的硬體要如何架設呢?

在開始之前,請先準備好十字螺絲起子、剪刀(或斜口鉗)以及您覺得順手的工具。請做好安全防護措施並按照以下步驟組裝,開始吧!

組裝步驟

STEP 1.
先將Wio Termial用金屬螺絲M2*8鎖在壓克力板上,在這裡要注意上下不要顛倒,Wio Termial背面的腳位需對準壓克力挖空的部分。

STEP 2.
將M2*5塑膠隔離柱3個、M2*5塑膠螺帽3個分別鎖在MOSFET模組上。

STEP 3.
將MOSFET模組鎖於壓克力板上 (Wio Terminal的背面),注意MOSFET模組 OUT的端子要朝外才能順利接上沉水馬達

STEP 4.
翻轉壓克力板,讓Wio Terminal正面螢幕朝自己,並在MOSFET模組的位置鎖上三個M2*5塑膠隔離柱。


接著再將將溫溼度感測器(DHT20)用M2*5塑膠螺絲鎖在塑膠隔離柱上。

STEP 5.
使用兩條GROVE線,分別將溫溼度感測器與MOSFET模組接上 Wio Terminal連接,請注意接線的位置。當 Wio Terminal 背面朝自己,MOSFET模組接到左邊,溫溼度感測器接右邊。

STEP 6.
將材料包裡附的兩條小束線帶,分別穿過PVC水管上的上下兩組孔洞,接著再穿過壓克力板上、下方的孔位,拉緊束線帶後,便可將PVC水管與壓克力底板固定在一起。

STEP 7.
先使用杜邦線(公公)與Wio Terminal的腳位連接,連接的位置如下:(也可查詢Wio Terminal的腳位圖自行更改接線)

  • 紅線接於上排右2的位置 (5V)
  • 黑線接於上排右3的位置 (GND)
  • 黃線接於上排右8的位置 (A2)
    Buy Seeed Studio Wio Terminal ATSAMD51 Core with Realtek RTL8720DN BLE5.0 Dev Board, Wireless Microcontroller Python Terminal Device Compatible with Raspberry Pi for Arduino, Micropython, and TinyML Online in Taiwan. B087LNFZ2T

接著再將杜邦線的另一端,按照對應的顏色與土壤溼度感測器的線連接。(橘>橘、紅>紅、黑>黑)

STEP 8.
拿出沉水馬達,將紅黑兩條線分別接在MOSFET模組的OUT端。紅色(正極)接OUT,黑色(負極)接GND。再將矽膠管塞進沉水馬達的出水孔。

STEP 9.
將土壤濕度感測器剩餘的線用大束線帶綁在水管上,您可以視使用的環境來決定線要留多長。

實際安裝圖片如下,歡迎與我們分享您的智慧植栽喔!

 

 

ZED 景深攝影機範例#8/8 – human body tracking

$
0
0

本文為 ZED 景深攝影機原廠範例,共有八個範例,請根據 ZED 的範例程式頁面,取得 C++ / Python / C# 等範例原始碼。說明如下:

  1. Hello ZED: 入門範例,說明如何連接 ZED 攝影機,並於終端機中顯示裝置序列編號。
  2. Image Capture: 開啟 ZED 攝影機,取得影像,並於終端機中顯示時間戳記、影像尺寸。
  3. Depth Perception: 取得畫面的深度資料與點雲,並於終端機中顯示指定點的距離。
  4. Camera Tracking: 啟用位置追蹤,可即時更新攝影機的位置與指向。
  5. Spatial Mapping: 啟用地圖繪製,可擷取環境的網格或融合點雲。
  6. 3D Object Detection: 偵測畫面中的物體,並進行 3D 定位 (只適用於 ZED 2 機型)。
  7. Using Sensors: 取得攝影機的 IMU、氣壓計與磁力感測器資料。
  8. Body Tracking: 追蹤人體骨架

範例08 – 人體追蹤

本範例將說明如何使用 ZED 3D 攝影機的人體追蹤模組(ZED Body Tracking module)來偵測與追蹤人體。

以下關節編號圖片來自 ZED 原廠文件:,分別為 34 點姿勢(POSE_34)以及 18 點姿勢 (POSE_18)

前置作業

  • 下載最新版的 ZED SDK (請點我)
  • 下載 Image Capture 範例程式,提供 C++, Python 與 C# 等版本
  • 在 Windows 或 Linux OS上,建置 C++ 環境(請點我) 或執行 Python 範例,本系列文章將使用 Python (教學請點我)

程式總覽

開啟攝影機

本範例會用到 ZED SDK 中的 Body Tracking AI 模組。如先前所有範例,首先要建立、設定與開啟攝影機。

# Create ZED objects
zed = sl.Camera()
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
init_params.sdk_verbose = True

# Open the camera
err = zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
    # Quit if an error occurred
    exit()
open the camera

啟用 3D 物件偵測

首先要啟用 Object Detection 模組才能執行人體追蹤。別忘了要先設定該模組的 ObjectDetectionParameters 才能啟用物件偵測功能喔 (詳細作法請回顧範例06物件偵測)。請注意物件追蹤也須用到位置追蹤功能才能在世界參考坐標中來追蹤想要的物件。

# Define the Object Detection module parameters
obj_param = sl.ObjectDetectionParameters()
# Different model can be chosen, optimizing the runtime or the accuracy
obj_param.detection_model = sl.DETECTION_MODEL.HUMAN_BODY_FAST
# run detection for every Camera grab
obj_param.image_sync = True
# Enable tracking to detect objects across time and space
obj_param.enable_tracking = True
# Optimize the person joints position, requires more computations
obj_param.enable_body_fitting = True

# If you want to have object tracking you need to enable positional tracking first
if obj_param.enable_tracking:
    positional_tracking_param = sl.PositionalTrackingParameters()
    zed.enable_positional_tracking(positional_tracking_param)
enable body tracking

啟用物件偵測時會一併載入 AI 模型,這項操作需要幾秒鐘。首次使用 AI 模型時,模型會根據您的硬體進行大約數分鐘的最佳化作業,不過別擔心,這個神經網路模型最佳化作業只要執行一次就好。

print("Object Detection: Loading Module...")
err = zed.enable_object_detection(obj_param)
if err != sl.ERROR_CODE.SUCCESS:
    print(repr(err))
    zed.close()
    exit(1)
start module and load AI model

擷取資料

物件偵測的信心閾值可於執行過程中隨時調整,讓您可根據場景複雜度來選擇是否只要偵測最相關的人體骨架。由於相關參數已被設為 image_sync,因此對於每次 grab 呼叫來說, 影像都會被送進 AI 模組並輸出個畫面的偵測結果。

# Set runtime parameter confidence to 40
obj_runtime_param = sl.ObjectDetectionRuntimeParameters()
obj_runtime_param.detection_confidence_threshold = 40

objects = sl.Objects()

# Grab new frames and detect objects
while zed.grab() == sl.ERROR_CODE.SUCCESS:
    err = zed.retrieve_objects(objects, obj_runtime_param)

    if objects.is_new:
        # Count the number of objects detected
        obj_array = objects.object_list
        print(str(len(obj_array)) + " Person(s) detected\n")

        if len(obj_array) > 0:
            first_object = objects.object_list[0]
            # Display the 3D keypoint coordinates of the first detected person
            print("\n Keypoint 3D ")
            keypoint = first_object.keypoint
            for it in keypoint:
                print("    " + str(it))
capture data

停用模組並離開程式

程式結束時,會一併停用模組並關閉攝影機。由於 zed.close() 會負責停用所有模組,因此這個步驟並非必要。。有必要的話,也可透過 destructor 自動呼叫close() 函式。

大功告成!

下一步

如果想知道如何偵測場景中的人體並將其 3D 骨架顯示於即時影像上(如本文開頭影片),請參考 3D Object Detection 進階範例。

註:本文經授權之後翻譯自 https://www.stereolabs.com/docs/tutorials/body-tracking/

空氣盒子感測器資料上傳至MSCLITE&ThingSpeak雲服務

$
0
0

本文延續【LinkIt™ 7697空氣品質偵測並上傳Google表單(空氣盒子2.0)】,本文要進一步把空氣盒子的監測數值分別上傳到MCSLite和ThingSpeak中,並會比較IFTTT、MCSLite和ThingSpeak的優缺點。

本範例程式碼下載請點我

撰寫/攝影 許鈺莨
前情提要
時間 1小時 材料表 空氣品質監測基本材料包
(使用LinkIt™ 7697、Grove 擴充板)
連結: https://robotkingdom.com.tw/product/airbox-kit-linkit-7697-2/Grove -VOC與eCO2 氣體感測器(選配)
連結:
https://robotkingdom.com.tw/product/grove-voc-and-eco2-gas-sensor-sgp30/
成本 難度  **** (最高10顆星)

 


我們將三套空氣盒子放置在機器人王國的 B1、一樓店面及三樓辦公室,並將空氣盒子的監測數值上傳到雲端。

B1

一樓店面

三樓

之前是將數值透過IFTTT上傳至Google表單,如果讀者有興趣請看【LinkIt™ 7697空氣品質偵測並上傳Google表單(空氣盒子2.0)】。本文則是要進一步把空氣盒子的感測器數值上傳到 MCSLite 和 ThingSpeak 兩個不同的雲服務中,。為將空氣盒子數值傳上ThingSpeak是新的嘗試,所以會著重ThingSpeak的說明,而因為程式碼比較龐大,所以會重點解釋程式碼,以下本文開始。


筆者針對目前使用過的雲端平台做一些心得感想:

1. 透過IFTTT上傳至Google表單

使用IFTTT的好處就是方便串接兩種不同的服務,而這樣的組合可說是變化多端。設定好之後可以做一套自動化的執行程序(Applets)。例如:當空氣盒子偵測到溫度過高可以傳訊息到LINE中通知。

對於偵測數值有變化進而可以控制裝置,或傳通知訊息的專案,可以使用這個方案,但缺點就是在2020年10月7號後,就會需要付費成專業版,可不受限制可以建立多個個人化的執行程序,若使用免費方案的話,最多只能有 3 個 applet,超過的話就必須刪除,以下就是當時收到建議付費成專業版的郵件內容。

再來是上傳數值的種類,最多只能選擇三種,例如:溫度、濕度、CO2數值,除此之外,筆者還發現,上傳到Google的表單的數值,最多只能顯示2000列,如下圖所示。

2. MCSLite

由聯發科技物聯網雲端平台 MediaTek Cloud Sandbox(MCS)的核心功能改寫而成的輕量化程式,雖然MCS在 2021年4月16日停止了服務,但是MCSLite還是有繼承MCS的部分功能,而且 MCSLite 還可以直接運行於 Windows、Linux、Mac OS 等作業系統,方便不聯外網的區域網路使用。

使用MCSLite優點是可以在網頁中可以創建控制通道和顯示通道,可以透過控制頁面來控制裝置,而在創建產品原型時可以無限制建立,而每個產品原型也可以無限制創建出測試裝置。

其缺點是只能將上傳的資料點存在雲端中,而無法下載成CSV檔或JSON檔等格式,儲存到電腦中。

3. ThingSpeak

ThingSpeak 是由知名MATLAB公司所提供的免費空間,每個註冊帳號可以免費有四個頻道(channel),而每個頻道可有八個自訂欄位(field),這意味著可以上傳八種不同的數值,可以提供很直觀圖表製作的功能,也可以下載CSV檔到電腦中,手機端也有專用 app 來檢視資料。但缺點就是無法像 IFTTT 一樣,可以設定超過一定數值就會推播訊息到裝置中,或像MCSLite可以有控制頁面直接控制裝置。


再來要說明如何將空氣盒子的資料點上傳到MCSLite和ThingSpeak的平台,首先以下說明建立這兩個的平台的註冊流程及通道建立。

上傳資料到 MCSLite

MCSLite從下載解壓縮檔案到註冊流程及通道建立,請由此網頁【5-4 MCS Lite介紹及MCS Lite環境建置】來查閱。

上傳資料到 ThingSpeak

以下為建立ThingSpeak平台通道的詳細步驟:

第一步:註冊 ThingSpeak 帳號

先到 ThingSpeak 網站註冊一個免費帳號

填寫Email信箱、所在地區…等基本資料,再按下Continue

第二步:建立頻道

註冊成功後,到上方頁面的Channels🡪My Channels

按下 New Channel 即可新增頻道

第三步:新增頻道

寫上頻道名稱,一個頻道可以建立最多8個欄位,代表空氣盒子可以上傳8種不同的數值。填完欄位名稱後,點選最下面的 Save Channel 代表頻道新增完畢。

筆者分別在公司的地下一樓、一樓店面及三樓辦公室個放了一個空氣盒子作環境監測,以下是三個樓層的頻道。

第四步:查詢API Key

由於 ThingSpeak 在此用於接收來自空氣盒子的感測器數值 (被寫入),所以這裡請複製Write API Key,後續程式會用到。

程式說明

本專案的程式是由BlocklyDuino附屬的Arduino IDE 1.8.5開啟,程式下載解壓縮後,請開啟CAVEDU_AirBox_V3_IFTTT_DHT20_1F.ino檔案。開啟時,其他的檔案都會匯入進來,以下為重要程式說明。

重要程式說明

/**************************************************/
/*  Copyright © 2021 CAVEDU. All rights reserved.

  The AirBox program is suitable for sensors:

  Grove-DHT20:
  https://robotkingdom.com.tw/product/grove-%e6%ba%ab%e6%ba%bc%e5%ba%a6%e6%84%9f%e6%b8%ac%e5%99%a8temperature-humidity-sensor-v2-0-dht20-upgraded-dht11-i2c-port/

  SPG30:
  https://robotkingdom.com.tw/product/grove-voc-and-eco2-gas-sensor-sgp30/

  OLED Display 0.96:
  https://robotkingdom.com.tw/product/grove-oled-display-0-96/
  (Also suitable for Grove-OLED Display 0.96”:https://robotkingdom.com.tw/product/oled-display-0-96/)

  Education Kit 001 Button :
  https://robotkingdom.com.tw/product/rk-education-kit-001/

  You can see the AirBox Technical article:
  https://blog.cavedu.com/2021/01/20/linkit-7697-airbox/

  https://blog.cavedu.com/2021/11/25/linkit-7697-airbox-2/

*/
/***************************************************/

#include "PMS.h"
#include  <SoftwareSerial.h>
#include "U8g2lib.h"
#include <DHT.h>
#include "sensirion_common.h"
#include "sgp30.h"
#include <LRTC.h>
#include <WiFiUdp.h>
#include <ctime>
#include "Wire.h"
#include "MCS.h"



/*============Pins==================*/
#define buttonPin 4
#define PMSTx     2
#define PMSRx     3
//#define DHT11Pin 10
#define DHTTYPE DHT20   // DHT 20
/*====================================*/

//Enter WIFI SSID & Password
#include <LWiFi.h>
char _lwifi_ssid[] = "_lwifi_ssid";
char _lwifi_pass[] = "_lwifi_pass";

//Enter IFTTT_key & Event_name
char IFTTT_key[] = "IFTTT_KEY";
char Event_name[] = "Event_Name";

unsigned long current_time = 0;//record current time
unsigned long previous_IFTTT_time = 0;//Record the last time of IFTTT
int IFTTT_upload_time = 2000;//IFTTT intervals of time

int frame = 0;

/*ThingSpeak value*/
unsigned long previous_ThingSpeak_time = 0;//record current time
int ThingSpeak_upload_time = 1000*60*10;//ThingSpeak intervals of time 

String Write_API_key= "Write_API_key";//輸入ThingSpeak的 Write_API_Key

/*MCSLITE value*/
unsigned long previous_mcslite_time = 0;//record current time
int mcslite_upload_time = 1000*60*10;//MCSLITE intervals of time 

#define _your_MCSLite_DeviceId   "_your_MCSLite_DeviceId"  //輸入MCSLite的DeviceId
#define _your_MCSLite_DeviceKey  "_your_MCSLite_DeviceKey" //輸入MCSLite的Devicekey
#define _your_MCSLite_IP         "_your_MCSLite_IP" //輸入MCSLite的IP                         
MCSLiteDevice mcs(_your_MCSLite_DeviceId , _your_MCSLite_DeviceKey, _your_MCSLite_IP, 3000);

MCSDisplayInteger _1F_CO2("_1F_CO2");
MCSDisplayInteger _1F_TVOC("_1F_TVOC");
MCSDisplayInteger _1F_PM10("_1F_PM10");
MCSDisplayInteger _1F_PM1_0("_1F_PM1_0");
MCSDisplayInteger _1F_PM2_5("_1F_PM2_5");
MCSDisplayInteger _1F_TEMP("_1F_TEMP");
MCSDisplayInteger _1F_HUMI("_1F_HUMI");

SoftwareSerial PMSSerial(PMSTx, PMSRx);
PMS pms(PMSSerial);
PMS::DATA data;
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
/*Monitor rotate angle
   U8G2_R0     0   degrees
   U8G2_R1     90  degrees
   U8G2_R2     180 degrees
   U8G2_R3     270 degrees
   U8G2_MIRROR mirror
*/
#define imgWidth 128
#define imgHeight 48

int pms1_0, pms2_5, pms10_0;
int temp;
int humi;
int button_state;

s16 err = 0;
u16 tvoc_ppb, co2_eq_ppm;


//DHT dht11_p10(DHT11Pin, DHT11);  //define dht11 PIN
DHT dht(DHTTYPE);

#if defined(ARDUINO_ARCH_AVR)
#define debug  Serial

#elif defined(ARDUINO_ARCH_SAMD) ||  defined(ARDUINO_ARCH_SAM)
#define debug  SerialUSB
#else
#define debug  Serial
#endif


float temp_hum_val[2];

void setup()
{
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  debug.println("DHT20 test!");
  Wire.begin();
  dht.begin();
  //dht11_p10.begin();

  PMSSerial.begin(9600);
  LRTC.begin();
  SGP30_int();
  oled_int();
  msclite_channel_int();
  pinMode(buttonPin, INPUT);

}

void loop()
{
  float temp_hum_val[2] = {0};
  button_state = digitalRead(buttonPin);
  //temp = dht11_p10.readTemperature();
  //humi = dht11_p10.readHumidity();


  while (!pms.read(data)) {}      //PM2.5 read Data
  pms1_0 = data.PM_AE_UG_1_0;
  pms2_5 = data.PM_AE_UG_2_5;
  pms10_0 = data.PM_AE_UG_10_0;

  err = sgp_measure_iaq_blocking_read(&tvoc_ppb, &co2_eq_ppm);
  if (err == STATUS_OK) {
    Serial.print("tVOC  Concentration:");
    Serial.print(tvoc_ppb);
    Serial.println("ppb");

    Serial.print("CO2eq Concentration:");
    Serial.print(co2_eq_ppm);
    Serial.println("ppm");
  } else {
    Serial.println("error reading IAQ values\n");
  }



  sensor_oled_task(); //OLED show sensor value
  button_oled_show_time();//Press Button to show time
  //IFTTT_pm25_temp_humi_task();//Upload sensor value to IFTTT
  upload_to_msclite_task();//Upload sensor value to msclite
  upload_to_ThingSpeak();//Upload sensor value to ThingSpeak
}
51,52,55,56,60,66,68,72,74-76,79-85,173-177
  • 51~52 第51行,輸入WIFI的SSID。
    第52行,輸入WIFI的密碼。
    55~56 第55行,輸入IFTTT的金鑰。
    第56行,輸入IFTTT的任務名稱。
    請看【LinkIt™ 7697空氣品質偵測並上傳Google表單(空氣盒子2.0)】[註1]文章。
    60 輸入上傳感測器到IFTTT的間隔時間(單位為毫秒)。
    66 輸入上傳感測器到ThingSpeak的間隔時間(單位為毫秒)。
    68 輸入ThingSpeak的Write_API_Key。
  • 72 輸入上傳感測器到MCSLite的間隔時間(單位為毫秒)。
  • 第74行,輸入MCSLite的DeviceId。
  • 第75行,輸入MCSLite的DeviceKey。
  • 第76行,輸入MCSLite的IP。請看【5-4 MCS Lite介紹及MCS Lite環境建置】[註2]。
  • 79~85 第79~85行,在MCSLite建立的資料通道。
  • 173 顯示感測器數值在OLED。
  • 174 按下按鈕會顯示時間在OLED。
  • 175 因IFTTT只能上傳三種數值,故選了PM2.5、溫度和濕度的數值。而程式前面有雙斜線則是註解,不會跑那一段程式。
  • 176 上傳感測器數值到MCSLite。
  • 177 上傳感測器數值到ThingSpeak 。

B1程式和一樓與三樓程式幾乎都一樣,所以就以一樓的空氣盒子程式來說明即可。更改完成後,就可以將程式燒錄到LinkIt™ 7697中,由於程式已整合成可同時把感測器數值上傳到IFTTT、MCSLite和ThingSpeak,若讀者想要上傳到IFTTT,請修改175行的註解。

以下為上傳到MCSLite和ThingSpeak成功的例子。

MCSLite

MCSLite屬於區域網路,無法公開網址,故分享感測器上傳成功的畫面。

ThingSpeak

ThingSpeak 可以公開頻道,在此分享網址給大家看看:

  • B1:https://thingspeak.com/channels/1626172
  • 1F:https://thingspeak.com/channels/1627706
  • 3F:https://thingspeak.com/channels/1627724

FB影片說明

https://www.facebook.com/watch/?v=453900302908283&ref=sharing

那麼以上就是空氣盒子2.0的使用教學,下次見!

ASUS Tinker Board 2S 與MCU的聯結應用—

$
0
0

前言

隨著AIoT與IIoT在近幾年的應用蓬勃發展,考量到IoT領域的各種應用情境,對於整體系統運作的成本與效能,一直是AIoT與IIoT考量的重點,因此便產生了各種嵌入系統與微控器的整合應用,希望透過嵌入系統與MCU的分工或互補,能有效提升系統運作整體的整體效益。

若系統開發若需要引入嵌入式系統,通常會有以下考量:

一、 高效GUI

1. 現行的嵌入式系統大都會將標準電腦的顯示介面電路設計進來,目前標準電腦常見的顯示介面,分別有VGA、HDMI、DVI等等,這些顯示介面都具有完整的圖形化顯示功能,作業系統透過程式設計的方式可以迅速建立圖形化使用者介面。

2. 圖形化使用者介面因為需要大量的記憶體存取與運算,必須透過強大的CPU與作業系統的偕同運作,嵌入式系統可較容易達成這些需要大量資料運算的應用。

二、 高運算負荷

1. 嵌入式系統因為有較大容量的記憶體與強大的CPU運算能力,非常適用於需要進行較大運算量的應用情境,如:人工智慧、人工智慧物聯網,其中人工智慧應用目前大都使用深度學習的方式進行設計,其中類神經網路的運算往往需要大量的運算。

2. 大量的網際網路或行動通訊網路應用需要大量的資料交換的過程,此外這些網路的服務往往也伴隨大量的GUI的操作,存取資料往往需要大量的系統記憶體進行儲存或運算。

三、 周邊電路擴充

1. 嵌入式系統大都提供類似標準電腦的硬體連接介面,顯示如:VGA、HDMI、DVI等,硬體擴充或連結介面如:USB系列、M.2、PCI-E、SATA等,能為各種標準電腦硬體周邊設備提供必要的支援。

2. 提供了許多工業標準的介面電氣規格介面,如:SPI、UART、I2C、RS485、TTL / CMOS介面,讓使用者可以擴充各種商用或自行設計的硬體模組電路。

若系統開發若需要引入MCU,通常會有以下考量:

 

一、 行動性

1. 現行常用的各種MCU都具備一個共同的特點就是「體積小」、「重量輕」,這樣的特性有助於開發各種行動裝置應用,或是各種穿戴式裝置的應用。

2. MCU裝置通常都是以單晶片微電腦的方式開發,現在常見的單晶片微電腦都將各種基本行動通訊功能整合在一起,這樣有助於進行各種強調行動通訊應用的情境。

二、 低耗能

1. MCU通常電源能耗都不會太大,在設計電源系統時就可以透過電池進行供電,這樣也有助於提高MCU應用的行動性。

2. 現行的各種MCU都設計有待機睡眠的功能,對於需要進行長時間待機的應用,MCU有甚低的靜態待機消耗功率的特點,對於節能主題的應用,提供相當不錯的實用價值。

三、 應用單純化

1. MCU因為低耗能的特性,大都是不具備甚高的運算效能,僅提供必要的基本資料轉換與算術或邏輯判斷功能,適用於對於環境資訊的蒐集應用。

2. MCU通常僅提供必要的擴充電路模組的信號接腳,提供簡單的電氣裝置的輸入或輸出功能,如:溫溼度感測、繼電器控制、簡單的有線或無線通訊界面功能。

分類 技術教學文 標籤 Tinker Board、Tinker Board 2S、ASUS、AI、AIoT、MCU、Bluetooth、UART、Serial、Communication
撰寫/攝影 曾俊霖
前情提要 有關Asus Tinker Board 2S作業系統的相關安裝教學,請參考以下連結:

ASUS Tinker Board 2S新版Debian 10 V2.0.3 系統與相依套件安裝教學

ASUS Tinker Board 2S 安裝 Android-11 V2.0.3版作業系統與應用軟體

有關Asus Tinker Board 2S作業系統在影像辨識的應用,請參考以下連結:

Tinker Board 2S Android-11 V2.0.3 執行 Tensorflow-Lite 範例程式教學

時間 2小時 材料表 ASUS  Tinker Board 2S / 2GB

 

MCU Linkit-7697

 

MCU ATmega-328系列

 

HC-05/06系列

成本
難度 中等

本篇教學將會著眼在嵌入式系統與MCU整合的應用上,從前述的兩種不同系統的運作特性裡,強調將這兩種系統進行整合開發應用的特點,而在現行各種大型專案的開發裡,尤其針對這兩種系統在進行資料交換的方式進行說明。

嵌入式系統與MCU系統在進行整合應用時,其通訊管道的建立對於這兩種系統的整合是極為重要的技術,本篇教學將會透過「有線通訊」與「無線通訊」兩種方式進行實作教學與應用的說明,其中嵌入式系統將會以華碩Tinker Board 2S為主要測試平台,而MCU將會採用Raspberry Pi Pico、聯發科Linkit-7697或Arduino系列進行測試。

 

一、 有線連線通訊系統設定與程式設計

1. 檢視UART在Tinker Board 2S上的硬體資源占用狀況

本篇教學將透過Tinker Board 2S的USB介面以UART通訊協定與MCU進行通訊,目前常用的MCU開發板(如:Linkit-7697、Raspberry Pi Pico或Arduino系列)都提供USB通訊界面,透過UART的方式來與Tinker Board 2S系統互相訊,並且透過各種開發工具(如:Arduino-IDE)進行連線,這樣的開發模式下,我們可透過UART 來對 MCU 上傳/燒錄程式。在 Tinker Board 2S 系統端若想要運用這樣的方式進行與MCU溝通,就必須要先了解MCU透過USB連結到Tinker Board 2S系統中在UART所占用的系統資源編號或是資源名稱,Tinker Board 2S控制程式設計就必須要開啟這些系統資源。

在Linux作業系統對於UART的通訊應用,通常在系統資源名稱都是以tty的方式進行資源的存取,這些相關存取的硬體資源訊息,通常都會放置在 /dev 目錄當中,只要查找 /dev 目錄就可以尋找到對應MCU的通訊資源編號與名稱,參考下圖,我們可以知道MCU占用Tinker Board 2S系統中的 ttyACM0

2. MCU的UART存取程式設計流程(簡單的輸入與輸出資料傳輸測試)

(1). 設定通訊埠Baud Rate
(2). 接收來自Tinker Board 2S的UART串列資料
(3). 檢查串列資料並進行RGB-LED的操作
(4). 輸出串列資料回應Tinker Board 2S

3. MCU 測試程式

在此使用 Linkit-7697,也適用 Raspberry Pi Pico。

#define LED_G 4
#define LED_B 3
#define LED_R 2

String str;

String SA[2];
int A[2];

void setup(void)
{
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  pinMode(LED_R, OUTPUT);
  digitalWrite(LED_G, LOW);
  digitalWrite(LED_B, LOW);
  digitalWrite(LED_R, LOW);

  Serial.begin(9600);
}

void loop(void)
{
  str = "";
  if (Serial.available()) {
    // 讀取傳入的字串直到"#"結尾
    str = Serial.readStringUntil('#');

    String_to_Int(str, 2);
  }
  
  if (str.length() != 0) {
    
    if (SA[0] == "G") {
      if (A[1] == 1) {
        digitalWrite(LED_G, HIGH);
        Serial.println("Green ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_G, LOW);
        Serial.println("Green OFF");
      }
    }

    if (SA[0] == "B") {
      if (A[1] == 1) {
        digitalWrite(LED_B, HIGH);
        Serial.println("Blue ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_B, LOW);
        Serial.println("Blue OFF");
      }
    }

    if (SA[0] == "R") {
      if (A[1] == 1) {
        digitalWrite(LED_R, HIGH);
        Serial.println("Red ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_R, LOW);
        Serial.println("Red OFF");
      }
    }
  }
}

void String_to_Int(String temp, int count)
{
  int index;

  index = temp.indexOf(',');
  SA[0] = temp.substring(0, index);

  for (int i = 1; i < count; i++) {
    temp = temp.substring(index + 1, temp.length());
    index = temp.indexOf(',');
    SA[i] = temp.substring(0, index);
    A[i] = SA[i].toInt();
  }
}

這個測試程式可以「接收」來自單板電腦端的指令來控制 RGB-LED,並且「發送」回應訊息,可以做到雙向通訊。

接收命令 RGB-LED動作 發送回應訊息
R,1# 亮起紅色光 Red ON
R,0# 熄滅紅色光 Red OFF
G,1# 亮起綠色光 Green ON
G,0# 熄滅綠色光 Green OFF
B,1# 亮起藍色光 Blue ON
B,0# 熄滅藍色光 Blue OFF

 

4. 單板電腦端測試流程:

在此使用 Tinker Board 2S 編寫 Python 來收發指令

(1). 輸入控制指令
(2). 發送控制指令至MCU
(3). 等候來自MCU的回應訊息
(4). 接收MCU的回應訊息
(5). 顯示來自MCU的回應訊息

5. Tinker Board 2S Python測試程式:

這個程式可以讓使用者可以把控制命令從Tinker Board 2S發送至MCU,並且等待MCU的回應訊息,接收回應訊息後必須檢查串列資料的內容是否正確,最後在螢幕顯示MCU的回傳訊息。

import serial
import argparse
import io
import time
import numpy as np
import cv2

from PIL import Image

parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument(
    '--com', help='Number of UART prot.', required=True)
args = parser.parse_args()

COM_PORT = args.com
BAUD_RATES = 9600
ser = serial.Serial(COM_PORT, BAUD_RATES,timeout=0, parity=serial.PARITY_EVEN, stopbits=1)

while(True):
    COMMAND = input('請輸入控制命令=')

    Data_flag = 0
    while(Data_flag == 0):
        Send_UART_command = COMMAND.encode(encoding='utf-8')    
        ser.write(Send_UART_command)  # 訊息必須是位元組類型

        time.sleep(0.5)
        while ser.in_waiting:
            mcu_feedback = ser.readline().decode()  # 接收回應訊息並解碼
            Data = mcu_feedback.strip('\r\n')

            if 'ON' in Data:
                Data_flag = 1
                print('MCU 回應:', Data)
            elif 'OFF' in Data:
                Data_flag = 1
                print('MCU 回應:', Data)
            else:
                Data_flag = 0

二、 Tinker Board 2S與MCU以USB有線連線方式的應用實例—「水質監測系統」

使用情境照
水質監測系統執行畫面

 

三、 Tinker Board 2S與MCU以USB有線連線方式的應用實例—「影像辨識機械手臂」

機器手臂實體圖

相關文章

 

ASUS Tinker Board 2S 與MCU的無線通訊 –藍牙篇

$
0
0

前言

嵌入式系統和MCU之間的連結,主要是透過有線通訊或無線通訊的方式進行,有線通訊的方式最常見的是採用USB的UART的方式進行,有線通訊是一種簡單方便的連線方式,傳輸效率甚高,但有線通訊在使用上會受到訊號線長度的限制,不適合用來進行較長距離或是行動裝置的應用情境中,為了解決有線通訊的限制,就必須考慮無線通訊的方式來處理嵌入式系統與MCU之間的環境限制的問題,由於無線通訊技術範疇十分廣泛,若以涵蓋範圍來分類可分為「廣域型」與「區域型」,本篇教學將會以「區域型」無線網路通訊應用為主,您將可看到 Tinker 2S 支援兩種藍牙通訊方式,為此將以不同的開發板來示範:

  • 傳統藍牙 – Raspberry Pi Pico + HC06 藍牙模組
  • 藍牙低功耗(BLE) – LinkIt 7697

目前在嵌入式系統或MCU常見的區域型無線通訊方式,如下表大致可以區分成5大類:

通訊技術 適用距離 Bits / Sec 常見應用
NFC 數公分~數公尺 約數百K 門禁裝置
藍牙 約 <50公尺

(5.X版可達300公尺)

約數十M 辦公室自動化、居家自動化
Wi-Fi 約 < 50公尺

(透過強波可到數百公尺)

約 <數G 辦公室自動化、居家自動化、工業自動化
Zigbee 約數百公尺 約數百K 辦公室自動化、居家自動化、工業自動化
LoRa 約數公里 約數十K 工業自動化、農業自動化

本篇教學將會以常見的藍牙通訊為主,主要是因為藍牙通訊廣泛支援許多行動裝置(如:手機、平板電腦、筆記型電腦等)的操作,此外藍牙通訊模組或晶片大量整合到各種常見MCU開發平台上,藍牙通訊也是各種無線通訊應用中最為廣泛的一種。

撰寫/攝影 曾俊霖
前情提要 有關Asus Tinker Board 2S作業系統的相關安裝教學,請參考以下連結:

ASUS Tinker Board 2S新版Debian 10 V2.0.3 系統與相依套件安裝教學

ASUS Tinker Board 2S 安裝 Android-11 V2.0.3版作業系統與應用軟體

有關Asus Tinker Board 2S作業系統在影像辨識的應用,請參考以下連結:

Tinker Board 2S Android-11 V2.0.3 執行 Tensorflow-Lite 範例程式教學

時間 2小時 材料表 ASUS  Tinker Board 2S / 2GB

MCU Linkit-7697

MCU ATmega-328系列

HC-05/06系列

難度 中等

BLE測試電路_7697
HC06測試電路_Pico

目前MCU常見的藍牙通訊模組電路或設計大致上是以「HC05系列的UART傳輸」或是「BLE系列的GATT傳輸」兩大類,本篇教學也將會以這兩種傳輸方式進行說明,以下是兩種做法的列表:

MCU種類 藍牙模組 傳輸方式
Raspberry Pi Pico 外接HC05或HC06系列 UART
Linkit-7697 內建BLE藍牙 GATT

Tinker Board 2S針對上述兩種傳輸方式,也必須採用不同的Python程式以及安裝不同的Python API套件程式,主要分別如下表:

HC05系列UART傳輸API套件 BLE系列GATT傳輸API套件
Pyserial套件 PyBluez套件

Gattlib套件

一、Tinker Board 2S安裝相關套件程式

Tinker 開機之後,請確認有網路連線,開啟終端機之後輸入以下指令來安裝相關套件:

pip3 install pyserial

sudo apt-get install python-dev
sudo apt-get install libbluetooth-dev
pip3 install pybluez --user
sudo apt-get install pkg-config
sudo apt-get install libboost-python-dev
sudo apt-get install libboost-thread-dev
sudo apt-get install libglib2.0-dev 
pip3 install pybluez[ble]==0.22 --user
sudo pip3 install gattlib

二、HC05系列UART傳輸連線設定與程式設計

1.檢視並設定藍牙配對

接下來要在 Tinker 的桌面作業系統來操作

開啟Bluetooth Manager藍牙管理程式
點選Search,並且找到HC-06(或HC-05)模組
點選配對「Key圖示」,進行HC-06與Tinker Board 2S通訊配對。
輸入配對密碼完成配對

 

點選標記「Star圖示」設定HC-06藍牙模組為可信賴的裝置

 

「滑鼠右鍵」點選HC-06模組,並選取Serial Port,設定連線方式為序列埠。
畫面提示已完成序列埠通訊設定,裝置編號為/dev/rfcomm1

2. 檢視UART在Tinker Board 2S上的硬體資源占用狀況

在Tinker Board 2S作業系統對於藍牙UART的通訊應用,在系統資源名稱都是以rfcomm的方式進行資源的存取,這些相關存取的硬體資源訊息,通常都會放置在 /dev 目錄當中,只要檢索該目錄就可以尋找到對應MCU的通訊資源編號與名稱,本篇教學的HC-06 UART存取,是以/dev/rfcomm1命名。

3. MCU的UART存取程式設計流程(簡易輸入/輸出資料傳輸測試)

  1. 設定通訊埠Baud Rate
  2. 接收來自Tinker Board 2S的UART串列資料
  3. 檢查序列資料並進行RGB-LED的操作
  4. 輸出序列資料回應Tinker Board 2S

4. Raspberry Pi Pico 連接 HC-06模組測試程式

請將 HC06 模組接在 Raspberry Pi Pico 之後,使用 Arduino IDE 燒錄以下程式:

#define LED_G 4
#define LED_B 3
#define LED_R 2

String str;

String SA[2];
int A[2];

UART Serial2(8, 9, 0, 0);

void setup(void)
{
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  pinMode(LED_R, OUTPUT);
  digitalWrite(LED_G, LOW);
  digitalWrite(LED_B, LOW);
  digitalWrite(LED_R, LOW);

  Serial2.begin(9600);
}

void loop(void)
{
  str = "";
  if (Serial2.available()) {
    // 讀取傳入的字串直到"#"結尾
    str = Serial2.readStringUntil('#');

    String_to_Int(str, 2);
  }

  if (str.length() != 0) {
    if (SA[0] == "G") {
      if (A[1] == 1) {
        digitalWrite(LED_G, HIGH);
        Serial2.println("Green ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_G, LOW);
        Serial2.println("Green OFF");
      }
    }

    if (SA[0] == "B") {
      if (A[1] == 1) {
        digitalWrite(LED_B, HIGH);
        Serial2.println("Blue ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_B, LOW);
        Serial2.println("Blue OFF");
      }
    }

    if (SA[0] == "R") {
      if (A[1] == 1) {
        digitalWrite(LED_R, HIGH);
        Serial2.println("Red ON");
      }
      if (A[1] == 0) {
        digitalWrite(LED_R, LOW);
        Serial2.println("Red OFF");
      }
    }
  }
}

void String_to_Int(String temp, int count)
{
  int index;

  index = temp.indexOf(',');
  SA[0] = temp.substring(0, index);

  for (int i = 1; i < count; i++) {
    temp = temp.substring(index + 1, temp.length());
    index = temp.indexOf(',');
    SA[i] = temp.substring(0, index);
    A[i] = SA[i].toInt();
  }
}
Pi pico HC06

這個測試程式主要可以「接收」使用者的命令進行RGB-LED電路模組的控制,並且「發送」訊息回應使用者。

接收命令 RGB-LED動作 發送回應訊息
R,1 亮起紅色光 Red ON
R,0 熄滅紅色光 Red OFF
G,1 亮起綠色光 Green ON
G,0 熄滅綠色光 Green OFF
B,1 亮起藍色光 Blue ON
B,0 熄滅藍色光 Blue OFF

5. Tinker Board 2S Python測試流程:

  1. 輸入控制命令
  2. 發送控制命令至MCU
  3. 等候來自MCU的回應訊息
  4. 接收MCU的回應訊息
  5. 顯示來自MCU的回應訊息

6. Tinker Board 2S Python測試程式:

在 Tinker 上,請用您喜歡的文字編輯器新增一個 .py 檔,例如:

nano SERIAL_DATA_IO.py
create .py

SERIAL_DATA_IO.py 內容如下:

import serial
import argparse
import io
import time
import numpy as np
import cv2

from PIL import Image

parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument(
    '--com', help='Number of UART prot.', required=True)
args = parser.parse_args()

COM_PORT = args.com
BAUD_RATES = 9600
ser = serial.Serial(COM_PORT, BAUD_RATES,timeout=0, parity=serial.PARITY_EVEN, stopbits=1)

while(True):
    COMMAND = input('請輸入控制命令=')

    Data_flag = 0
    while(Data_flag == 0):
        Send_UART_command = COMMAND.encode(encoding='utf-8')    
        ser.write(Send_UART_command)  # 訊息必須是位元組類型

        time.sleep(0.5)
        while ser.in_waiting:
            mcu_feedback = ser.readline().decode()  # 接收回應訊息並解碼
            Data = mcu_feedback.strip('\r\n')

            if 'ON' in Data:
                Data_flag = 1
                print('MCU 回應:', Data)
            elif 'OFF' in Data:
                Data_flag = 1
                print('MCU 回應:', Data)
            else:
                Data_flag = 0
tinker bluetooth communication

這個程式可以讓使用者可以把控制命令從Tinker Board 2S發送至MCU,並且等待MCU的回應訊息,接收回應訊息後必須檢查序列資料的內容是否正確,最後在螢幕顯示MCU的回傳訊息。

使用以下指令來對指令 com port 進行通訊:

python3 SERIAL_DATA_IO.py --com /dev/rfccomm1
run .py to communicate with MCU via BT

Raspberry Pi pico 實際執行照片

二、Tinker Board 2S與Linkit-7697以BLE藍牙通訊方式進行連結

透過BLE藍牙通訊協定進行連結最大的特點,便是Tinker Board 2S與藍牙裝置彼此之間不需要進行「事前配對」,只要Tinker Board 2S開啟藍牙存取功能,在系統中所運作的程式中便可以進行配對,並且可以在程式結束之後立刻將系統資源進行釋放,這樣的運作方式,除了可以更加有效率進行各種藍牙周邊裝置的資源分配,還可以讓Tinker Board 2S在程式運作的過程中迅速完成各類裝置的資料存取。

本篇教學所採用的Linkit-7697 MCU內建了BLE藍牙通訊功能,不需要再額外連接藍牙通訊模組,而且也擁有可以用於進行各種電路周邊模組連接的數位類比控制接腳,非常適合進行各種藍牙通訊控制應用,開發環境亦支援搭配Arduino IDE進行開發。

1. 測試電路

2. Linkit-7697 BLE藍牙通訊存取程式設計流程(簡易輸入與輸出資料傳輸測試)

設定通訊埠存取規範,如:存取名稱、GATT存取方式等。
接收來自Tinker Board 2S以GATT傳輸的串列資料
檢查串列資料並進行RGB-LED的操作
透過GATT方式將輸出串列資料回應給Tinker Board 2S

3. Linkit-7697 BLE藍牙通訊Arduino IDE測試程式片段:

請用 Arduino IDE (需先安裝 LinkIt 7697 相關套件)燒錄以下程式,可參考 [LinkIt 7697 BlocklyDuino 使用指南] (已移交 CAVEDU管理)

#include <LBLE.h>
#include <LBLEPeriphral.h>


#define LED_G 10
#define LED_B 11
#define LED_R 12


String Send_Data;


String SA[2];
int A[2];


// Define a simple GATT service with only 1 characteristic
LBLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214");
//LBLECharacteristicInt switchCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", LBLE_READ | LBLE_WRITE);
LBLECharacteristicString switchCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", LBLE_READ | LBLE_WRITE);


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);


  Serial.begin(9600);


  //初始化BLE
  Serial.println("BLE begin");
  LBLE.begin();
  while (!LBLE.ready()) {
    delay(100);
  }
  Serial.println("BLE ready");
  Serial.print("device address is:");
  Serial.println(LBLE.getDeviceAddress()); //顯示7697 BLE addr


  //設定廣播資料
  // In this case, we simply create an advertisement that represents an
  // connectable device with a device name
  LBLEAdvertisementData advertisement;
  advertisement.configAsConnectableDevice("BLE Jack");


  // Configure our device's Generic Access Profile's device name
  // Ususally this is the same as the name in the advertisement data.
  LBLEPeripheral.setName("BLE Jack");


  // Add characteristics into ledService
  ledService.addAttribute(switchCharacteristic);


  // Add service to GATT server (peripheral)
  LBLEPeripheral.addService(ledService);


  //啟動GATT伺服器,此時已可被連線
  LBLEPeripheral.begin();


  //開始廣播
  LBLEPeripheral.advertise(advertisement);
}


void loop()
{
  if (switchCharacteristic.isWritten()) {
    String value = switchCharacteristic.getValue();


    String_to_Int(value, 2);


    Serial.print(SA[0]);
    Serial.print(",");
    Serial.println(A[1]);


    if (SA[0] == "G") {
      if (A[1] == 1) {
        digitalWrite(LED_G, HIGH);
        Send_Data = "Green ON";
        switchCharacteristic.setValue(Send_Data);
      }
      if (A[1] == 0) {
        digitalWrite(LED_G, LOW);
        Send_Data = "Green OFF";
        switchCharacteristic.setValue(Send_Data);
      }
      value = "";
    }


    if (SA[0] == "B") {
      if (A[1] == 1) {
        digitalWrite(LED_B, HIGH);
        Send_Data = "Blue ON";
        switchCharacteristic.setValue(Send_Data);
      }
      if (A[1] == 0) {
        digitalWrite(LED_B, LOW);
        Send_Data = "Blue OFF";
        switchCharacteristic.setValue(Send_Data);
      }
      value = "";
    }


    if (SA[0] == "R") {
      if (A[1] == 1) {
        digitalWrite(LED_R, HIGH);
        Send_Data = "Red ON";
        switchCharacteristic.setValue(Send_Data);
      }
      if (A[1] == 0) {
        digitalWrite(LED_R, LOW);
        Send_Data = "Red OFF";
        switchCharacteristic.setValue(Send_Data);
      }
      value = "";
    }
  }
  delay(100);
}


void String_to_Int(String temp, int count)
{
  int index;


  index = temp.indexOf(',');
  SA[0] = temp.substring(0, index);


  for (int i = 1; i < count; i++) {
    temp = temp.substring(index + 1, temp.length());
    index = temp.indexOf(',');
    SA[i] = temp.substring(0, index);
    A[i] = SA[i].toInt();
  }
}
linkit 7697 ble communication

這個測試程式主要可以「接收」使用者的命令進行RGB-LED電路模組的控制,並且「發送」訊息回應使用者。

接收命令 RGB-LED動作 發送回應訊息
R,1 亮起紅色光 Red ON
R,0 熄滅紅色光 Red OFF
G,1 亮起綠色光 Green ON
G,0 熄滅綠色光 Green OFF
B,1 亮起藍色光 Blue ON
B,0 熄滅藍色光 Blue OFF

 

4. Tinker Board 2S Python測試流程:

  1. 輸入控制命令
    發送控制命令至MCU
    等候來自MCU的回應訊息
    接收MCU的回應訊息
    顯示來自MCU的回應訊息

 

5. Tinker Board 2S檢查Linkit-7697 BLE 藍牙相關資訊

主要是針對Linkit-7697 BLE藍牙名稱與藍牙MAC資訊,無需事先配對,但因為Python程式需要針對特定的藍牙MAC進行存取,因此必須先行檢查,檢查的方式可由Tinker Board 2S作業系統的Bluetooth Device程式進行MAC的查詢。


6. Tinker Board 2S Python測試程式

同樣,請新增一個 .py 之後輸入以下內容

from gattlib import GATTRequester


import io
import time
import os
import sys


MAC_BT = 'F5:7C:00:2B:88:8C'
requester = GATTRequester(MAC_BT,False)


data_list = []


def BLE_connect():
  print("Connecting...", end='')
  sys.stdout.flush()
  requester.connect(True)
  print("OK!")


def send_data(Data_String):
  requester.write_by_handle(0xa2,Data_String)


def request_data():
  data = requester.read_by_handle(0xa2)[0]
  data_string = data.decode()
  # data_list = data_string.split(',')
  # return data_list
  return data_string


def main():
  BLE_connect()


  while (True):
    COMMAND = input('請輸入控制命令=')


    send_data(COMMAND)
    recive_data = request_data()


    print(recive_data)


if __name__ == '__main__':
  main()
tinker BLE communication

這個程式可以讓使用者可以把控制命令從Tinker Board 2S發送至MCU,並且等待MCU的回應訊息,接收回應訊息後必須檢查序列資料的內容是否正確,最後在螢幕顯示MCU的回傳訊息。

使用以下指令來對指令 com port 進行通訊:

python3 BLE_DATA_IO.py
run BLE communication

 

本篇教學是以藍牙通訊為主進行Tinker Board 2S與MCU的連結控制,藍牙通訊大都是應用在短距離傳輸且資料傳輸量不高的應用情境中,搭配上各種行動裝置大都也將藍牙通訊列為標準配備,使得藍牙通訊成為用途極廣的物聯網應用技術,但若是要進行長距離的遠端遙控,藍牙通訊便會有所限制,因此,我們會在另外一篇「ASUS Tinker Board 2S 與MCU的聯結應用—<WiFi無線通訊篇>」,針對TCP/IP通訊應用進行詳細的介紹,希望透過TCP/IP的相關協定應用設計,將整個物聯網相關應用範疇拓展到更加寬廣的應用情境當中。

 

相關文章

 

 

Viewing all 678 articles
Browse latest View live