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

Bring AI to the edge ! NVIDIA Jetson AGX ORIN 邊緣運算開發套件

$
0
0

Bring AI to the edge ! NVIDIA Jetson AGX ORIN 平台作為新一代的邊緣運算平台,在去年公布之後,終於在本次 GTC2022 公布了開發套件。Jetson AI 大使阿吉老師表示非常期待~  先置入行銷一下 CAVEDU 在 Jetson 平台上的成果,由左到右分別為我們特製的 JetRacer / Jetbot、AI 大使證書、專訪頁面以及課程紀錄照片


好的,進入正題。根據 NVIDIA 執行長黃仁勳先生在 GTC2022 keynote表示,機器人是下一個 AI 大放異彩的領域 (當然影片中還提到元宇宙等等更多內容,千萬別錯過)。本文主要在介紹最新的 Jetson NVIDIA Jetson AGX ORIN 平台,在 NVIDIA DRIVE 自動駕駛平台上也扮演了非常重要的角色喔!來看看相關發展吧。

從 2014 年的 Jetson Tk1 開始,Jetson 系列就希望把更強大的運算能力帶到物聯網的終端,CAVEDU 從 2019 年的 Jetson Nano 加入這輛高速列車,一路也做了不少成果喔!(參考 [活動紀錄] 2021 RK-Jetbot機器學習道路識別競賽-中區邀請賽相關文章 )

Jetson 系列對機器人與邊緣AI帶來了重大的衝擊與影響,當然也有賴於上下游生態系的整合

Jetson 生態系夥伴目前已經超過130家以上 (希望有一天可以在這裡看到 CAVEDU)

除了硬體效能不斷提升之外,這一切也多虧了軟體面的不斷進步:包含 AI 模型開發、應用程式框架以及 Jetpack SDK

對於 AI 模型來說,如何更快訓練、更泛用以及更高效能一直都是努力的目標。

針對不同領域,NVIDIA 也推出了對應的 AI 加速應用,除了一般開發用電腦之外,當然也可以部署在 ORIN 等邊緣運算平台上

完整的軟體支援,正是 Jetson 強大的秘密。您可以參考 TAO 工具包,代表 Train, Adapt 與 Optimize。可將各種預訓練模型應用於邊緣雲算裝置上。

新一代的機器人需要更多感測器、更高一級的應用,當然也需要更強大的運算能力來支撐!

好了!主角登場,Jetson AGX ORIN 開發套件,規格請看下圖,簡單說就是 “邊緣端的伺服器等級AI效能“,也就是本文主題 Bring AI to the Edge。

相比 Jetson AGX Xavier,ORIN 效能當然強大更多了,視覺、對話以及任何AI模型都能部署並運行於 ORIN 上。

開發套件會有以下內容,開箱文很快就來了,敬請期待喔!

相關資訊感謝 NVIDIA 原廠提供,ORIN 平台詳細資料請參閱 https://developer.nvidia.com/sites/default/files/akamai/Jetson_AGX_Orin_Developer_Kit_RG.pdf

也請參考 NVIDIA blog [NVIDIA 宣布推出全新 Jetson AGX Orin 機器人電腦,為未來的邊緣人工智慧與自主機器開拓發展之路]


NVIDIA® Jetson™ TX2 NX系統安裝以及將系統移動至SSD開機教學

$
0
0
撰寫/攝影 郭俊廷
前情提要  

 

時間 1~2 小時 材料表
  • NVIDIA® Jetson™ TX2 NX
  • Linux作業系統電腦 x 1
  • 鍵盤滑鼠
  • USB TO micro USB線
  • 90W功率的DC變壓器
  • SAMSUNG EVO Plus 500GB SSD
成本
難度 ★★☆☆☆

NVIDIA® Jetson™ TX2 NX介紹

以下是我們的 NVIDIA® Jetson™ TX2 NX 套件外觀與使用情境

接著來看一下NVIDIA Jetson TX2 NX原廠網站的介紹

NVIDIA ® Jetson ™ TX2 NX 讓入門級嵌入式和邊緣產品的 AI 性能更上一層樓。它提供高達 Jetson Nano 2.5 倍的性能,並與 Jetson Nano 和 Jetson Xavier™ NX 有相同的外形尺寸和引腳的兼容性。

這個小模塊為整個 AI 打包了硬體加速器,而 NVIDIA JetPack ™ SDK 提供了您在應用程序中使用它們所需的工具。使用來自 NVIDIA NGC ™和 NVIDIA TAO 工具包的預訓練 AI 模型,自定義 AI 網路開發變得容易,容器化部署使您的產品更新變得更靈活和無縫。

易於開發和部署速度——再加上外形尺寸、性能和功耗優勢的獨特組合——使 Jetson TX2 NX 成為理想的大眾市場 AI 產品平台。 

接著介紹NVIDIA ® Jetson ™ TX2 NX的產品技術規格

技術規格
AI 效能 1.33 TFLOPS
GPU NVIDIA Pascal™ 架構,配備 256 個 NVIDIA CUDA 核心
CPU 雙核心 Denver 2 64 位元 CPU 與四核心 ARM A57 Complex
記憶體 4 GB 128 位元 LPDDR4

51.2 GB/s

儲存空間 16 GB eMMC 5.1
電源 7.5W | 15W
PCIe 1 x1 + 1 x2

PCIe Gen2, total 30 GT/s

CSI 相機 最多至 5 個相機

(透過虛擬頻道 12 個)

12 個 MIPI CSI-2 D-PHY 通道

D-PHY 1.2 (最高 30 Gbps)

Video Encode 1x 4K60 | 3x 4K30 | 4x 1080p60 | 8x 1080p30 (H.265)

1x 4K60 | 3x 4K30 | 7x 1080p60 | 14x 1080p30 (H.264)

影片編碼 2x 4K60 | 4x 4K30 | 7x 1080p60 | 14x 1080p30 (H.265 & H.264)
顯示 2 multi-mode DP 1.2/eDP 1.4/HDMI 2.0

1x 2 DSI (1.5Gbps/lane)

連線能力 10/100/1000 BASE-T Ethernet
機械規格 69.6 mm x 45 mm

260-pin SO-DIMM edge 接頭

再來比較我們常用的四種 Jetson 模組規格

比較 NVIDIA Jetson 模組規格

Jetson Nano TX2 NX Jetson Xavier NX 16GB Jetson AGX Xavier 64GB
人工智慧效能 472 GFLOPS 1.33 TFLOPS 21 TOPS 32 TOPS
GPU 128 核心 NVIDIA Maxwell™ GPU 256 核心 NVIDIA Pascal™ GPU 具備 48 個 Tensor 核心的 384 核心 NVIDIA Volta™ GPU 具備 64 個 Tensor 核心的 512 核心 NVIDIA Volta GPU
CPU 四核心 ARM® Cortex®-A57 MPCore 處理器 雙核心 Denver 2 64 位元 CPU 與四核心 Arm Cortex-A57 MPCore 處理器 6 核心 NVIDIA Carmel Arm®v8.2 64 位元 CPU 6MB L2 + 4MB L3 8 核心 NVIDIA Carmel Arm®v8.2 64 位元 CPU 8MB L2 + 4MB L3
記憶體 4GB 64 位元 LPDDR4 25.6GB/秒 4GB 128 位元 LPDDR4 51.2GB/秒 16GB 128 位元 LPDDR4x 59.7GB/秒 64GB 256 位元 LPDDR4x 136.5GB/秒
儲存空間 16GB eMMC 5.1 16GB eMMC 5.1 16GB eMMC 5.1 32GB eMMC 5.1
功耗 5W | 10W 7.5W | 15W 10W | 15W | 20W 10W | 15W | 30W
機械規格 69.6mm x 45mm

260 針型 SO-DIMM 接頭

69.6mm x 45mm

260 針型 SO-DIMM 接頭

69.6mm x 45mm

260 針型 SO-DIMM 接頭

100mm x 87mm

699 針型接頭

組合型板式熱交換器

更多的模組資料請參考原廠網站

系統安裝

接著要介紹如何安裝系統至內建的eMMC裡面,由於目前(2022年)新出產的Jetson TX2 NX以及Jetson Xavier NX都無內建SD卡卡槽,所以需要把系統燒錄至內建的eMMC裡面。

首先請準備一台安裝Linux作業系統的電腦,本文中使用的作業系統為Ubuntu 18.04 LTS,建議使用此版本,Ubuntu 20.04 LTS目前經測試使用最新板NVIDIA SDK Manager會無法正常使用。

安裝JetPack4.5(經測試目前TX2 NX使用此底板無法透過NVIDIA SDK Manager正常安裝JetPack4.6稍後會介紹使用指令安裝方法,如更新後可正常安裝會在本文更新)

Step 1.

首先要準備一台安裝Linux作業系統的電腦,本文中使用的作業系統為Ubuntu 18.04 LTS。

Step 2.

到NVIDIA網站上下載NVIDIA SDK Manager,本文中使用的版本為1.7.2-9007:https://developer.nvidia.com/nvidia-sdk-manager

Step 3.

下載後點擊檔案並選擇install按鈕即可安裝,也可以打開terminal,前往檔案所在資料夾後,輸入以下指令安裝對應版本的程式。

sudo apt install ./sdkmanager-[version].deb

 

Step 4.

安裝好後,搜尋SDK Manager就可以點擊後開啟以下畫面。

也可以在terminal中輸入以下指令開啟。

sdkmanager

畫面開啟後要登入 NVIDIA 開發者帳號,還沒有帳號請先註冊(免費)

Step 5.

接下來需要確認所有的硬體裝置都準備完成,你現在正在使用的電腦(Host Machine)跟Jetson™ TX2 NX(Target Hardware)有連接在一塊並在同一個網域下(透過電腦的USB線連接到Jetson™ TX2 NX的Micro USB孔),並選擇JetPack 4.5.1,並且重新讀取開發版選擇開發板Jetson TX2 NX,DeepStream不要打勾,即可進入下一步。

如果第一次下載還找不到開發板定義檔,請先勾選 Host Machine 下載到本機端。

 

如果已經下載過檔案到本機端過,可以取消勾選Host Machine,直接安裝系統到Jetson TX2 NX當中。

 

注意,開發板在此要選擇 Jetson TX2 modules (系統會自動判斷為Jetson TX2 NX)

接著選擇TARGET OPERATING SYSTEM(目標系統)為JetPack 4.5.1(rev.1),即可進入下一步。

Step 6.

接著要選擇要下載那些套件,本文中按照預設會全部下載並安裝,記得要勾選下方的 accept license,如果要馬上安裝的話,請不要勾選 Download now. Install later。如果只是想要先下載之後再安裝,則可勾選。

Step 7.

接下來,需要一些時來的下載與安裝(如果是第一次下載會花比較多時間下載,下載過一次後就不用重新下載)。這時候請記得先把Jetson TX2 NX 接上銀幕、鍵盤、滑鼠,後續要建立登入的帳號密碼。

下載好會開始安裝Jetson OS 就是Jetson TX2 NX的Ubuntu作業系統,安裝完之後要先建立一組帳號密碼才可以繼續安裝其他套件。開機後會出現如下圖的安裝步驟,選擇鍵盤類型、地區時間、帳號密碼等資訊。這些系統設定之後都可以修改。

出現桌面,代表帳號建立成功了!

 

在 SDK Manager 中輸入剛剛建立的帳號密碼,就可以繼續安裝Jetson SDK Components

如果是要重新安裝之前已安裝好的系統,則會跳出以下畫面,輸入你的帳號密碼就可以開始重新安裝系統。

Step 8.

SDK Manager 看到這個畫面(右下角出現FINISH and EXIT)就代表下載燒錄完成囉!由於內建的eMMC容量只有16G,在安裝完所有套件後空間將剩餘不多,必須將系統移動到SSD開機。 


使用指令燒錄系統方法

由於JetPack4.6 會因為載板與NVIDIA SDK Manager,在安裝時會發生錯誤,原廠建議我們使用指令燒錄系統,以下將介紹如何使用指令燒錄系統。

Step 1.

首先先讓板子進入 Recovery Mode,做法是用 jumper 插上pin9 與 pin10(FC REC,GND),之後再通電,如下圖紅框處:

Step 2.

接著一樣使用NVIDIA SDK Manager下載JetPack4.6相關套件並且不要安裝。

Step 3.

接著移動到 JetPack4.6 的資料夾 JetPack_4.6_Linux_JETSON_TX2_TARGETS/Linux_for_Tegra的位置,執行以下指令(根據你所下載的路徑不同位置也不同)

cd ~/nvidia/nvidia_sdk/JetPack_4.6_Linux_JETSON_TX2_TARGETS/Linux_for_Tegra

Step 4.

接著使用以下指令進行燒錄

sudo ./flash.sh jetson-xavier-nx-devkit-tx2-nx mmcblk0p1

如想了解更多SDK安裝流程問題請參考NVIDIA原廠說明


將系統移動到SSD開機

安裝完系統及相關套件之後,板子原本的 16G eMMC 裡面已經快滿了,需將系統移動到 SSD 來開機。由於Jetson TX2 NX目前無法預設將系統燒錄至SSD開機,所以需要先將系統燒錄至eMMC,再將系統由eMMC複製到SSD開機。

在此使用 forecr_scripts 的 change_rootfs_storage_direct-emmc_to_ssd 專案將系統移動至SSD開機

如果這顆 SSD 是全新拆封之前都未用過的話,需要將其格式化成 Jetson TX2 NX 可讀之系統,請參考以下文章 [在Jetson Xavier NX上以SSD運行作業系統] 中的格式化SSD一段

接著下載移動系統到SSD下載點解壓縮後可以看到 change_rootfs_storage_direct-emmc_to_ssd.sh 這個檔案。

接著,設定該腳本權限,並執行它:

ls -l change_rootfs_storage_direct-emmc_to_ssd.sh

chmod +x change_rootfs_storage_direct-emmc_to_ssd.sh

ls -l change_rootfs_storage_direct-emmc_to_ssd.sh

sudo ./change_rootfs_storage_direct-emmc_to_ssd.sh /dev/nvme0n1p1

全部指令都執行完最後會如下圖顯示Reboot for changes to take effect,提示需要重新開機讓所有設定生效。

重開機後可以看到系統左下方出現SD卡選項,代表系統已經順利移動到SSD裡面了。

參考資料:

  • https://www.forecr.io/blogs/bsp-development/change-root-file-system-to-m-2-ssd-directly

 

 

 

 

 

 

前言
文字……..

 

 

 

內文

NVIDIA® Jetson™ TX2 NX介紹

以下是我們的NVIDIA® Jetson™ TX2 NX套件的樣子以及使用情境

01

02

接著來看一下NVIDIA Jetson TX2 NX原廠網站的介紹

NVIDIA ® Jetson ™ TX2 NX 讓入門級嵌入式和邊緣產品的 AI 性能更上一層樓。它提供高達 Jetson Nano 2.5 倍的性能,並與 Jetson Nano 和 Jetson Xavier™ NX 有相同的外形尺寸和引腳的兼容性。

這個小模塊為整個 AI 打包了硬體加速器,而 NVIDIA JetPack ™ SDK 提供了您在應用程序中使用它們所需的工具。使用來自 NVIDIA NGC ™和 NVIDIA TAO 工具包的預訓練 AI 模型,自定義 AI 網路開發變得容易,容器化部署使您的產品更新變得更靈活和無縫。

易於開發和部署速度——再加上外形尺寸、性能和功耗優勢的獨特組合——使 Jetson TX2 NX 成為理想的大眾市場 AI 產品平台。

接著介紹NVIDIA ® Jetson ™ TX2 NX的產品技術規格
技術規格
AI 效能
1.33 TFLOPS
GPU
NVIDIA Pascal™ 架構,配備 256 個 NVIDIA CUDA 核心
CPU
雙核心 Denver 2 64 位元 CPU 與四核心 ARM A57 Complex
記憶體
4 GB 128 位元 LPDDR4
51.2 GB/s
儲存空間
16 GB eMMC 5.1
電源
7.5W | 15W
PCIe
1 x1 + 1 x2
PCIe Gen2, total 30 GT/s
CSI 相機
最多至 5 個相機
(透過虛擬頻道 12 個)
12 個 MIPI CSI-2 D-PHY 通道
D-PHY 1.2 (最高 30 Gbps)
Video Encode
1x 4K60 | 3x 4K30 | 4x 1080p60 | 8x 1080p30 (H.265)
1x 4K60 | 3x 4K30 | 7x 1080p60 | 14x 1080p30 (H.264)
影片編碼
2x 4K60 | 4x 4K30 | 7x 1080p60 | 14x 1080p30 (H.265 & H.264)
顯示
2 multi-mode DP 1.2/eDP 1.4/HDMI 2.0
1x 2 DSI (1.5Gbps/lane)
連線能力
10/100/1000 BASE-T Ethernet

 

機械規格
69.6 mm x 45 mm
260-pin SO-DIMM edge 接頭

 

 

再來比較我們常用的四個Jetson 模組規格
比較 NVIDIA Jetson 模組規格

 

Jetson Nano
TX2 NX
Jetson Xavier NX 16GB
Jetson AGX Xavier 64GB
人工智慧效能
472 GFLOPS
1.33 TFLOPS
21 TOPS
32 TOPS
GPU
128 核心 NVIDIA Maxwell™ GPU
256 核心 NVIDIA Pascal™ GPU
具備 48 個 Tensor 核心的 384 核心 NVIDIA Volta™ GPU
具備 64 個 Tensor 核心的 512 核心 NVIDIA Volta GPU
CPU
四核心 ARM® Cortex®-A57 MPCore 處理器
雙核心 Denver 2 64 位元 CPU 與四核心 Arm Cortex-A57 MPCore 處理器
6 核心 NVIDIA Carmel Arm®v8.2 64 位元 CPU 6MB L2 + 4MB L3
8 核心 NVIDIA Carmel Arm®v8.2 64 位元 CPU 8MB L2 + 4MB L3
記憶體
4GB 64 位元 LPDDR4 25.6GB/秒
4GB 128 位元 LPDDR4 51.2GB/秒
16GB 128 位元 LPDDR4x 59.7GB/秒
64GB 256 位元 LPDDR4x 136.5GB/秒
儲存空間
16GB eMMC 5.1
16GB eMMC 5.1
16GB eMMC 5.1
32GB eMMC 5.1
功耗
5W | 10W
7.5W | 15W
10W | 15W | 20W
10W | 15W | 30W
機械規格
69.6mm x 45mm
260 針型 SO-DIMM 接頭
69.6mm x 45mm
260 針型 SO-DIMM 接頭
69.6mm x 45mm
260 針型 SO-DIMM 接頭
100mm x 87mm
699 針型接頭
組合型板式熱交換器

 

更多的模組資料請參考以下原廠網站
https://www.nvidia.com/zh-tw/autonomous-machines/embedded-systems/

系統安裝

接著要介紹如何安裝系統至內建的eMMC裡面,由於目前(2022年)新出產的Jetson TX2 NX以及Jetson Xavier NX都無內建SD卡卡槽,所以需要把系統燒錄至內建的eMMC裡面。

首先請準備一台安裝Linux作業系統的電腦,本文中使用的作業系統為Ubuntu 18.04 LTS,建議使用此版本,Ubuntu 20.04 LTS目前經測試使用最新板NVIDIA SDK Manager會無法正常使用。

安裝JetPack4.5(經測試目前TX2 NX使用此底板無法透過NVIDIA SDK Manager正常安裝JetPack4.6稍後會介紹使用指令安裝方法,如更新後可正常安裝會在本文更新)

 

Step 1.
首先要準備一台安裝Linux作業系統的電腦,本文中使用的作業系統為Ubuntu 18.04 LTS。
Step 2.
到NVIDIA網站上下載NVIDIA SDK Manager,本文中使用的版本為1.7.2-9007。
網站如下:
https://developer.nvidia.com/nvidia-sdk-manager
Step 3.
下載後點擊檔案並選擇install按鈕即可安裝。

也可以打開terminal,前往檔案所在資料夾後,輸入以下指令安裝對應版本的程式。

sudo apt install ./sdkmanager-[version].deb

Step 4.
安裝好後,搜尋SDK Manager就可以點擊後開啟以下畫面。

03

也可以在terminal中輸入以下指令開啟。
sdkmanager

04
畫面開啟後要登入NVIDIA帳號,還沒有帳號需要先註冊(免費)。

05

Step 5.
接下來需要確認所有的硬體裝置都準備完成,你現在正在使用的電腦(Host Machine)跟Jetson™ TX2 NX(Target Hardware)有連接在一塊並在同一個網域下(透過電腦的USB線連接到Jetson™ TX2 NX的Micro USB孔),並選擇JetPack 4.5.1,並且重新讀取開發版選擇開發板Jetson TX2 NX,DeepStream不要打勾,即可進入下一步。

 

如果第一次下載還沒有開發版 可以先勾選Host Machine下載到本機端。

06

如果已經下載過檔案到本機端過,可以取消勾選Host Machine,直接安裝系統到Jetson TX2 NX當中。

07

注意這裡要選擇開發版Jetson TX2 modules(系統會自動判斷為Jetson TX2 NX)

08
接著選擇TARGET OPERATING SYSTEM(目標系統)為JetPack 4.5.1(rev.1)即可進入下一步。

09
Step 6.
都確認好後就是選擇要下載的項目,本文中按照預設進行全部下載安裝,記得要勾選下方的accept license,如果要馬上安裝記得不要勾選Download now. Install later。如果你只是想要先下載還沒有要安裝,可以勾選Download now. Install later。

10
Step 7.
接下來就是會花一段時間的下載與安裝(如果是第一次下載會花比較多時間下載,下載過一次後就不用重新下載)。
這時候請記得先把Jetson TX2 NX 接上銀幕、鍵盤、滑鼠等等要用來建立帳號密碼
下載好會開始安裝Jetson OS 就是Jetson TX2 NX的Ubuntu作業系統,安裝完之後要先建立一組帳號密碼才可以繼續安裝其他套件。
會出現如下圖的安裝步驟,選擇鍵盤類型、地區時間、帳號密碼等資訊。

11
出現桌面的畫面後代表帳號建立成功。

12

把剛剛建立的帳號密碼輸入至以下出現的畫面就可以繼續安裝Jetson SDK Components

13

如果是要把之前安裝好的系統重新安裝會跳出以下畫面,輸入你的帳號密碼就可以開始重新安裝系統。

14
Step 8.
看到這個畫面就代表下載燒錄完成囉! 由於內建的eMMC容量只有16G,在安裝完所有套件後空間將剩餘不多,必須將系統移動到SSD開機。
15
接著介紹使用指令燒錄系統方法
由於JetPack4.6因為載板與NVIDIA SDK Manager安裝時會發生錯誤,原廠建議我們使用指令燒錄系統,以下將介紹如何使用指令燒錄系統。

Step 1.
首先先用將板子進入Recovery Mode
Recovery Mode的方法是先將jumper插上pin9與pin10(FC REC,GND)在通電。
如下圖紅框處:

16

Step 2.
接著一樣使用NVIDIA SDK Manager下載JetPack4.6相關套件並且不要安裝。

Step 3.
接著移動到JetPack4.6的資料夾JetPack_4.6_Linux_JETSON_TX2_TARGETS/Linux_for_Tegra的位置,執行以下指令(根據你所下載的路徑不同位置也不同)
cd ~/nvidia/nvidia_sdk/JetPack_4.6_Linux_JETSON_TX2_TARGETS/Linux_for_Tegra

 

Step 4.
接著使用以下指令進行燒錄
sudo ./flash.sh jetson-xavier-nx-devkit-tx2-nx mmcblk0p1

 

如想了解更多SDK安裝流程問題請參考原廠網站
https://docs.nvidia.com/sdk-manager/install-with-sdkm-jetson/index.html#install-with-sdkm-jetson

將系統移動到SSD開機
安裝完系統及相關套件到16G eMMC裡面已經沒有多少空間可用,需將系統移動到SSD開機。由於Jetson TX2 NX目前無法預設將系統燒錄至SSD開機,所以需要先將系統燒錄至eMMC,在將系統由eMMC複製到SSD開機。
我們使用forecr_scripts的change_rootfs_storage_direct-emmc_to_ssd專案將系統移動至SSD開機

首先如果是第一次使用該SSD需要將該SSD格式化成Jetson TX2 NX可讀之系統,請參考以下文章在Jetson Xavier NX上以SSD運行作業系統之Step 2. 格式化SSD

【教學】在Jetson Xavier NX上以SSD運行作業系統

接著下載移動系統到SSD下載點:
https://github.com/mistelektronik/forecr_scripts/blob/master/change_rootfs_storage_direct-emmc_to_ssd.zip

解壓縮後可以得到 change_rootfs_storage_direct-emmc_to_ssd.sh 的檔案

接著
將下載的腳本文件(sh檔)的權限設置為可執行文件並使用以下指令運行它:

ls -l change_rootfs_storage_direct-emmc_to_ssd.sh
chmod +x change_rootfs_storage_direct-emmc_to_ssd.sh
ls -l change_rootfs_storage_direct-emmc_to_ssd.sh
sudo ./change_rootfs_storage_direct-emmc_to_ssd.sh /dev/nvme0n1p1

 

全部指令都執行完最後會如下圖顯示Reboot for changes to take effect.提示你要重新開機。
17

重開機後可以看到系統左下方出現SD卡選項代表系統已經移動到SSD裡面了。

18

參考資料:
https://www.forecr.io/blogs/bsp-development/change-root-file-system-to-m-2-ssd-directly

 

 

 

 

 

使用DF燈光/馬達控制板,透過電腦來控制馬達轉速

$
0
0

本篇要介紹 DF燈光/馬達控制板的使用方式,這片控制板的優點就是可以透過電腦來控制PWM輸出。其模組的外接電源支持5~24伏特的電壓,可以驅動 50瓦的直流馬達,或等於控制5米長LED光條的亮度,適合用於沉水馬達水量控制、散熱風扇的轉速控制、燈光調節控制…等。

由於這個模組的PWM輸出的訊號控制非常多元,所以筆者只針對直流馬達的轉速控制來做測試,有興趣的讀者也可以透過本文的方法,將直流馬達改成LED燈條來做測試。

購物連結:https://robotkingdom.com.tw/product/light-motor-driver-for-python-v1-0/

撰寫/攝影 鈺莨
前情提要
時間 20分鐘 材料表

 

成本
難度 ★☆☆☆☆

 

這片 DF燈光/馬達控制板 提供了以下方式來進行 PWM 訊號控制:

  • 電腦透過傳輸線,執行軟體控制。
  • 電腦透過傳輸線,執行Python程式控制。
  • Arduino(或LinIt7697等開發板)燒錄程式後,透過序列埠(UART)控制。
  • 直接外接旋轉電位計(又稱可變電阻)控制。

另外DF網站也提供了在樹莓派中執行Python程式,透過UART的方式控制,所以控制PWM輸出的方式相當多元,以下來介紹這塊DF燈光/馬達控制板模組,如圖所示。

如何控制PWM輸出

接下來針對上述幾點的方式來說明PWM控制,但請注意必須都要外接電源才可以驅動PWM控制。

1. 電腦透過傳輸線,執行軟體控制

(1). 連接電腦USB孔、外接電源、直流馬達

下圖將解說如何連接電腦USB孔、外接電源、直流馬達。

(2). 安裝USB驅動程式(本篇以Windows電腦為主)
驅動程式請由此下載

解壓縮後安裝請安裝下方之執行檔,安裝完畢會在電腦的裝置管理員找到COM號。

 

由上個步驟找到COM號後,請開啟控制介面程式,這是第一種不需寫程式就可以直接控制DF燈光/馬達控制板。軟體下載請點我

開啟DF燈光/馬達控制板的控制介面程式,下圖為執行檔 (Light and Motor Driver.exe)。

開啟DF燈光/馬達控制板的控制介面程式後,執行方式:

  1. 先找到DF燈光/馬達控制板裝置的連接埠號。
  2. 勾選欲控制的頻道。目前測試A頻道有效,讀者們可以試試看B、C頻道。
  3. 調整此頻道的PWM輸出,數值為0~255。
  4. 按下Start

相關選項如下圖所示。

實際操作影片如下:

 

2. 電腦透過傳輸線,執行Python程式控制。

DF網站提供了2種不同的Python套件模組安裝Python程式控制,程式亦可以在樹莓派上執行,本範例示範在電腦使用Anaconda執行,若要安裝Anaconda 請參考本文【AI人工智慧-神經運算】環境建置:安裝Anaconda、Tensorflow、Keras與openCV(Windows篇)

以下說明套件安裝步驟及程式執行。

(1). 使用 pinpong 函式庫控制

● 下載程式
請由本連結取得 PWM_motor_concroller_via_python_pinpong_library.py 檔案。

● 請於 anaconda prompt 中安裝 pinpong套件:

pip install pinpong library

● 改程式,第17行需改成電腦的序列埠號,如下圖所示。若不知序列埠號,請回顧以上的『1.電腦透過傳輸線,執行軟體控制』的第2步

pwmd = DRI0050(port="COM14")

 

● 輸入以下指令執行程式

python  PWM_motor_concroller_via_python_pinpong_library.py

實際操作影片如下:

(2). 使用 modbus 函式庫控制

● 下載程式
由此下載 PWM_motor_concroller_via_python_pinpong_library.py 檔案

● 第17行需改成電腦的序列埠號,如下圖所示。若不知序列埠號,請回顧『1.電腦透過傳輸線,執行軟體控制』的第2步

PORT = "COM14"

● 請於 Anconda prompt 中安裝相關套件

pip install pyserial modbus_tk

● 執行程式

python  PWM_motor_concroller_via_python_modbus_library.py

實際操作影片如下:

3. Arduino(或LinIt7697等開發板)燒錄程式後,透過序列埠(UART)控制

DF燈光/馬達控制板也可以透過Arduino開發板燒錄程式,接TX、RX的腳位來控制,請由此下載 PWM_motor_concroller.ino程式,並透過 Arduino IDE 燒錄到開發板。

以下說明如何連接Arduino的TX、RX腳位,如下圖所示。

實際操作影片如下:

4. 外接旋轉電位計(又稱可變電阻)控制

最後一種操作本控制板的法是外接旋轉電位計,這是第二種不需寫程式來控制PWM訊號輸出,如下圖所示。

實際操作影片如下:

 

以上為您整理並測試過的DF燈光/馬達控制板的控制方式,希望您可以運用它做出更多更有趣的應用,我們下次見!

 

[ Wio Terminal ] 合體說明!熱影像攝影模組與wio terminal 開發板

$
0
0

本文將說明如何將熱影像攝影模組與 wio terminal 開發板組合起來,後續會有更好的攜帶性,當然也更穩固。

購物連結請點我

在開始之前,請先準備好十字螺絲起子。由於部分組裝有方向的問題,為了講述方便,本文會將看的到Wio Terminal螢幕的那面稱為正面,而有接腳的那面則稱之為背面,組裝時還請多多注意!閱讀文字的同時可以對照圖片確認組裝細節

時間 20 分鐘 材料表 Wio Terminal 開發板 x1

SparkFun 熱影像攝影模組/紅外線陣列 FOV,MLX90640 110度 x1

雷射切割壓克力背板

grove轉杜邦接頭4pin線 x1

金屬螺絲M2*8 2個

塑膠螺絲M3*6 4個

塑膠六角隔離柱M3*6 4個

塑膠M3螺帽  4個

成本
難度 新手 OK

零件很少,基本上就是把 Wio Terminal 和熱影像模組固定在壓克力背板,再接線就完成了。不過這篇有很多圖片輔助,希望對大家有幫助。

組裝步驟

STEP 1.
撕取壓克力背板的保護膜(如果有的話),再將Wio Terminal用金屬螺絲M2*8鎖在(圖1紅色圓框)壓克力背板上。這裡要注意Wio Terminal背面的腳位需對準壓克力挖空(圖1紅色方框)的部分。

STEP 2.

將4個M3*6塑膠六角隔離柱利用4個M3*6塑膠螺絲鎖在壓克力背板的背面。安裝時不要鎖的太緊以便步驟3的組裝。完成時會如下圖,紅圈是鎖點。

螺絲是從壓克力正面鎖進去的,如圖2.2的紅圈所示。

STEP 3.

步驟2完成後將熱影像攝影模組利用4個塑膠M3螺帽鎖於塑膠隔離柱上。此步驟須注意熱影像攝影模組的針腳方向需朝下,如下圖

STEP 4.

使用 Grove 轉杜邦接頭4pin線,將杜邦頭的那端與熱影像攝影模組的針腳連接,連接的位置如下

  • 黑線->GND
  • 紅線->3.3V
  • 白線->SDA
  • 黃線->SCL

STEP 5.

由於grove轉杜邦接頭4pin線很長,因此會需要整線,簡單整線方法如下:
將線穿過熱影像攝影模組底部再繞出來,繞個兩圈後就能得到較為合適的長度了,接著將線穿過右下的孔洞

接著轉至正面後將 grove 那端插入Wio Terminal 的左側接頭

這次的組裝到這裡就完成囉!比較困難也比較花時間的地方會是整線吧。後續還會有文章說明如何讓 wio terminal 開發板取得熱顯像模組的資料,敬請期待喔!

相關文章

 

 

 

Article 0

$
0
0

Production and supply-chain update

Production and supply-chain update

As you will have noticed, it can be hard to buy a Raspberry Pi unit from stock at the moment. Several factors are contributing to this, and we thought it would be helpful to provide an update on what’s been happening since we last wrote about this in October.

Supply

Over the last six months we’ve been working hard to get more Raspberry Pi products built and shipped to customers. Despite a variety of supply-chain challenges, we’ve consistently been able to build around half a million of our single-board computers and Compute Module products each month. As we said in October, the 28nm BCM2711 part used on Raspberry Pi 4 and Compute Module 4 has been more readily available than the 40nm parts used on our older products.

We have a strong pipeline of components, and will continue to build units at at least this rate over the coming months.

Demand

As we’ve said before, the current situation is as much a demand shock as a supply shock: demand for Raspberry Pi products increased sharply from the start of 2021 onwards, and supply constraints have prevented us from flexing up to meet this demand, with the result that we now have significant order backlogs for almost all products. In turn, our many resellers have their own backlogs, which they fulfil when they receive stock from us.

Raspberry Pi being made in our factory in Wales

These backlogs absorb Raspberry Pi units as fast as (or faster than!) we can produce them, with the result that little of our production volume ends up being immediately available on reseller websites. Where units do appear, bots often attempt to scalp stock which is then resold at higher prices elsewhere. Many Approved Resellers have implemented single-unit limits to combat this, with Adafruit and others going further and enforcing two-factor authentication – we’re encouraging other Approved Resellers to consider this route.

We spend a lot of time on backlog management. We have to balance volume demand from commercial and industrial customers with the demand we see from individuals. Right now we feel the right thing to do is to prioritise commercial and industrial customers – the people who need Raspberry Pis to run their businesses – we’re acutely aware that people’s livelihoods are at stake. There is currently enough supply to meet the needs of those customers. (Read to the end if you’re in this position and are struggling.) Unfortunately this comes at the cost of constrained supply for individual customer, who might be looking to buy a small number for home projects or for prototyping.

Advice

So, what should you do if you need to buy a Raspberry Pi in 2022?

Always buy from an Approved Reseller

We can’t emphasise this enough! Our Approved Resellers get preferential access to supplies of Raspberry Pi products. They’re also held to a single price: those people you see complaining on social media that they’ve seen Raspberry Pis on sale for vastly inflated amounts of money aren’t buying from Approved Resellers, who will all sell you a Raspberry Pi product for the price we state on our products pages plus your local taxes and shipping where appropriate.

If you’re a consumer, click on the “Buy Now” on a Raspberry Pi product page to find an Approved Reseller in your region. Some Approved Resellers take pre-orders, and should be able to give you a good indication of how long it will take to fulfil an order; others don’t, in which case you may want to use tools such as rpilocator to keep an eye on which resellers have recently received stock.

Consider Raspberry Pi 400, or Raspberry Pi Pico

These products are generally in better stock positions.

Raspberry Pi 400
Raspberry Pi 400

We set aside a certain amount of BCM2711 silicon supply for Raspberry Pi 400, which plays an important role in our mission to provide general-purpose PC computing at an affordable price. Many of our Approved Resellers have this product in stock today.

Raspberry Pi Pico
Pico

While they are not full-fledged PCs like other Raspberry Pi products, Raspberry Pi Pico, and the many third-party boards based on our RP2040 microcontroller, can be used for many of the same embedded applications. We have plenty of stock of Pico, and of RP2040.

Get in touch!

If you require volume supply of Raspberry Pi products for an industrial or commercial application, you can contact us at business@raspberrypi.com. There remain levers we can pull, and we’ll do our best to support you.

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的相關協定應用設計,將整個物聯網相關應用範疇拓展到更加寬廣的應用情境當中。

 

相關文章

 

 

Raspberry Pi 生產與供應鏈更新

$
0
0

本文取得授權後。翻譯自 [Production and supply-chain update],原文作者為 Eben Upton,原文中的我們指 “Raspberry Pi 基金會


生產與供應鏈更新

您應已發現,現在不太容易買到 Raspberry Pi。造成這個現象有幾個原因,我們認為這篇文章有助於說明從去年 10 月首次發文討論以來發生了什麼事情。

供貨

已過六個月以來,我們很努力產出更多 Raspberry Pi 產品並送達顧客手中。 Despite a variety of supply-chain challenges, we’ve consistently been able to 讓單板電腦與運算模組的產量每月可達約 50 萬 (half a million)。如去年 10 月所述,用於 Raspberry Pi 4 與 Compute Module 4 的 28nm BCM2711 晶片在供貨上已比較早期產品中的 40nm 晶片更穩定。

We have a strong pipeline of components, and will continue to build units at at least this rate over the coming months.

需求端

如前所述,the current situation is as much a demand shock as a supply shock:Raspberry Pi 相關產品的需求自 2021 年初以來急遽增加,而供應鏈相關限制使得我們無法彈性調整來滿足這波需求,導致 we now have significant order backlogs for almost all products. In turn, our many resellers have their own backlogs, which they fulfil when they receive stock from us.

Raspberry Pi being made in our factory in Wales

These backlogs absorb Raspberry Pi units as fast as (或更快!) we can produce them,造成這些極小的產量在 reseller 網站上一上架就秒殺。Where units do appear,但搶貨機器人 often attempt to scalp stock which is then resold at higher prices elsewhere。許多經過認證的 Resellers 已建立的一人一片限制來對抗這個現象,而 Adafruit 與其他 reseller 則進一步採用兩階段認證 – we’re encouraging other Approved Resellers to consider this route.

我們已投入大量時間來管理存貨。We have to balance volume demand from commercial and industrial customers with the demand we see from individuals。目前我們覺得要做的事情是 prioritise commercial and industrial customers – 需要 Raspberry Pi 來執行商業行為的人 – we’re acutely aware that people’s livelihoods are at stake。目前的供貨足以滿足顧客需求。(Read to the end if you’re in this position and are struggling.) Unfortunately this comes at the cost of constrained supply for individual customer, who might be looking to buy a small number for home projects or for prototyping.

建議

So, what should you do if you need to buy a Raspberry Pi in 2022?

請由認證經銷商購買

這真的講到不要講了!Our 認證經銷商 get preferential access to supplies of Raspberry Pi products. They’re also held to a single price: those people you see complaining on social media that they’ve seen Raspberry Pis on sale for vastly inflated amounts of money 都不是從認證經銷商購買的,who will all sell you a Raspberry Pi product for the price we state on our products pages 加上當地稅金與合理運費。

If you’re a consumer, click on the “Buy Now” on a Raspberry Pi product page to find an Approved Reseller in your region. Some Approved Resellers take pre-orders, and should be able to give you a good indication of how long it will take to fulfil an order; others don’t, in which case you may want to use tools such as rpilocator to keep an eye on which resellers have recently received stock.

Consider Raspberry Pi 400, or Raspberry Pi Pico

These products are generally in better stock positions.

Raspberry Pi 400
Raspberry Pi 400

We set aside a certain amount of BCM2711 silicon supply for Raspberry Pi 400, which plays an important role in our mission to provide general-purpose PC computing at an affordable price. Many of our Approved Resellers have this product in stock today.

Raspberry Pi Pico
Raspberry Pi Pico

Raspberry Pi Pico, and the many third-party boards based on our RP2040 microcontroller,雖然不像 Raspberry Pi 系列產品是功能完整的 PC, 也可用於許多相同的嵌入式專題中。我們 Pico 與 RP2040 的存貨非常充足。

保持聯絡!

If you require volume supply of Raspberry Pi products for an industrial or commercial application, you can contact us at business@raspberrypi.com. There remain levers we can pull,我們會盡全力協助您。

申請 NVIDIA NGC API key 用於 TAO toolkit DLI 課程

$
0
0

最近 NVIDIA 針對自家的 TAO toolkit (這發音真有意境) 推出了 [使用 TAO 工具套件訓練文字分類模型] DLI 課程,承襲 DLI 完整課程內容,本文只介紹如何申請本課程所需的 NGC API Key,其餘內容就請大家趕快參加課程囉!

請先申請 NVIDIA NGC 帳號(也可用開發者帳號),並由此頁面來申請 NGC API key,課程中會用到。

登入之後,從右上角的個人帳號點選 Setup

 

接著點選 Get API Key

在  API Key 頁面會有 CLI / Docker 的相關說明,但請點選 Generate API Key 即可

在 跳出的 [Generate a New API Key] 視窗中,點選 Confirm

這樣就在同一個頁面最下面看到 API Key 了!很簡單吧!

接著回到課程中,在第一步輸入剛剛申請的 AKI Key 就可以順利執行課程內容。


Jetson Xavier NX 使用 SSD 開機以及映像檔燒錄系統教學(2022版)

$
0
0

之前已經寫過 NVIDIA® Jetson Xavier™ NX系統安裝教學,由於新版的 NVIDIA® Jetson Xavier™ NX沒有記憶卡插槽,因此要安裝在內建的16G eMMC裡面,但這樣做的話,在安裝完系統與相關套件已經沒有多少空間可用,所以本篇將介紹如何將系統安裝在SSD以及使用映像檔燒錄開機教學。

相關設備購買請參考機器人王國商城

時間 1 ~ 2 小時 材料表
  • NVIDIA® Jetson™ NX 單板電腦
  • Linux作業系統電腦 x 1
  • 鍵盤滑鼠
  • USB TO micro USB線
  • 90W功率的DC變壓器
  • NVMe的SSD硬碟外接盒
成本  
難度 3顆星(滿分5)

如購買 CAVEDU 已安裝好系統的RK-NVIDIA® Jetson Xavier™ NX套件,系統已經燒錄在 SSD 中了~ 請直接跳到 Step B.透過映像檔燒錄系統至SSD開機 來看看如何燒錄映像檔至SSD。或是如果是要重新安裝或升級系統可參考Step A.的步驟。

NVIDIA SDK Manager下載點:https://developer.nvidia.com/nvidia-sdk-manager

先前已經有文章介紹過如何使用 NVIDIA SDK Manager 來燒錄系統至 Jetson TX2 NX,如還沒下載安裝過NVIDIA SDK Manager 與詳細的 NVIDIA SDK Manager 安裝流程還有安裝系統條件,請參考本文:https://blog.cavedu.com/2022/03/26/nvidia-jetson-tx2-nx-setup-ssd/

Step A.將系統燒錄至SSD

首先在 Jetson Xavier™ NX中,使用NVIDIA SDK Manager是可以正常安裝JetPack4.6的,並且版本在JetPack4.6以上就可以將系統預設安裝在eMMC以外的地方。

這裡介紹NVIDIA SDK Manager的安裝流程,以下流程皆須使用Ubuntu 18.04的作業系統操作。

Step 01

開啟NVIDIA SDK Manager並且將電腦的USB線連接到Jetson Xavier NX的Micro USB中,
如有下載過相關JetPack可以取消勾選Host Machine,並且將Target Hardware選擇Jetson Xavier NX modules,TARGET OPERATING SYSTEM需要選擇JetPack4.6以上才可以將系統安裝至SSD當中。

Step 02

選擇要下載的項目,本文將使用預設設定來全部下載安裝。請記得 勾選下方的 accept license,如果要馬上安裝,則不要勾選 Download now. Install later。如果想先下載之後再安裝,則可勾選該選項。

Step 03

開始安裝後輸入密碼後會跳出以下選項。

可以選擇自動安裝(如無法自動安裝再選擇手動安裝),IP使用預設IP,帳號密碼請輸入當初建立的帳號及密碼。

最重要的一點是把 5. Storage Device(儲存裝置) 由下拉式選單中選擇 NVMe,也就是 SSD儲存空間,便可以將系統預設安裝至SSD當中。

帳號密碼以及儲存裝置相關設定都正確的話,就可以將系統正確安裝至SSD中。安裝所有系統及套件需要大約 40分鐘,請耐心等候(根據您的電腦設備以及傳輸線的速度會影響安裝時間)

Step 04

出現以下畫面代表系統安裝成功。下一段要說明如何透過映像檔燒錄系統到 SSD 來開機


Step B.透過映像檔燒錄系統至SSD開機

由於我們使用的SSD硬碟是SAMSUNG EVO Plu NVMe M.2 固態硬碟 500GB,因此需要準備一個支援NVMe的SSD硬碟外接盒才可以將系統透過映像檔燒錄至SSD。

請先下載好映像檔,接著下載 balenaEtcher 燒錄軟體,此軟體可以燒錄壓縮過的映像檔(Windows/Mac/Linux系統電腦皆可用)

開啟balenaEtcher程式選擇要燒錄的映像檔(如下圖紅框處)

接著選取要燒錄的磁區(如下圖紅框處)

確認燒錄路徑(千萬別選錯啦~)

按下 Flash! 即可開始燒錄,燒錄前會向你要求管理員權限,請同意即可。

燒錄中,燒錄時間根據 SSD 與外接盒讀寫速度而定。

燒錄完畢後會開始驗證是否沒有錯誤,檢查完畢代表燒錄都正常可以直接開機。完成囉!


如何更換Jetson Xavier NX底板的SSD

更換SSD請先記得關閉所有連接的電源,如有靜電手環也可以使用防止靜電。如果裝有底板,請先把底板固定螺絲取下。

接著看到 NX 模組的背面,可以 SSD 有個固定螺絲,把它拆除。

拆裝 SSD 時候請注意插入方向,須對齊如下圖的位置。


安裝新版無線網路卡驅動程式

如是使用以下兩款網路卡,皆須安裝以下驅動才可使用。

  • EDIMAX AC1200 雙頻 長距離USB 3.0無線網路卡
  • ASUS AC1200 USB Nano雙頻無線網卡

請開啟終端機並輸入以下指令

sudo apt install -y dkms git build-essential
git clone https://github.com/morrownr/88x2bu-20210702.git
cd ~/88x2bu-20210702
./ARM64_RPI.sh
sudo ./install-driver.sh
install wifi-dongle driver

 

 

Wio Terminal 搭配 SparkFun 熱影像攝影模組 程式上傳 (也適用於Grove 熱顯像儀)

$
0
0

此篇文章感謝環境工程技師事務所 劉紹淵 技師提供的程式碼,如有需求可進行修改,本文章只說明如何利用Arduino IDE 將程式上傳至Wio Terminal 開發板。

時間 10 分鐘 材料表
  • Wio Terminal 開發板 x1
  • SparkFun 熱影像攝影模組/紅外線陣列 FOV,MLX90640 110度 x1
  • grove轉杜邦接頭4pin線 x1
  • 全套購買連結請點我
成本  個別材料價格如右
難度 1顆星(滿分5)

開始吧!本文將使用 Windows 作業系統來說明並進行相關截圖。

A.下載Arduino IDE,並設定開發板

請先取得Arduino IDE,本文章所使用的版本為Arduino 1.8.19。請根據您所用的作業系統與希望的安裝方式自己下載即可。

開啟Arduino IDE後首先要新增 Wio Terminal 開發板相關定義,步驟如下:

1.點擊檔案->偏好設定

2. 新增開發板定義網址

於偏好設定中的額外的開發板管理員網址處(紅框處)新增

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json


3. 進入開發板管理員

於 Arduino 上方選單中,點擊工具 -> 開發板 -> 開發板管理員

4. 尋找套件並安裝

在搜尋欄(紅框處)輸入wio,就可以在下方找到 Seeed SAMD Boards 的選項並安裝,本篇文章安裝的版本為 1.8.2。注意,先前於偏好設定的網址一定要輸入,否則會找不到對應結果。

5. 設定開發板

上述步驟完成後就能在 工具 -> 開發板 選項中找到 Seeeduino Wio Terminal 與其他系列產品,不要選錯囉。

6. 連接開發板與電腦,確認 COM port 編號

接著使用 USB typeC 傳輸線連接 Wio Terminal 與電腦之後,Wio Terminal 就會自動開機,稍等一段時間等電腦安裝Wio Terminal的驅動程式後,就可以在 工具->序列埠 中找到其COM 編號。或以 Windows 系統來說,也可進入 “裝置管理員” 來檢視。

B. 設定函式庫與程式碼

1. 取得 github

相關程式感謝紹淵提供。點選下方github連結後,點擊右側綠底的CODE字樣,選擇Download ZIP(紅框處)
下載完成後解壓縮:

GitHub – kylecat/GAIA-Irrigation-Level: Sensoring Irrigation Level via MLX900640 Thermal Image Cam

2. 匯入 Arduino IDE

解壓縮完成後,點選 Arduino IDE 的 Library-> libraries 將資料夾中的所有資料夾複製,接著到您的 Arduino IDE 資料夾中找到 libraries 資料夾,並將先前複製的資料夾貼上,完成後將Arduino IDE重新開啟

C.上傳程式到Wio Terminal

1. 開啟範例程式

開啟 Arduino IDE後,點擊左上功能列的 檔案->開啟。找到之前所下載並解壓縮完成的 github 檔案,並依照下列路徑開啟 TestCode_Wio_TFT_MLX90640.ino程式,路徑:

GAIA-Irrigation-Level-main\GAIA-Irrigation-Level-main\Firmware_WioTerminal\TestCode_Wio_TFT_MLX90640\TestCode_Wio_TFT_MLX90640.ino

2. 確認開發板型號與 COM port

選定Seeed Wio Terminal,並選定正確的 COM port (請根據您的電腦實際狀況而定,紅框處)

3. 上傳程式

終於到最後一步驟了,最後只要上傳程式就完成了。先確認Wio Terminal已開機,且開發板和序列埠都選擇正確後,滑鼠點擊 Arduino IDE 左上角的上傳(紅圈處)。此步驟需要一些等待時間,完成後左下會有上傳完畢 (Done Uploading)的字樣,如發生上傳錯誤,通常是 USB 線沒接好或 COM port 跳掉,請重新插拔 USB 線再檢查 COM 後上傳。

D.實際運作

在接感測器前請先把 Wio Terminal 關機。將grove那端插入Wio Terminal左邊,再將杜邦接頭端與熱影像攝影模組的針腳對接,對接的腳位如下 (左 wio terminal / 右 感測器)

  • 黑線->GND
  • 紅線->3.3V
  • 白線->SDA
  • 黃線->SCL

重新開機後就能看到熱影像畫面囉!

 

 

 

 

 

 

 

 

 

ASUS Tinker Board 2S 與 MCU 的無線通訊 – Wi-Fi篇

$
0
0

前言

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

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

(5.X版可達300公尺)

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

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

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

WiFi在無線通訊應用中是具有高頻寬、高速、訊號涵蓋範圍廣的特性,是許多行動裝置中最常使用的通訊技術,也廣泛應用於各種網際網路的訊息傳輸應用,在物聯網的應用裡,WiFi更是扮演著非常重要的角色,透過TCP / IP協定可以實現物聯網對於各種裝置的相互通聯,本篇教學將會透過WiFi、TCP / IP、UDP三種協定服務實作教學,實現Tinker Board 2S與MCU (Linkit-7697)進行網際網路的遠端遙控應用。

Tinker Board 2S透過PCIE-M.2擴充槽安裝通訊模組,其通訊模組包含WiFi與藍牙通訊的功能,通訊模組上也提供了可以外接天線或是與強波裝置連結的端子台,這主要是為了可以強化Tinker Board 2S在進行網際網路應用的彈性,無論是當成Server或是Client都可以具有一定的傳輸效能。

Tinker Board 2S做為嵌入式系統,其安裝的作業系統Linux-Debian也充分支援各種WiFi的協定應用,透過Python程式已Socket的方式進行通訊軟體介面的建立,因此本篇教學文將會著眼在Python程式設計的相關重點說明為主。

Linkit-7697微控器本身內建有WiFi與BLE藍牙通訊的功能,在小巧的微控器晶片裡充分整合了通訊與硬體擴充電路介面的操作,是一種非常適合運用在各種物聯網應用的微控器,此外,Linkit-7697的WiFi開發資源非常完整,可以開發成Server或是Client的各種應用,因此本篇教學將會以Linkit-7697進行WiFi遠端遙控的操作。

撰寫/攝影 曾俊霖
時間 2小時 材料表 ASUS  Tinker Board 2S / 2GB-連結

MCU Linkit-7697

 

難度 中等

 

一、測試電路

本次測試電路是以Linkit-7697控制RGB三色LED模組進行測試,如下圖。

二、Linkit7697測試電路範例程式

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

(1). 設定WiFi通訊SSID與密碼
(2). 起始化並設定UDP通訊連接埠
(3). 接收UDP資料並進行命令格式解碼
(4). 依照命令進行RGB LED的控制
(5). 透過UDP連線回傳資料回應Tinker Board 2S

2. Linkit7697測試程式片段:

#include <LWiFi.h>
#include <WiFiUdp.h>

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

int status = WL_IDLE_STATUS;
char ssid[] = "CAVEDU_02"; //  your network SSID (name)
char pass[] = "12345678";    // your network password (use for WPA / WEP)
int keyIndex = 0;            // your network key Index number (needed only for WEP)

unsigned int localPort = 7890;      // local port to listen on

char Reply_R_ON[] = "Red ON";       // a string to send back
char Reply_R_OFF[] = "Red OFF";       // a string to send back
char Reply_G_ON[] = "Green ON";       // a string to send back
char Reply_G_OFF[] = "Green OFF";       // a string to send back
char Reply_B_ON[] = "Blue ON";       // a string to send back
char Reply_B_OFF[] = "Blue OFF";       // a string to send back

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

WiFiUDP Udp;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  Udp.begin(localPort);
}

void loop() {

  char packetBuffer[255]; //buffer to hold incoming packet

  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remoteIp = Udp.remoteIP();
    Serial.print(remoteIp);
    Serial.print(", port ");
    Serial.println(Udp.remotePort());

    // read the packet into packetBufffer
    int len = Udp.read(packetBuffer, 255);
    if (len > 0) {
      packetBuffer[len] = 0;
    }
    Serial.println("Contents:");
    Serial.println(packetBuffer);

    String value = String(packetBuffer);

    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 a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_G_ON);
        Udp.endPacket();
      }
      if (A[1] == 0) {
        digitalWrite(LED_G, LOW);
        // send a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_G_OFF);
        Udp.endPacket();
      }
      value = "";
    }

    if (SA[0] == "B") {
      if (A[1] == 1) {
        digitalWrite(LED_B, HIGH);
        // send a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_B_ON);
        Udp.endPacket();
      }
      if (A[1] == 0) {
        digitalWrite(LED_B, LOW);
        // send a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_B_OFF);
        Udp.endPacket();
      }
      value = "";
    }

    if (SA[0] == "R") {
      if (A[1] == 1) {
        digitalWrite(LED_R, HIGH);
        // send a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_R_ON);
        Udp.endPacket();
      }
      if (A[1] == 0) {
        digitalWrite(LED_R, LOW);
        // send a reply, to the IP address and port that sent us the packet we received
        Udp.beginPacket(Udp.remoteIP(), localPort);
        Udp.write(Reply_R_OFF);
        Udp.endPacket();
      }
      value = "";
    }
  }
}

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();
  }
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
LinkIt 7697 wifi

這個測試程式主要可以「接收」使用者的命令進行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

 

3. Linkit7697的操作狀態訊息輸出(透過Serial 序列資料輸出訊息)

(a) 初始狀態訊息輸出

 

(b) 接收來自Tinker Board 2S UDP資料並輸出狀態

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

    • 起始化並設定WiFi TCP / IP
    • 設定UDP通訊模式
    • 輸入控制命令
    • 透過UDP發送控制命令至MCU
    • 等候來自MCU的回應訊息
    • 接收MCU的回應訊息
    • 顯示來自MCU的回應訊息

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

import socket
import threading

import inspect
import ctypes

import time
import datetime
import requests

import tkinter
import tkinter.font as tkFont

from PIL import Image
import PIL.Image, PIL.ImageTk

import os
import io

class App:
    def __init__(self, window, window_title):
        self.window = window
        self.window.title(window_title)
        self.window.geometry('800x600')
        self.window.resizable(False, False)

        self.thread_sw = 1

        self.fontStyle = tkFont.Font(size=30)
        self.header_label = tkinter.Label(window, text='網路監控系統',font=self.fontStyle)
        self.header_label.place(x=80, y=0, width=640)

        # 建立套接字
        self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 繫結本地資訊
        self.udp_socket.bind(('', 7890))

        # 建立一個子執行緒用來接收網路資料
        self.t = threading.Thread(target=self.recv_msg, args=(self.udp_socket,))
        self.t.start()

        # 建立網路節點1顯示訊息
        self.fontStyle = tkFont.Font(size=20)
        self.Node_1_Label = tkinter.Label(window,text="網路節點1",padx = 20, font=self.fontStyle)
        self.Node_1_Label.place(x=20, y=150, width=150)

        # 發送起始訊息給本機清空訊息欄位
        self.dest_ip = '127.0.0.1'
        self.dest_port = 7890
        self.msg = ''
        self.udp_socket.sendto(self.msg.encode("utf-8"), (self.dest_ip, self.dest_port))

        # 建立發送位址顯示訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Send_IP_Label = tkinter.Label(window,text="發送位址",padx = 20, font=self.fontStyle)
        self.Send_IP_Label.place(x=20, y=520, width=100)

        # 建立發送位址輸入訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Send_IP_input = tkinter.Entry(window,font=self.fontStyle,fg='blue')
        self.Send_IP_input.place(x=125, y=520, width=200)

        # 建立位址埠號顯示訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Port_Label = tkinter.Label(window,text="位址埠號",padx = 20, font=self.fontStyle)
        self.Port_Label.place(x=330, y=520, width=100)

        # 建立位址埠號輸入訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Port_input = tkinter.Entry(window,font=self.fontStyle,fg='blue')
        self.Port_input.place(x=435, y=520, width=100)

        # 建立發送資訊顯示訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Data_Label = tkinter.Label(window,text="發送資訊",padx = 20, font=self.fontStyle)
        self.Data_Label.place(x=20, y=555, width=100)

        # 建立發送資訊輸入訊息
        self.fontStyle = tkFont.Font(size=16)
        self.Data_input = tkinter.Entry(window,font=self.fontStyle,fg='blue')
        self.Data_input.place(x=125, y=555, width=400)

        # 建立發送資料按鈕
        self.fontStyle = tkFont.Font(size=20)
        self.Send_Button = tkinter.Button(window, text='發送資料', font=self.fontStyle, command=self.Send_Data)
        self.Send_Button.place(x=550, y=525, width=200, height=60)

        # 建立關閉多執行緒按鈕
        self.fontStyle = tkFont.Font(size=20)
        self.Send_Button = tkinter.Button(window, text='關閉執行緒', font=self.fontStyle, command=self.Close_Thread)
        self.Send_Button.place(x=550, y=450, width=200, height=60)

        # After it is called once, the update method will be automatically called every delay milliseconds
        self.delay = 1000
        self.update()

        self.window.mainloop()

    def Send_Data(self): 
        self.dest_ip = self.Send_IP_input.get()
        self.dest_port = int(self.Port_input.get())
        self.msg = self.Data_input.get()
        self.udp_socket.sendto(self.msg.encode("utf-8"), (self.dest_ip, self.dest_port))

    def Close_Thread(self): 
        self.thread_sw = 0
        self.stop_thread(self.t)
        self.udp_socket.shutdown(socket.SHUT_RDWR)
        self.udp_socket.close()

    def update(self):
        """接收網路資料並顯示"""
        # 顯示網路節點1的資料
        self.fontStyle = tkFont.Font(size=20)
        self.Node_1_value_Label = tkinter.Label(
            self.window,
            text = str(self.recv_msg),
            padx = 20, bg="white", fg="red", font=self.fontStyle)
        self.Node_1_value_Label.place(x=20, y=200, width=400)

        self.window.after(self.delay, self.update)

    def recv_msg(self,udp_socket):
        while (self.thread_sw == 1):
            # print('self.thread_sw = ',self.thread_sw)
            print('thread_sw = ',self.thread_sw)
            # 1. 接收資料
            self.recv_msg = udp_socket.recvfrom(1024)
            # 2. 解碼
            self.recv_ip = self.recv_msg[1]
            self.recv_msg = self.recv_msg[0].decode("utf-8")

    def _async_raise(self,tid, exctype):
        """raises the exception, performs cleanup if needed"""
        tid = ctypes.c_long(tid)
        if not inspect.isclass(exctype):
            exctype = type(exctype)
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
        if res == 0:
            raise ValueError("invalid thread id")
        elif res != 1:
            # """if it returns a number greater than one, you're in trouble,
            # and you should call it again with exc=NULL to revert the effect"""
            ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
            raise SystemError("PyThreadState_SetAsyncExc failed")

    def stop_thread(self,thread):
        self.thread_sw = 0
        self._async_raise(thread.ident, SystemExit)

    def __del__(self):
        print('Good Bye!')


# Create a window and pass it to the Application object
App(tkinter.Tk(), "網路監控系統")
tinker GUI app to talk with LinkIt 7697 via wifi

(a) Tinker Board 2S 程式初始狀態

(b) 設定發送IP(如:Linkit7697的IP為192.168.12.139)、設定通訊埠(如:7890)與設定欲發送命令資料(如:B,1,讓LED亮藍燈),按下「發送資料」後,網路節點1(來自Linkit7697的回應訊息)回應「Blue ON」

本篇教學文主要針對Tinker Board 2S以WiFi通訊透過UDP協定進行與MCU的通訊,透過UDP的傳輸,具有資料傳輸效率甚高的特點(因為不需要進行嚴謹的資安檢查),非常適合用來進行高速的資料傳輸,因此,將會於另一篇教學文中,介紹透過UDP協定進行多個計算機系統之間串流影像傳輸,實現遠端的影像串流傳輸,並且透過人工智慧影像辨識的整合,達成網路串流影像辨識的目的,大家就拭目以待吧!

相關文章

 

[新品開箱] NVIDIA® Jetson AGX Orin™ 開發套件開箱及系統安裝

$
0
0

NVIDIA 在今年的 GTC2022 推出了新一代的 Jetson 旗艦機種: NVIDIA® Jetson AGX Orin™ (本文後簡稱 Orin) 號稱把 server 的 AI 運算能力帶到了邊緣端,延續之前的搶先報 [Bring AI to the edge ! NVIDIA Jetson AGX ORIN 邊緣運算開發套件],今天要來看看如何安裝 Orin 的系統喔!

撰寫/攝影 郭俊廷
時間 2小時 材料表
難度 ** (滿分5 *)

NVIDIA® Jetson AGX Orin™介紹

NVIDIA® Jetson AGX Orin™原廠網站的介紹如下:

Jetson AGX Orin 每秒可進行 275 兆次運算,處理能力較前一代 Jetson AGX Xavier 高出八倍以上,並且維持與前一代相同的小巧尺寸及針腳相容性,以及相似的售價。Jetson AGX Orin 搭載 NVIDIA Ampere 架構 GPU、Arm Cortex-A78AE CPU、新一代深度學習和視覺加速器、高速介面、更快的記憶體頻寬及支援多模態感測器,可支援多個同時運作的 AI 應用程式。

CAVEDU的相關介紹可以參考以下文章:https://blog.cavedu.com/2022/03/23/nvidia-jetson-agx-orin/

節錄 Jetson AGX Orin 開發套件規格如下:

 


開箱時間

Jetson AI 大使阿吉老師已經先錄好第一手的開箱影片,如果還沒看的人可以先來看看喔 (居然用鋼彈的盒子來墊,果然是阿吉…):https://www.facebook.com/CAVEEducation/videos/508615087508812

 

開箱最開心啦

來看看 NVIDIA® Jetson AGX Orin™ 開發套件箱子裡面有甚麼東西,首先是箱子本體

打開箱子之後可以看到 Orin 本體

拿開Jetson AGX Orin本體第一層之後可以看到下面一層是說明書以及電源線、變壓器還有一條USB To Type C的線。

電源線跟變壓器如下圖所示,可以看到我們變壓器是使用最大功率20V、4.5A:90W輸出的TYPE C接頭的變壓器。

另外跟Jetson AGX Xavier 一樣附贈一條USB To Type C的線可以用來連接電腦燒錄系統等功能。

Jetson AGX Orin本體

詳細的 Orin 硬體規格請點我,本文就從外觀開始介紹吧,上方看下去可以看到裡面有個大的風扇,面對大功率散熱也是夠用的。

側面方接頭處可以看到由左至右分別是:
DC接頭和Type-C接頭(DFP規格)的電源接頭、RJ45網路孔、兩個USB孔(USB 3.2 Gen 2)、DisplayPort 輸出、Micro USB孔。

請注意:Orin 已經沒有HDMI的螢幕輸出了,只剩下DisplayPort 輸出,所以如果螢幕線材是HDMI的人需要再購買一個DisplayPort轉HDMI的轉接線。

另一面還有一個Type-C接頭是用來燒錄系統用的、還有我們常看到的40 PIN的GPIO、最右邊的還有另外兩個USB孔(USB 3.2 Gen 1),總共四個USB孔解決了之前Jetson AGX Xavier的USB孔位不足的問題。

在側面開關處,可以看到這次開箱的 Orin 是研發專用的非賣品 (收藏收藏~)。中間有三個黑色按鈕,由左至右分別是電源鈕、Force Recovery鈕、Reset鈕。

這是需要另外購買的DisplayPort轉HDMI的轉接線。

系統安裝

接著要介紹如何設定 Orin 的作業系統。請接好電源、鍵盤滑鼠與螢幕(要搭配DP – HDMI轉接線) 之後按下上述的電源鈕就會開機,Orin 在第一次開機之後會開始設定Ubuntu 20.04作業系統。首先要先同意相關條款

接著選擇系統語系,請按照你喜歡的語系來選擇,後續可以隨時修改。

接著選擇鍵盤配置

接著可以連上可用的 Wi-Fi,或直接接上網路線就可以上網了。

選擇自己所在地的時區

接著輸入自己的裝置名稱、使用者帳號密碼,也可以設定開機時自動登入

再來詢問要開啟的功率模式,選擇預設MAXN即可使用最大功率

接著會詢問你要不要安裝Chromium,由於系統已內建 Firefox 瀏覽器,後續有需要另外下載即可。

以上步驟都選擇完畢之後稍微等待一陣子之後就可以進入系統登入畫面,輸入你剛剛建立的帳號密碼即可登入。

登入之後的畫面如下,熟悉的 Ubuntu,桌面上已經有常用的 NVIDIA 資源連結。

接著就要來安裝最重要的 JetPack,根據相關文件,Jetson AGX Orin 需要安裝 JetPack5.0以上才能使用。

根據原廠網站資料,於終端機中執行以下兩個指令就可自動安裝,但本文開箱用的測試版 Orin 會無法找到對應的 jetpack 安裝檔,希望後續出貨的正式版能直接安裝。總之我們有裝好了~

sudo apt dist-upgrade
sudo apt install nvidia-jetpack
install jetpack

 


安裝 JetPack 額外作法

如果無法順利安裝時,請試試看以下作法:

如果執行兩個指令都出現錯誤,需更改以下目錄的套件來源網址:

/etc/apt/sources.list.d/nvidia-l4t-apt-source.list

原本的網址是

deb https://repo.download.nvidia.com/jetson/common r34.0 main

deb https://repo.download.nvidia.com/jetson/t234 r34.0 main

需改成(如下圖紅框處)

deb https://repo.download.nvidia.com/jetson/common r34.1 main

deb https://repo.download.nvidia.com/jetson/t234 r34.1 main

請在終端機中使用 nano 指令來更改檔案內容

sudo nano /etc/apt/sources.list.d/nvidia-l4t-apt-source.list 
edit source

改完之後存檔,重開機,再次執行 JetPack 安裝指令即可。下一篇文章我們將介紹如何將 Jetson AGX Orin移到SSD開機。

參考資料

NVIDIA 原廠網站安裝資料:https://developer.nvidia.com/embedded/learn/get-started-jetson-agx-orin-devkit

[累累累] Google Mediapipe 深蹲偵測,結合 Arduino 首次接觸就上手

$
0
0

Google Mediapipe

 

Mediapipe 姿態偵測結合 Arduino 首次接觸就上手

這次要做一個自虐的專題,偵測深蹲是否到位,其實是檢測大腿與小腿的夾角是否小於指定角度,看看怎樣使用 mediapipe 來做到喔!分成兩個版本:[Mediapipe 結合 Arduino 首次接觸就上手] 以及 [純 Mediapipe (超純)]。兩者差異在於 python 端會根據辨識結果發送訊號給 Arduino 首次接觸就上手,來看影片吧 (大家都要減肥了QQ)

以下是 mediapipe POSE api 的人體關節點定義,可看到兩腿的髖、膝與踝關節分別為 24 26 28 與 23 25 27。

pose_tracking_full_body_landmarks.png

請用以下指令安裝 mediapipe (python)

pip install mediapipe

如果執行本範例程式出現錯誤,請根據本文操作到圖 21。本文執行環境使用 Anaconda (python 3.7)

Python

使用 pose API 偵測兩腿夾角,想挑戰更高難度的話可以把角度調小一點(先不要謝謝)

import cv2
import mediapipe as mp
import numpy as np
import time
import json
import serial

cam = cv2.VideoCapture(0)
mppose = mp.solutions.pose
mpdraw = mp.solutions.drawing_utils
poses = mppose.Pose()
h = 0
w = 0
ser = serial.Serial("COM3", 9600)

start_time = 0
status = False

sport = {
    "name": "Squat",
    "count": 0,
    "calories": 0
}


def logger(count, cals):
    f = open("log.txt", 'a')
    fs = f"{time.ctime()} count: {count} cals: {cals}\n"
    f.write(fs)
    f.close()


def calc_angles(a, b, c):
    a = np.array(a)
    b = np.array(b)
    c = np.array(c)

    radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - \
              np.arctan2(a[1] - b[1], a[0] - b[0])

    angle = np.abs(radians * 180.0 / np.pi)

    if angle > 180:
        angle = 360 - angle

    return angle


def get_landmark(landmarks, part_name):
    return [
        landmarks[mppose.PoseLandmark[part_name].value].x,
        landmarks[mppose.PoseLandmark[part_name].value].y,
        landmarks[mppose.PoseLandmark[part_name].value].z,
    ]


def get_visibility(landmarks):
    if landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].visibility < 0.8 or \
            landmarks[mppose.PoseLandmark["LEFT_HIP"].value].visibility < 0.8:
        return False
    else:
        return True


def get_body_ratio(landmarks):
    r_body = abs(landmarks[mppose.PoseLandmark["RIGHT_SHOULDER"].value].y
                 - landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y)
    l_body = abs(landmarks[mppose.PoseLandmark["LEFT_SHOULDER"].value].y
                 - landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y)
    avg_body = (r_body + l_body) / 2
    r_leg = abs(landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y
                - landmarks[mppose.PoseLandmark["RIGHT_ANKLE"].value].y)
    l_leg = abs(landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y
                - landmarks[mppose.PoseLandmark["LEFT_ANKLE"].value].y)
    if r_leg > l_leg:
        return r_leg / avg_body
    else:
        return l_leg / avg_body


def get_knee_angle(landmarks):
    r_hip = get_landmark(landmarks, "RIGHT_HIP")
    l_hip = get_landmark(landmarks, "LEFT_HIP")

    r_knee = get_landmark(landmarks, "RIGHT_KNEE")
    l_knee = get_landmark(landmarks, "LEFT_KNEE")

    r_ankle = get_landmark(landmarks, "RIGHT_ANKLE")
    l_ankle = get_landmark(landmarks, "LEFT_ANKLE")

    r_angle = calc_angles(r_hip, r_knee, r_ankle)
    l_angle = calc_angles(l_hip, l_knee, l_ankle)

    m_hip = (r_hip + l_hip)
    m_hip = [x / 2 for x in m_hip]
    m_knee = (r_knee + l_knee)
    m_knee = [x / 2 for x in m_knee]
    m_ankle = (r_ankle + l_ankle)
    m_ankle = [x / 2 for x in m_ankle]

    mid_angle = calc_angles(m_hip, m_knee, m_ankle)

    return [r_angle, l_angle, mid_angle]


def main():
    global h, w, start_time, status
    flag = False
    if not cam.isOpened():
        print("Camera not open")
        exit()

    try:
        f = open("sport_recorder.json", "r")
        prevdata = json.load(f)
        if sport['name'] == prevdata['name']:
            sport['count'] = prevdata['count']
            sport['calories'] = prevdata['calories']
            print("Read Success!")
        f.close()
    except:
        print("Read Error...")
        pass

    tmp = f"a{sport['count']}\n"
    ser.write(str.encode(tmp))
    tmp = f"b{sport['calories']}\n"
    ser.write(str.encode(tmp))

    cv2.namedWindow('frame', cv2.WINDOW_FREERATIO)

    while not flag:
        ret, frame = cam.read()
        if not ret:
            print("Read Error")
            break
        frame = cv2.flip(frame, 1)
        rgbframe = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        poseoutput = poses.process(rgbframe)
        h, w, _ = frame.shape
        preview = frame.copy()

        if poseoutput.pose_landmarks:
            mpdraw.draw_landmarks(preview, poseoutput.pose_landmarks, mppose.POSE_CONNECTIONS)
            knee_angles = get_knee_angle(poseoutput.pose_landmarks.landmark)
            body_ratio = get_body_ratio(poseoutput.pose_landmarks.landmark)
            if knee_angles[0] < 120:
                cv2.putText(preview, "Left: Down {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
            elif knee_angles[0] < 130:
                cv2.putText(preview, "Left: ??? {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA
                            )
            else:
                cv2.putText(preview, "Left: Up {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )

            if knee_angles[1] < 120:
                cv2.putText(preview, "Right: Down {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
            elif knee_angles[1] < 130:
                cv2.putText(preview, "Right: ??? {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA
                            )
            else:
                cv2.putText(preview, "Right: Up {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )

            avg_angle = (knee_angles[0] + knee_angles[1]) // 2

            # determine the status
            if status:
                if avg_angle > 160:
                    status = False
                    pass_time = time.time() - start_time
                    start_time = 0
                    if 3000 > pass_time > 3:
                        sport['count'] = sport['count'] + 1
                        sport['calories'] = sport['calories'] + int(0.66 * pass_time)
                        logger(sport['count'], sport['calories'])
                        tmp = f"a{sport['count']}\n"
                        ser.write(str.encode(tmp))
                        tmp = f"b{sport['calories']}\n"
                        ser.write(str.encode(tmp))

            else:
                if avg_angle < 120 and body_ratio < 1.2:
                    start_time = time.time()
                    status = True

            # print(f"status:{status} {start_time}")
            if status:
                cv2.putText(preview, f"{status} : {avg_angle:.1f} {body_ratio:.3f}", (10, 120)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
                if time.time() - start_time > 3:
                    ser.write(b'command_2\n')
                else:
                    ser.write(b'command_1\n')
            else:
                cv2.putText(preview, f"{status} : {avg_angle:.1f} {body_ratio:.3f}", (10, 120)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )
                ser.write(b'command_4\n')
        else:
            ser.write(b'command_4\n')
            start_time = 0

        cv2.imshow('frame', preview)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            flag = True

    f = open("sport_recorder.json", "w+")
    f.write(json.dumps(sport))
    f.close()

    # release camera
    cam.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()
Mediapipe squat detecting with Arduino Uno

Arduino ino

接收來自於PC端的 python程式,並執行對應的LED、蜂鳴器動作

//Arduino首次接觸就上手
//Google Mediapipe - POSE api
//example: https://cavedu.gitbook.io/cavedu/hangeekduino

#define LED_1_pin 4  //板子上有
#define LED_2_pin 9  //需另外接
#define AUDIO_pin 5  //板子上有

int counter = 0;
int calories = 0;

bool stat = false;
bool breaker = false;

String str;

void setup(void)
{
  Serial.begin(9600);

  // init pin states
  pinMode(LED_1_pin, OUTPUT);
  digitalWrite(LED_1_pin, LOW);
  pinMode(LED_2_pin, OUTPUT);
  digitalWrite(LED_2_pin, LOW);
}

void loop(void)
{
  int i;

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

    if (str.startsWith("a")) {
      str.remove(0, 1);
      counter = str.toInt();
    }
    else if (str.startsWith("b")) {
      str.remove(0, 1);
      calories = str.toInt();
    }
    else if (str == "command_1") {
      if (!stat) {
        tone(AUDIO_pin, 110, 100);
        delay(100);
        tone(AUDIO_pin, 165, 100);
      }
      stat = true;
      digitalWrite(LED_1_pin, HIGH);
      digitalWrite(LED_2_pin, LOW);
    }
    else if (str == "command_2") {
      digitalWrite(LED_1_pin, HIGH);
      digitalWrite(LED_2_pin, HIGH);
      if (!breaker) {
        tone(AUDIO_pin, 200, 100);
        delay(100);
        tone(AUDIO_pin, 200, 100);
        breaker = true;
      }
    }
    else if (str == "command_4" ) {
      if (stat) {
        tone(AUDIO_pin, 165, 100);
        delay(100);
        tone(AUDIO_pin, 110, 100);
      }
      stat = false;
      breaker = false;
      digitalWrite(LED_1_pin, LOW);
      digitalWrite(LED_2_pin, LOW);
    }
  }
}
arduino receive command from PC (python serial)

純 Mediapipe 版本

可以看出就是取消 serial 相關的程式碼而已,歡迎您也一起來做做看喔!

import cv2
import mediapipe as mp
import numpy as np
import time
import json
#import serial

cam = cv2.VideoCapture(0)
mppose = mp.solutions.pose
mpdraw = mp.solutions.drawing_utils
poses = mppose.Pose()
h = 0
w = 0
#ser = serial.Serial("COM3", 9600)

start_time = 0
status = False

sport = {
    "name": "Squat",
    "count": 0,
    "calories": 0
}


def logger(count, cals):
    f = open("log.txt", 'a')
    fs = f"{time.ctime()} count: {count} cals: {cals}\n"
    f.write(fs)
    f.close()


def calc_angles(a, b, c):
    a = np.array(a)
    b = np.array(b)
    c = np.array(c)

    radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - \
              np.arctan2(a[1] - b[1], a[0] - b[0])

    angle = np.abs(radians * 180.0 / np.pi)

    if angle > 180:
        angle = 360 - angle

    return angle


def get_landmark(landmarks, part_name):
    return [
        landmarks[mppose.PoseLandmark[part_name].value].x,
        landmarks[mppose.PoseLandmark[part_name].value].y,
        landmarks[mppose.PoseLandmark[part_name].value].z,
    ]


def get_visibility(landmarks):
    if landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].visibility < 0.8 or \
            landmarks[mppose.PoseLandmark["LEFT_HIP"].value].visibility < 0.8:
        return False
    else:
        return True


def get_body_ratio(landmarks):
    r_body = abs(landmarks[mppose.PoseLandmark["RIGHT_SHOULDER"].value].y
                 - landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y)
    l_body = abs(landmarks[mppose.PoseLandmark["LEFT_SHOULDER"].value].y
                 - landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y)
    avg_body = (r_body + l_body) / 2
    r_leg = abs(landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y
                - landmarks[mppose.PoseLandmark["RIGHT_ANKLE"].value].y)
    l_leg = abs(landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y
                - landmarks[mppose.PoseLandmark["LEFT_ANKLE"].value].y)
    if r_leg > l_leg:
        return r_leg / avg_body
    else:
        return l_leg / avg_body


def get_knee_angle(landmarks):
    r_hip = get_landmark(landmarks, "RIGHT_HIP")
    l_hip = get_landmark(landmarks, "LEFT_HIP")

    r_knee = get_landmark(landmarks, "RIGHT_KNEE")
    l_knee = get_landmark(landmarks, "LEFT_KNEE")

    r_ankle = get_landmark(landmarks, "RIGHT_ANKLE")
    l_ankle = get_landmark(landmarks, "LEFT_ANKLE")

    r_angle = calc_angles(r_hip, r_knee, r_ankle)
    l_angle = calc_angles(l_hip, l_knee, l_ankle)

    m_hip = (r_hip + l_hip)
    m_hip = [x / 2 for x in m_hip]
    m_knee = (r_knee + l_knee)
    m_knee = [x / 2 for x in m_knee]
    m_ankle = (r_ankle + l_ankle)
    m_ankle = [x / 2 for x in m_ankle]

    mid_angle = calc_angles(m_hip, m_knee, m_ankle)

    return [r_angle, l_angle, mid_angle]


def main():
    global h, w, start_time, status
    flag = False
    if not cam.isOpened():
        print("Camera not open")
        exit()

    try:
        f = open("sport_recorder.json", "r")
        prevdata = json.load(f)
        if sport['name'] == prevdata['name']:
            sport['count'] = prevdata['count']
            sport['calories'] = prevdata['calories']
            print("Read Success!")
        f.close()
    except:
        print("Read Error...")
        pass

    tmp = f"a{sport['count']}\n"
    #ser.write(str.encode(tmp))
    tmp = f"b{sport['calories']}\n"
    #ser.write(str.encode(tmp))

    cv2.namedWindow('frame', cv2.WINDOW_FREERATIO)

    while not flag:
        ret, frame = cam.read()
        if not ret:
            print("Read Error")
            break
        frame = cv2.flip(frame, 1)
        rgbframe = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        poseoutput = poses.process(rgbframe)
        h, w, _ = frame.shape
        preview = frame.copy()

        if poseoutput.pose_landmarks:
            mpdraw.draw_landmarks(preview, poseoutput.pose_landmarks, mppose.POSE_CONNECTIONS)
            knee_angles = get_knee_angle(poseoutput.pose_landmarks.landmark)
            body_ratio = get_body_ratio(poseoutput.pose_landmarks.landmark)
            if knee_angles[0] < 120:
                cv2.putText(preview, "Left: Down {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
            elif knee_angles[0] < 130:
                cv2.putText(preview, "Left: ??? {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA
                            )
            else:
                cv2.putText(preview, "Left: Up {:.1f}".format(knee_angles[0]), (10, 40)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )

            if knee_angles[1] < 120:
                cv2.putText(preview, "Right: Down {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
            elif knee_angles[1] < 130:
                cv2.putText(preview, "Right: ??? {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA
                            )
            else:
                cv2.putText(preview, "Right: Up {:.1f}".format(knee_angles[1]), (10, 80)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )

            avg_angle = (knee_angles[0] + knee_angles[1]) // 2

            # determine the status
            if status:
                if avg_angle > 160:
                    status = False
                    pass_time = time.time() - start_time
                    start_time = 0
                    if 3000 > pass_time > 3:
                        sport['count'] = sport['count'] + 1
                        sport['calories'] = sport['calories'] + int(0.66 * pass_time)
                        logger(sport['count'], sport['calories'])
                        tmp = f"a{sport['count']}\n"
                        #ser.write(str.encode(tmp))
                        tmp = f"b{sport['calories']}\n"
                        #ser.write(str.encode(tmp))

            else:
                if avg_angle < 120 and body_ratio < 1.2:
                    start_time = time.time()
                    status = True

            # print(f"status:{status} {start_time}")
            if status:
                cv2.putText(preview, f"{status} : {avg_angle:.1f} {body_ratio:.3f}", (10, 120)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA
                            )
                #if time.time() - start_time > 3:
                    #ser.write(b'command_2\n')
                #else:
                    #ser.write(b'command_1\n')
            else:
                cv2.putText(preview, f"{status} : {avg_angle:.1f} {body_ratio:.3f}", (10, 120)
                            , cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA
                            )
                #ser.write(b'command_4\n')
        else:
            #ser.write(b'command_4\n')
            start_time = 0

        cv2.imshow('frame', preview)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            flag = True

    f = open("sport_recorder.json", "w+")
    f.write(json.dumps(sport))
    f.close()

    # release camera
    cam.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()
Mediapipe squat detecting

 

使用RK物聯網教學實驗箱搭配手機建構遠端控制門禁系統

$
0
0

前言

本文發想來源是曾希哲老師的手機遠端控制門禁系統文章,並透過RK物聯網教學實驗箱來實現,RK物聯網教學實驗箱的特色有下列幾點:

  1. 符合108課綱「智慧居家實習」、「介面電路控制實習」。
  2. LinkIt7697開發板由聯發科技推出,搭載了WIFI及藍牙(BLE)模組,可激發使用者針對物聯網情境的應用。
  3. RK物聯網擴充板(RK IOT EX Shield),有Grove防呆接頭,可輕鬆接上感測器不費力,不必苦惱腳位接錯。
  4. 圖控介面的程式操作,圖形化程式介面取代文字介面撰寫,可以大大降低專題開發的難度,再也不用因為拼錯字所造成的編譯錯誤而傷腦筋。後續也可銜接到正式的文字程式環境。
撰寫/攝影 許鈺莨
   
時間 2小時 材料表
   
難度 中等

本實驗箱是依照108課綱所配置,其主題有「工場安全衛生及介面電路控制應用」、「通用序列匯流排介面」、「數位類比轉換介面」、「環境感測介面」、「辨識介面」、「藍牙無線傳輸介面」、「綜合應用」,共七個章節。本篇門禁系統就是屬於「藍牙無線傳輸介面」的延伸,以下就針對遠端控制門禁系統的範例來做說明。

流程圖說明

筆者是透過曾希哲老師公開的程式碼,可讓手機端藉由 BLE 藍牙來與 LinkIt 7697 開發板互動,請由此下載 iOS / Android 版本的 LinkIt Remote app,只要編寫 7697 端程式就能生成手機 app 介面,功能雖然簡單了點但已足以完成各種監控介面。請參考相關文章:https://blog.cavedu.com/?s=linkit%20remote

輸入密碼有三次機會,三次機會全錯會有五秒鐘的等待時間,正確密碼為 9527 ,而遠端控制門禁系統操作流程,如圖所示。

硬體介紹

再來介紹所使用的硬體,如下圖所示。本篇是利用LinkIt7697的LinkIt Remote功能與手機連線,並在擴充板接上繼電器來控制電磁鎖,但需注意的是控制電磁鎖需要額外的外接電源,所以筆者是用四顆 1.5V 的四號電池來當外接電源。

程式撰寫

本篇程式是透過 BlocklyDuino圖形化介面來撰寫,而以下 Arduino程式碼是由 BlocklyDuino圖形化介面轉換而來,若讀者希望取得本BlocklyDuino程式碼,請由此下載

也請參考我們所編寫的 BlocklyDuino  + LinkIt 7697 相關範例,很完整喔!

#include <LRemote.h>

int time;

int SetPasswd;

int PhonePasswd;

int c;

String S1;

String S2;

String S3;

String S4;

int count;

LRemoteButton button0;
LRemoteLabel label1;
LRemoteButton button1;
LRemoteButton button2;
LRemoteButton button3;
LRemoteButton button4;
LRemoteButton button5;
LRemoteButton button6;
LRemoteButton button7;
LRemoteButton button8;
LRemoteButton button9;
LRemoteButton button10;

void setup()
{
  LRemote.setName("remote");
  LRemote.setOrientation(RC_PORTRAIT);
  LRemote.setGrid(3, 5);
    button0.setPos(0, 4);
    button0.setText("0");
    button0.setSize(1, 1);
    button0.setColor(RC_ORANGE);
    LRemote.addControl(button0);

    label1.setPos(0, 0);
    label1.setText("輸入密碼");
    label1.setSize(3, 1);
    label1.setColor(RC_BLUE);
    LRemote.addControl(label1);

    button1.setPos(0, 3);
    button1.setText("1");
    button1.setSize(1, 1);
    button1.setColor(RC_ORANGE);
    LRemote.addControl(button1);

    button2.setPos(1, 3);
    button2.setText("2");
    button2.setSize(1, 1);
    button2.setColor(RC_ORANGE);
    LRemote.addControl(button2);

    button3.setPos(2, 3);
    button3.setText("3");
    button3.setSize(1, 1);
    button3.setColor(RC_ORANGE);
    LRemote.addControl(button3);

    button4.setPos(0, 2);
    button4.setText("4");
    button4.setSize(1, 1);
    button4.setColor(RC_ORANGE);
    LRemote.addControl(button4);

    button5.setPos(1, 2);
    button5.setText("5");
    button5.setSize(1, 1);
    button5.setColor(RC_ORANGE);
    LRemote.addControl(button5);

    button6.setPos(2, 2);
    button6.setText("6");
    button6.setSize(1, 1);
    button6.setColor(RC_ORANGE);
    LRemote.addControl(button6);

    button7.setPos(0, 1);
    button7.setText("7");
    button7.setSize(1, 1);
    button7.setColor(RC_ORANGE);
    LRemote.addControl(button7);

    button8.setPos(1, 1);
    button8.setText("8");
    button8.setSize(1, 1);
    button8.setColor(RC_ORANGE);
    LRemote.addControl(button8);

    button9.setPos(2, 1);
    button9.setText("9");
    button9.setSize(1, 1);
    button9.setColor(RC_ORANGE);
    LRemote.addControl(button9);

    button10.setPos(1, 4);
    button10.setText("Clear");
    button10.setSize(2, 1);
    button10.setColor(RC_GREEN);
    LRemote.addControl(button10);
  LRemote.begin();
  SetPasswd = 10;
  PhonePasswd = 0;
  c = 0;
  S1 = "*";
  S2 = "**";
  S3 = "***";
  S4 = "****";
  count = 3;
  pinMode(5, OUTPUT);
}


void loop()
{
  time = 5;
  LRemote.process();
  if (button9.isValueChanged()) {
    if (button9.getValue() == 1) {
      c = c + 1;
      if (c == 1) {
        PhonePasswd = PhonePasswd + 1;

      }

    }

  } else if (button5.isValueChanged()) {
    if (button5.getValue() == 1) {
      c = c + 1;
      if (c == 2) {
        PhonePasswd = PhonePasswd + 2;

      }

    }
  } else if (button2.isValueChanged()) {
    if (button2.getValue() == 1) {
      c = c + 1;
      if (c == 3) {
        PhonePasswd = PhonePasswd + 3;

      }

    }
  } else if (button7.isValueChanged()) {
    if (button7.getValue() == 1) {
      c = c + 1;
      if (c == 4) {
        PhonePasswd = PhonePasswd + 4;

      }

    }
  } else if (button4.isValueChanged()) {
    if (button4.getValue() == 1) {
      c = c + 1;

    }
  } else if (button0.isValueChanged()) {
    if (button0.getValue() == 1) {
      c = c + 1;

    }
  } else if (button6.isValueChanged()) {
    if (button6.getValue() == 1) {
      c = c + 1;

    }
  } else if (button1.isValueChanged()) {
    if (button1.getValue() == 1) {
      c = c + 1;

    }
  } else if (button8.isValueChanged()) {
    if (button8.getValue() == 1) {
      c = c + 1;

    }
  } else if (button3.isValueChanged()) {
    if (button3.getValue() == 1) {
      c = c + 1;

    }
  } else if (button10.isValueChanged()) {
    if (button10.getValue() == 1) {
      label1.updateText(String("輸入密碼"));
      c = 0;
      PhonePasswd = 0;

    }
  }
  if (c == 1) {
    label1.updateText(String(S1));

  } else if (c == 2) {
    label1.updateText(String(S2));
  } else if (c == 3) {
    label1.updateText(String(S3));
  } else if (c == 4) {
    label1.updateText(String(S4));
    delay(500);
    if (SetPasswd == PhonePasswd) {
      label1.updateText(String("開門"));
      digitalWrite(5, HIGH);
      delay(5000);
      digitalWrite(5, LOW);

    } else {
      label1.updateText(String("密碼錯誤"));
      delay(1000);
      count = count - 1;
      label1.updateText(String(String() + "還有" + count + "次"));
      if (count == 0) {
        count = 3;
        label1.updateText(String("五秒後輸入"));
        delay(1000);
        for (int count2 = 0; count2 < 5; count2++) {
          time = time - 1;
          label1.updateText(String(String() + "剩餘" + time + "秒"));
          delay(1000);
        }

      }
      delay(2000);

    }
    label1.updateText(String("輸入密碼"));
    c = 0;
    PhonePasswd = 0;
  }
  delay(100);
}
RK iot box remote control

影片展示

最後,來看看RK物聯網教學實驗箱門禁系統影片的操作說明吧!

本實驗箱已備有使用手冊,及多種範例實際應用操作,各位讀者及大專院校老師們欲購買RK物聯網教學實驗箱,請點選以下連結,我們會有專人為您服務:
https://robotkingdom.com.tw/product/rk_iotedubox/

本篇主題到此告一段落,下次再見囉!

reComputer J1020(安裝 Jetson Nano運算模組)使用教學以及注意事項

$
0
0

前言

NVIDIA 原廠的 Jetson nano developer kit 最近真的很不好買,不過市面上有許多使用 Jetson Nano module 搭配自家 carrier board 的相容套件,本文要為您介紹 Seeedstudio 推出的 reComputer Jetson 系列(機器人王國商城連結請點我),以下是相關介紹。

撰寫/攝影 郭俊廷
   
時間 1小時 材料表
難度 2 (滿分5)

 


這次要介紹的是reComputer Jetson-10-1-H0(安裝Jetson Nano運算模組),reComputer Jetson系列有分為安裝Jetson Nano與Jetson Xavier NX 運算模組。

reComputer Jetson-10-1-H0 (安裝Jetson Nano運算模組) 與 NVIDIA 原廠的 Jetson Nano developer kit最大的不同就是移除了記憶卡插槽,並新增內建的16 GB 的eMMC。在系統方面則是預裝了 Ubuntu 18.04 LTS 和NVIDIA JetPack 4.6,另外還可以安裝NVMe M.2 SSD 固態硬碟擴充儲存空間。reComputer系列購物連結:
https://robotkingdom.com.tw/product-category/board/seeed/recomputer-jetson/

以下是 Seeedstudio原廠網站reComputer的介紹:

reComputer Jetson 系列是採用NVIDIA AI 嵌入式系統所打造的小型邊緣運算裝置,目前推出了Jetson-10 (使用Jetson Nano為核心板) 和Jetson-20(使用Xavier NX為核心)2個系列。憑藉豐富的擴充模組、工業級周邊設備、熱管理以及 Seeed 數十年的硬體設計製造經驗,reComputer Jetson 已準備好幫助您加速和擴充在各種 AI 場景中出現的下一代 AI 產品。
本系列產品相容於 NVIDIA Jetson 軟體堆疊、原生雲工作流程以及最頂尖的 AI 框架,有助於實現無縫整合各種 AI 應用。

 

reComputer Jetson 系列產品均內建16 GB 的eMMC,並已預先安裝了 Ubuntu 18.04 LTS 和NVIDIA JetPack 4.6,開機簡單配置後即可開始使用。目前已有4款不同型號的reComputer Jetson,機殼外觀都一樣,差異在背部接口。

接下來介紹Jetson A206 載板,之前我們文章介紹過的TX2 NX以及NX都是使用此A206 載板。
此載板用於:reComputer Jetson-10-1-H0reComputer Jetson-20-1-H1reComputer Jetson-20-1-H2機型。

Jetson A206 載板各接頭的對應說明如以下表格:

編號 名稱 說明
1 DC輸入(圓形接頭) 供電用
2 DisplayPort接頭輸出 接螢幕用
3 HDMI接頭輸出 接螢幕用
4 4x USB 3.0 Type-A接頭 接相關周邊設備用
5 RJ45接頭 接網路線用
6 Micro-USB輸出 跟電腦傳輸數據用

請注意,A206 載板無法使用原本Jetson Nano的5V 4A變壓器,會無法供電,需使用對應的變壓器。H0附贈的變壓器是12V/2電源(有5個可替換插頭搭配不同規格插座)

H0附贈的變壓器

 

軟體設定教學

如果是跟 CAVEDU 購買已安裝好的reComputer Jetson-10-1-H0 (安裝 Jetson Nano運算模組)套組,以下步驟可以跳過不用做,直接開機即可使用我們幫您移動到SSD裡的系統,也已經預設安裝好一些常用的套件及程式。是給購買者的小小福利喔!
帳號及密碼皆是 jetson(都小寫)

開機與建立帳號

我們的開箱影片有介紹如何開機並且建立帳號等相關資訊,如果是要從建立帳號開始觀看可從6分01秒的時間開始觀看

建立完帳號之後由於 reComputer Jetson內建的16 GB 的 eMMC,已經安装了 ubuntu 18.04 LTS 和 NVIDIA JetPack 4.6 ,所以剩餘的使用空間大約只剩2GB左右,這對使用者要來安裝其他套件以及軟體來說空間不足,所以需要透過安裝SSD來擴充儲存空間,並把系統移動到SSD讓系統更快。

將reComputer Jetson-10-1-H0「系統」從eMMC轉移至SSD固態硬碟

Step 1. 搜尋SSD

請在「開始」按搜尋,並尋找「disks」關鍵字,點選 Disks 圖示,如果您已將系統語系改為正體中文,則為下圖中的 「磁碟」。

Step 2. 格式化SSD

點擊「磁碟」,會看到以下畫面,點選你的SSD,如本文中為Samsung的500GB Disk:

點選右上角的三條橫槓,選擇格式化(Format Disk),選擇預設的GPT,再次確認要格式化(Format),接著輸入使用者密碼,授權進行格式化:

Format格式選擇GPT

Step 3. 建立分割

點選下圖加號來建立新的磁碟分割( partition)

本文中建立的新分區容量遵循網頁建議為500GB,大家可以根據自身需求做調整。接下來幫該分區取名稱命名為NANO_SSD(或是你自己想取的SSD名稱),類型要選擇Ext4(Fourth EXTended filesystem)。

格式化完成之後如下圖

Step 4. 下載rootOnNVMe檔案,複製eMMC內容到SSD開機

下載JetsonHacks Github上的檔案

git clone https://github.com/jetsonhacks/rootOnNVMe

進入rootOnNVMe資料夾

cd rootOnNVMe

接著將EMMC的內容複製到SSD

./copy-rootfs-ssd.sh

最後,新增一個服務在系統啟動時運行腳本。該腳本會將root移至SSD,讓系統在SSD運行。

./setup-service.sh

重新開機後會看到 eMMC 已變成一個磁碟圖示(如下圖),代表系統已經把它視為一個磁碟。

更多的詳細資料及介紹可參考以下原廠網站(相關資料及圖片引用自以下網站):
https://wiki.seeedstudio.com/reComputer_getting_started/

相關文章

 

 

 

 

 

 


華碩Tinker Board 2嵌入式系統新品,火熱開箱!

$
0
0

前言

Tinker Board 2(或2S)是一款Arm-based 單板電腦 (SBC),採用 Arm big.LITTLE™ 技術 64 位元處理器,可提供更優異的運算效能且耗電量低。在原廠網站對於Tinker Board 2(或2S)的介紹裡,可以清楚看出Tinker Board 2(或2S)相較前代產品Tinker Board S有著更為新穎與強大的CPU與GPU的架構,此外,更標榜著可透過強大的工具程式(如:FOTA),讓使用者可以輕易透過網路線上更新系統韌體,從這裡在在都可以看出Tinker Board 2(或2S),都著眼朝著人工智慧物聯網AIoT的方向發展。

 華碩AIoT Tinker Board 2S和Tinker Board 2在硬體架構上基本上都是相同的,主要的差異在於Tinker Board 2S比Tinker Board 2多了16GB eMMC記憶體,相關Tinker Board 2S教學文章,都已經發表在CAVEDU技術部落格中,請參考以下連結:https://blog.cavedu.com/tag/tinker-board-2s/

撰寫/攝影 曾俊霖
 
時間 1小時 材料表
難度 1 (滿分5)

從包裝外盒取出物品,其實主要可以分成3個主要零組件,分別是單板微電腦、處理器散熱片與Wi-Fi天線,這個散熱片還挺高的…

本次開箱的單板微電腦就外觀來看,Tinker Board 2與Tinker Board 2S這兩個版本的各種外接界面規劃其實沒有差異,這兩個版本主要是差異在2S版是有內建16GB的eMMC內部儲存空間,而2版是沒有內建eMMC儲存空間,除此之外,這兩個版本在其他的硬體規格是一模一樣的。相關軟硬體操作手冊請參考下載連結:https://tinker-board.asus.com/tw/doc_tb2.html#user

真心建議:教學或是測試階段,比較適合使用Tinker board 2,實際部署時,使用有eMMC的Tinker board 2S,比較能承受頻繁的資料讀寫

Tinker Board 2硬體規格列表

項目 型號
SoC Rockchip RK3399
CPU Dual-core Arm® Cortex®-A72 @ 2.0 GHz
Quad-core Arm® Cortex®-A53 @ 1.5 GHz
GPU Arm® Mali™-T860 MP4 GPU @ 800 MHz
Display 1 x HDMI™ with CEC hardware ready
1 x USB Type-C® (DP Alt Mode)
1 x 22-pin MIPI DSI (4 lane)
Memory Size Dual-CH LPDDR4 2GB / 4GB
Storage Micro SD(TF) card slot (push/pull)

Tinker Board 2僅能使用這個儲存方式

Connectivity 1 x RTL8211F-CG GbE LAN
1 x M.2 – 802.11 a/b/g/n/ac wireless & BT 5.0 (2T2R)
Audio 1 x HDMI™ audio output
1 x S/PDIF TX pin (from GPIO)
1 x PCM/I2S pins (from GPIO)
USB 3 x USB 3.2 Gen1 Type-A ports
1 x USB 3.2 Gen1 Type-C® OTG port
Camera Interface 1 x 15-pin MIPI CSI-2 (2 lane)
Internal Headers 1 x 40-pin headers includes:
– up to 28 x GPIO pins
– up to 2 x SPI bus
– up to 2 x I2C bus
– up to 2 x UART
– up to 3 x PWM
– up to 1 x PCM/I2S
– up to 1 x S/PDIF TX
– 2 x 5V power pins
– 2 x 3.3V power pins
– 8 x ground pins
1 x 2-pin Recovery header
1 x 2-pin Power-on header
1 x 2-pin Reset header
1 x 2-pin Debug UART header
1 x 2-pin DC Fan header
1 x 2-pin RTC Battery header
Power Connector
(up to 65W)
1 x 12~19V DC Power Input Jack (5.5/2.5 mm)
OS Support Debian 10 / Android 10
Dimension 3.37 inch x 2.125 inch (85 x 56 mm)

Tinker Board 2 硬體周邊介紹

Tinker Board 2 在硬體周邊介面方面和Tinker Board 2S是完全一樣,相關的教學說明請參考以下連結:【開箱】華碩Tinker Board 2S 嵌入式系統開箱介紹

比較Tinker Board  2與Tinker Board 2S的差異

Tinker Board 2與Tinker Board 2S 在硬體功能設定上的差異

按照原廠手冊第15頁的說明可知,Tinker Board 2S的16GB eMMC主要用於復原系統(Recovery)的用途,在一般的狀況還是處於關閉的狀態,因此Tinker Board 2由於不使用eMMC,因此跳線J3只要直接開路即可。

相關系統軟體安裝

Tinker Board 2和Tinker Board 2S若都使用SD記憶卡進行系統軟體安裝的話,基本上都是採用相同的安裝流程,只要確認相關eMMC的設定正確,便可以通用SD記憶卡。

作業系統軟體安裝流程,為您整理如下:

收集資料、訓練模型與部署模型一次完成:阿吉老師看 Jetson AGX ORIN 平台

$
0
0

NVIDIA 在 GTC2022 發布了 Jetson 系列王者:NVIDIA Jetson AGX ORIN 邊緣運算開發套件之後,就CAVEDU來看,對於邊緣運算裝置的日常:收集資料、訓練模型與部署模型這個流程上,是相當不錯的解方。Jetson Nano 這個等級的邊緣裝置為了平衡價格、算力,還要能做成一台可由行動電源供電的機器人,自然在許多地方受限了,就讓老大哥 ORIN 來幫忙吧!

新品開箱] NVIDIA® Jetson AGX Orin™ 開發套件開箱及系統安裝– CAVEDU教育團隊技術部落格
ORIN開箱,阿吉不知道樂什麼
借助NVIDIA Jetson Nano,16 歲高中生一舉奪下機器人競賽冠軍- NVIDIA 台灣官方部落格
收集道路資料中的 Jetbot

一如當年 Jetson nano 發布之後,我們就覺得這套產品在規格、價格、生態系與社群支援(資源)上都拿捏了一個很好的平衡點,也確實造成了一定的熱度。 CAVEDU 除了多位講師取得 Jetson AI 專家的資格,我也取得了 Jetson AI 大使並榮獲白金獎(推廣人數全球第一!)的肯定喔

我(阿吉)常常和學生說,時間也是成本的一種。當然,學生比較難做到課金升級這件事,但就算是 Google Colab 免費版,也是會遇到記憶體、使用時間與可用算力等限制。試想一下情境:

教學現場一直以來低估一件事:軟體環境架設的一致性很難達成與維護。現在遠距教學更加普及,老師不再只須要管好一間電腦教室,還要"隔空"處理學生的電腦。但學生對於環境架設如果卡住,通常也都是兩手一攤,如果有一個server可以又快又好地處理神經網路模型訓練,該有多好?

速度很重要,然後呢?

根據[Bring AI to the edge ! NVIDIA Jetson AGX ORIN 邊緣運算開發套件]一文說明,Jetson Orin 的規格當然要配得上它的售價,但如果只看規格好像又不夠,就讓我們用 [[活動紀錄] 2021 RK-Jetbot機器學習道路識別競賽-中區邀請賽] 這個情境來看,學生需要反覆進行以下步驟:

  • 操作 Jetbot (Jetson nano 為核心的機器人) 來收集道路資料
  • 整理好道路影像,動輒數百甚至上千張彩色圖片
  • 回傳到 notebook 進行訓練神經網路模型 (這裡最耗時間)
  • 把神經網路模型放回到 Jetbot 上來驗證辨識效果並調整機器人參數 (反覆操作)

用資料來佐證!

由下表可以看到相同資料集、訓練20回合數的前提下,不同 Jetson 平台的訓練時間,Jetson AGX Orin 的訓練時間是 28秒 !如果是在同一個教室中的話,Jetbot 收集到的資料可以透過區域網路,或USB隨身碟來傳輸,而 Orin 訓練好的神經網路模型也可以相同的方式送回 Jetbot。

經實測可以節省大量時間,開發環境也單純多了!

在 Jetson nano 上原場考照(訓練神經網路模型)可以嗎?可以,但時間非常可觀(800秒/20回合),另外還要注意 Jetson nano 在訓練的過程中,系統會因為耗用大量資源而近乎當機…

 

教學現場考量

就CAVEDU長期規畫各種軟硬體整合的課程的角度,課程能否成功就在於以下四個項目可否平衡。對許多先進來說,這當然是老生常談,但在此就再強調一次。

歡迎洽詢體驗

如果對於 orin 的威力想要體驗一下的話,歡迎參考相關文章,我們對於 orin 配置於教學現場來提升邊緣預算課程的順暢度下了很多功夫,不論是軟體硬體整合與課程規劃上,都有很多實務經驗,歡迎多多洽詢喔!

相關文章

 

2022 RK-Jetbot機器學習道路識別競賽-彰化邀請賽@鹿港高中

$
0
0

前言

RK-Jetbot機器學習道路辨別競賽首次登場是在 2020 年的台北市教育博覽會,之後在后綜高中舉行了2021年的中區邀請賽。今年2022,在諸多考驗之下,我們順利在鹿港高中舉辦了第三場的比賽。


活動精彩紀錄

首先,感謝 NVIDIA Taiwan 為本次「2022 RK-Jetbot機器學習道路識別競賽-彰化邀請賽」提供豐富的獎品與贊助。第一名獎品是學生所期待的【RTX-3060】顯示卡一張 (流口水啊)。還要感謝地主鹿港高中跟參加比賽的彰化女中、彰化高中師生,這次活動感謝各位的熱情參與與協助才能順利完成喔!

在活動前,先由本團隊曾俊霖老師對各校的參賽師生進行兩次集訓,接著在2022年6月12號進行RK-Jetbot機器學習道路識別競賽-中區邀請賽競賽。先來看看比賽場地:

 

CAVEDU所設計的RK-Jetbot自駕車以及比賽場地
CAVEDU所設計的RK-Jetbot自駕車以及比賽場地

 

第一次課程為基礎的Jetbot操作,說明如何控制車子的動作,也學習如何搜集賽道的影像資料、資料整理並訓練出一個神經網路模型來機器人執行對應的動作。

第二次課程時,有些隊伍已有不錯的訓練成果,有些選手已經可以用很快的速度建立自動駕駛模型,順利跑完整圈賽道。

彰化女中老師賽前採訪

彰化女中老師表示:女生比較會通盤的考量,並細心整理出所有可能會遇到的狀況,男生則是遇到問題才會考慮要如何解決 (阿吉老師點頭贊同)。這點也顯現在比賽上,男生遇到突發狀況會比較混亂,彰化女中的同學們在這次也斬獲佳績,歡迎大家之後多多參加我們的活動喔!

課程中會讓學生設計行車路線,嘗試分析該賽道的最佳路線;接著透過協作的方式,一人執行Python程式,再由另一人移動自駕車來收集賽道資料並設定標籤,等同於告訴神經網路模型看到類似的圖像所要執行的動作:左轉、右轉、幅度與速度;接著進行神經網路模型的訓練,學生可調整訓練參數 (回合數、神經元數量、dropout rate 與 learning rate 等等) 來達到更好的神經網路模型;最後進行測試並且反覆調整參數來達到最佳的狀態。透過實際操作體驗與反覆操作,同學們對於這些參數的掌握度明顯更好。

比賽當天大家都開始備戰,專心聽曾老師講解比賽賽程,以及進行Jetbot相關檢測及維修。

如果車子有突發狀況,我們工作人員馬上讓機器人進維修站,有專業維修員(一條龍)為大家檢查並維修。

這次來參加的學生有來自鹿港高中、彰化女中、彰化高中三間學校的學生,總共分成6隊參加,學生們雖然事先已經在各自學校的場地來收集資料自行練習。但是正式比賽現場環境畢竟還是不太一樣,包括場地架高、使用隔板、燈光等其他因素都會影響到神經網路的推論結果,當然也會連帶影響到機器人的成績。

使用 Jetson ORIN 加速神經網路模型訓練

時間不等人,多練習一次就多穩一次,為了讓大家有機會多練習,本次比賽我們出了最新的邊緣運算王者:Jetson AGX Orin™ 來加速訓練模型。大家看到訓練時間縮短那麼多~ 都很驚訝呢。Jetson AGX Orin™相關資訊可參考以下文章:https://blog.cavedu.com/2022/06/14/jetson-orin-education/

桌上的神祕灰色金屬盒子就是 Jetson ORIN!

活動紀錄

本次賽制規則,五分鐘內跑五圈賽道,取其中最快圈速為最終成績。出界就會增加懲罰秒數,為了讓機器人更穩更好,大家上午都很專心的收集資料。

比賽前先來個大合照舒緩緊張的氣氛,同學拿著顯卡拍照也很高興呢。

 

比賽成績

   這次比賽有三組無法完整跑完整圈,但前3名的隊伍完賽時間相差只有一兩秒而已,比賽過程相當刺激緊張。沒跑完的原因,大部分是因為偏離原本的路線就回不來了,希望之後參賽的選手可以考慮到:車子如何避免跑到錯誤的路線,以及如何回到原本的賽道等等補救策略。

第一名:鹿港高中 12.8秒 – 一夫當關

第一名是鹿港高中的選手,在隊友因為某些因素無法參加情況下,一個人勇奪第一名,獲得 NVIDIA提供的RTX-3060顯示卡一張,厲害!

採訪時,這位同學表示他是第一次參加AI相關的比賽。自己練習時,機器人的成績都在一圈15秒左右,但到了正式比賽時覺得試跑很順,就把車速調整到最高,並且調整其他PID的參數,一舉突破到12秒的成績自己也很驚訝。

第二名:彰化女中 14.1秒 – 強運積極

這一隊其中一位同學在賽前訪問時提到,想在高中生活參加活動留下紀錄,但找了很久都沒人要一起參加… 最後終於找到了一位隊友。另外一位同學則表示,國中時有看過自駕車的相關資訊覺得很有趣,看到活動訊息就趕快報名參加,過程中也覺得很好玩。

賽後訪問時,本組表示在賽前練習時沒有真正測試車子跑,卻得到了第二名(!?),覺得很幸運。好吧,雖然說運氣也是實力的一種,但還是要把自己能做的做好會更安心喔。最後本組提到,它們有寫 email 給講師詢問相關問題,這種積極解決問題的精神,值得其他組參考。

第三名:彰化女中 14.9秒 – 詳實記錄

第三名隊伍說,練習時有把賽道上遇到的情況先記錄下來,如果發現車子超出賽道或是走錯方向都先記錄起來,並調整相關參數。正式比賽時就有參考到之前紀錄的數值。另一位講到他們有很清楚的分工,每個人都各自分配到的任務,只要把相關資料與參數都好好記錄,最後正式比賽就有機會順利完賽。

在練習過程中,彰化高中曾經創下10秒的最佳賽道紀錄,可惜在比賽時未能發揮應有水準,代表機器人的穩定度、現場的資料搜集以及參數調整也是很重要的。

大合照!大家都換上NVIDIA贊助的衣服,有發現小巧思嗎?標語不管從左邊或右邊讀,都是 i am ai 呢!

 

有興趣的單位,歡迎跟我們聯絡,很樂意到   貴校/單位辦理邀請賽,之後也希望有機會可以舉辦大決賽,讓之前飲恨的參賽隊伍可以再次挑戰。本次活動影片剪輯完成之後就會與大家分享,先來看看2021年后綜高中場次的活動花絮吧~

RK-reComputer J1010 (安裝Jetson Nano運算模組)系統安裝及移動到USB開機系統教學

$
0
0

前言

延續前一篇[reComputer J1020(安裝 Jetson Nano運算模組)使用教學以及注意事項],本文將接續介紹 reComputer 系列中相對於 Jetson Nano 的 J1010,說明如何安裝系統與透過 USB 隨身碟來開機。

撰寫/攝影 郭俊廷
   
時間 1小時 材料表
難度 2 (滿分5)

 


這次要介紹的 reComputer J1010(安裝Jetson Nano運算模組,後簡稱 J1010) 的系統安裝以及移動到USB開機系統教學,上次介紹的 reComputer J1020 有包含NVMe M.2 SSD 固態硬碟擴充槽位置,而 J1010則不包含 SSD 固態硬碟擴充槽的,所以內建的16 GB 的eMMC在系統預裝了 Ubuntu 18.04 LTS 和NVIDIA JetPack 4.6之後空間就不到 2 GB了。如需安裝其它套件必須將系統移動到USB或記憶卡等其他儲存空間。

reComputer J1010(安裝Jetson Nano運算模組)在系統方面一樣預裝了 Ubuntu 18.04 LTS 和NVIDIA JetPack 4.6,如購買安裝好CAVEDU系統及套件的,不用建立相關帳號與設定即可使用,如是購買沒有安裝好系統的需要建立帳號等相關資訊可以參考以下影片可從6分01秒的時間開始觀看:

 

reComputer J1010的各個接頭圖所對應的表格如下:

編號 名稱 說明
1 DC輸入(Type-C接頭) 僅做為供電用
2 HDMI接頭輸出 接螢幕用
3 1個USB 3.0 Type-A接頭 接相關周邊設備用
4 2個USB 2.0 Type-A 接相關周邊設備用
5 RJ45接頭 接網路線用
6 USB Type-C 接頭 僅做為電腦傳輸數據用

PART A:使用NVIDIA SDK Manager 燒錄系統

首先來使用NVIDIA SDK Manager燒錄系統 (在此的作法與 J1020 相同),NVIDIA SDK Manager安裝方法請參考以下兩篇文章。筆者目前測試NVIDIA SDK Manager 1.8安裝JetPack 5.0以下版本皆須使用Ubuntu 18.04 LTS 中進行,Ubuntu 20只能安裝JetPack 5.0以上的版本,建議使用Ubuntu 18.04 LTS來安裝

NVIDIA SDK Manager網站及安裝方法可以參考以下兩個網站:

  • https://developer.nvidia.com/nvidia-sdk-manager
  • https://blog.cavedu.com/2022/03/26/nvidia-jetson-tx2-nx-setup-ssd/

STEP 1

先將Jetson Nano傳輸數據用的接頭接上安裝好NVIDIA SDK Manager的電腦,並且將HDMI接頭輸出接上對應的螢幕,最後接上鍵盤與滑鼠。

接著開啟NVIDIA SDK Manager,取消勾選Host Machine。在 Target Hardware 區塊中點選 refresh 後會自動顯示 Jetson Nano (J1010 就是使用  Jetson Nano 模組喔!),沒自動選擇後在如下圖選擇第一個Jetson Nano)。

在此要直接安裝系統,我們一樣安裝reComputer預設的JetPack 4.6(目前CAVEDU常用的JETSON相關套件及程式測試在JetPack 4.6皆可正常使用)。
注意:請勿勾選 DeepStream 選項,因為Jetson Nano內建的16G eMMC空間不夠)即可點選右下角CONTINUE進入下一步。

STEP 2

選擇要下載的項目,本文使用,預設選項也就是全部下載安裝。請記得勾選下方的 accept license 選項,如果要馬上安裝,則不要勾選 Download now. Install later選項,如果想先下載之後再安裝,則可勾選該選項。

按下 CONTINUE 會要求輸入你的 Ubuntu 系統密碼,輸入之後按下OK就會進入下一步。


STEP 3

開始安裝系統之前,請選擇自動設定 Automatic Setup - Jetson Nano,在此同樣需要輸入帳號密碼才可進行安裝。

05

以下安裝過程的畫面,安裝全部系統需要大約30分鐘以上,請耐心等候(安裝時間會根據您的電腦設備、傳輸線速度以及網路速度而不同)。

 

Jetson OS 就是 Jetson 的Ubuntu作業系統,安裝完之後要先建立一組帳號密碼才可以繼續安裝其他套件。開機後會出現如下圖的安裝步驟,選擇鍵盤類型、地區時間、帳號密碼等資訊。這些系統設定之後都可以修改。出現桌面,代表帳號建立成功了!

接著安裝Jetson SDK Components,輸入帳號密碼之後就可以將相關套件安裝到 Jetson Nano (aka J1010) 中了。

驗證Jetson Nano空間及系統是否正常,一切順利就會開始安裝 Jetson SDK Components。

STEP 4

所有系統及套件安裝完成之 NVIDIA SDK Manager 畫面如下,安裝完即可按下 FINISH 關閉程式。

以上是當 Jetson Nano 在可正常開機的狀態下的 JetPack 安裝教學,如果已經無法開機的話,就必須使用Recovery Mode 進入回復模式才能重新安裝,之後會寫另一篇文章來教大家如何使用。

PART B  使用腳本將reComputer J1010 系統從eMMC轉移至USB隨身碟

STEP 1 USB隨身碟之介紹

首先如需將系統移動到USB隨身碟,建議使用3號USB 3.0 Type-A接頭做為讀取USB的接口,否則讀取速度會不夠。


隨身碟建議使用SanDisk Ultra Flair USB 3.0 64GB隨身碟,相同或更高的等級的隨身碟也可以
(傳輸速度150MB以上),且不建議使⽤ 512 GB 以上容量的 USB 隨身碟(供電會不夠)。

STEP 2 USB隨身碟之設定

接著要格式化隨身碟,並設定成GPT的格式,步驟如下:

將隨身碟插入上圖的 3 號 USB3.0接頭,於 Ubuntu 系統中搜尋 Disks 並點擊第一個結果,會看到以下畫面,點選你的USB,本文中為USB SanDisk:

點選右上角的三條橫槓,選擇 Format Disk... (格式化),選擇預設的GPT,再次確認要格式化(Format),接著輸入使用者密碼,授權進行格式化:

格式選擇 GPT

點選加號來建立新的磁碟分割,本文建立的新磁碟分割容量為 USB 隨身碟的所有空間,大家可以根據自身需求做調整。接下來幫該分割命名為 NANO_USB (或是你自己想取的名稱),類型請選擇 Ext4 (Fourth EXTended filesystem)。

最後,點選圖示來掛載 USB

STEP 3 移動到USB開機

在終端機中執行 df -h 指令來檢視是否成功掛載以及其路徑,如以下的 /dev/sda1

接著把開機選項移動到USB來開機,首先是下載 change_rootfs_storage_direct-emmc_to_sdmmc.sh,並將檔案複製到Jetson Nano當中。目前測試 JetPack 4.6 可正常執行此腳本,其他 JetPack 版本目前尚未測試過,建議使用JetPack 4.6。(腳本作者使用JetPack 4.6製作)
腳本載點如下:
https://www.dropbox.com/s/93zh995b4czqxwk/change_rootfs_storage_direct-emmc_to_sdmmc.sh?dl=0

接著移動到該檔案的目錄下執行該檔案

於終端機中執行以下指令,賦予該腳本權限

chmod +x change_rootfs_storage_direct-emmc_to_sdmmc.sh

執行以下指令,執行該腳本(注意最後的 /dev/sda1 為要移動的USB位置,請用 df -h 指令再次確認)

sudo ./change_rootfs_storage_direct-emmc_to_sdmmc.sh /dev/sda1

可以看到要搬移約 13 GB 空間到USB 大約需要 12多分鐘,完成後請重新開機。

重開機後可以看到系統左下方出現SD卡選項,代表系統已經順利移動到SSD裡面了!

也可以用 df -h 指令再次確認,是否正確移動到USB開機了

參考資料與相關文章

 

使用RK物聯網教學實驗箱搭配手機建構遠端控制門禁系統

$
0
0

前言

本文發想來源是曾希哲老師的手機遠端控制門禁系統文章,並透過RK物聯網教學實驗箱實現,RK物聯網教學實驗箱的特色有下列幾點:

  1. 符合108課綱「智慧居家實習」、「介面電路控制實習」。
  2. LinkIt7697開發板由聯發科技推出,搭載了WIFI及藍牙(BLE)模組,可激發使用者針對物聯網情境的應用。
  3. RK物聯網擴充板(RK IOT EX Shield),有Grove防呆接頭,可輕鬆接上感測器不費力,不必苦惱腳位接錯。
  4. 圖控介面的程式操作,圖形化程式介面取代文字介面撰寫,可以大大降低專題開發的難度,再也不用因為拼錯字所造成的編譯錯誤而傷腦筋。後續也可銜接到正式的文字程式環境。
撰寫/攝影 許鈺莨
   
時間 2小時 材料表
   
難度 中等

本實驗箱是依照108課綱所配置,其主題有「工場安全衛生及介面電路控制應用」、「通用序列匯流排介面」、「數位類比轉換介面」、「環境感測介面」、「辨識介面」、「藍牙無線傳輸介面」、「綜合應用」,共七個章節。本篇門禁系統就是屬於「藍牙無線傳輸介面」的延伸,以下就針對遠端控制門禁系統的範例來做說明。

流程圖說明

筆者是透過曾希哲老師公開的程式碼,可讓手機端藉由 BLE 藍牙來與 LinkIt 7697 開發板互動,請由此下載 iOS / Android 版本的 LinkIt Remote app,只要編寫 7697 端程式就能生成手機 app 介面,功能雖然簡單了點但已足以完成各種監控介面。請參考相關文章:https://blog.cavedu.com/?s=linkit%20remote

輸入密碼有三次機會,三次機會全錯會有五秒鐘的等待時間,正確密碼為 9527 ,而遠端控制門禁系統操作流程,如圖所示。

硬體介紹

再來介紹所使用的硬體,如下圖所示。本篇是利用LinkIt7697的LinkIt Remote功能與手機連線,並在擴充板接上繼電器來控制電磁鎖,但需注意的是控制電磁鎖需要額外的外接電源,所以筆者是用四顆 1.5V 的四號電池來當外接電源。

程式撰寫

本篇程式是透過 BlocklyDuino圖形化介面來撰寫,而以下 Arduino程式碼是由 BlocklyDuino圖形化介面轉換而來,若讀者希望取得本BlocklyDuino程式碼,請由此下載

也請參考我們所編寫的 BlocklyDuino  + LinkIt 7697 相關範例,很完整喔!

#include <LRemote.h>

int time;

int SetPasswd;

int PhonePasswd;

int c;

String S1;

String S2;

String S3;

String S4;

int count;

LRemoteButton button0;
LRemoteLabel label1;
LRemoteButton button1;
LRemoteButton button2;
LRemoteButton button3;
LRemoteButton button4;
LRemoteButton button5;
LRemoteButton button6;
LRemoteButton button7;
LRemoteButton button8;
LRemoteButton button9;
LRemoteButton button10;

void setup()
{
  LRemote.setName("remote");
  LRemote.setOrientation(RC_PORTRAIT);
  LRemote.setGrid(3, 5);
    button0.setPos(0, 4);
    button0.setText("0");
    button0.setSize(1, 1);
    button0.setColor(RC_ORANGE);
    LRemote.addControl(button0);

    label1.setPos(0, 0);
    label1.setText("輸入密碼");
    label1.setSize(3, 1);
    label1.setColor(RC_BLUE);
    LRemote.addControl(label1);

    button1.setPos(0, 3);
    button1.setText("1");
    button1.setSize(1, 1);
    button1.setColor(RC_ORANGE);
    LRemote.addControl(button1);

    button2.setPos(1, 3);
    button2.setText("2");
    button2.setSize(1, 1);
    button2.setColor(RC_ORANGE);
    LRemote.addControl(button2);

    button3.setPos(2, 3);
    button3.setText("3");
    button3.setSize(1, 1);
    button3.setColor(RC_ORANGE);
    LRemote.addControl(button3);

    button4.setPos(0, 2);
    button4.setText("4");
    button4.setSize(1, 1);
    button4.setColor(RC_ORANGE);
    LRemote.addControl(button4);

    button5.setPos(1, 2);
    button5.setText("5");
    button5.setSize(1, 1);
    button5.setColor(RC_ORANGE);
    LRemote.addControl(button5);

    button6.setPos(2, 2);
    button6.setText("6");
    button6.setSize(1, 1);
    button6.setColor(RC_ORANGE);
    LRemote.addControl(button6);

    button7.setPos(0, 1);
    button7.setText("7");
    button7.setSize(1, 1);
    button7.setColor(RC_ORANGE);
    LRemote.addControl(button7);

    button8.setPos(1, 1);
    button8.setText("8");
    button8.setSize(1, 1);
    button8.setColor(RC_ORANGE);
    LRemote.addControl(button8);

    button9.setPos(2, 1);
    button9.setText("9");
    button9.setSize(1, 1);
    button9.setColor(RC_ORANGE);
    LRemote.addControl(button9);

    button10.setPos(1, 4);
    button10.setText("Clear");
    button10.setSize(2, 1);
    button10.setColor(RC_GREEN);
    LRemote.addControl(button10);
  LRemote.begin();
  SetPasswd = 10;
  PhonePasswd = 0;
  c = 0;
  S1 = "*";
  S2 = "**";
  S3 = "***";
  S4 = "****";
  count = 3;
  pinMode(5, OUTPUT);
}


void loop()
{
  time = 5;
  LRemote.process();
  if (button9.isValueChanged()) {
    if (button9.getValue() == 1) {
      c = c + 1;
      if (c == 1) {
        PhonePasswd = PhonePasswd + 1;

      }

    }

  } else if (button5.isValueChanged()) {
    if (button5.getValue() == 1) {
      c = c + 1;
      if (c == 2) {
        PhonePasswd = PhonePasswd + 2;

      }

    }
  } else if (button2.isValueChanged()) {
    if (button2.getValue() == 1) {
      c = c + 1;
      if (c == 3) {
        PhonePasswd = PhonePasswd + 3;

      }

    }
  } else if (button7.isValueChanged()) {
    if (button7.getValue() == 1) {
      c = c + 1;
      if (c == 4) {
        PhonePasswd = PhonePasswd + 4;

      }

    }
  } else if (button4.isValueChanged()) {
    if (button4.getValue() == 1) {
      c = c + 1;

    }
  } else if (button0.isValueChanged()) {
    if (button0.getValue() == 1) {
      c = c + 1;

    }
  } else if (button6.isValueChanged()) {
    if (button6.getValue() == 1) {
      c = c + 1;

    }
  } else if (button1.isValueChanged()) {
    if (button1.getValue() == 1) {
      c = c + 1;

    }
  } else if (button8.isValueChanged()) {
    if (button8.getValue() == 1) {
      c = c + 1;

    }
  } else if (button3.isValueChanged()) {
    if (button3.getValue() == 1) {
      c = c + 1;

    }
  } else if (button10.isValueChanged()) {
    if (button10.getValue() == 1) {
      label1.updateText(String("輸入密碼"));
      c = 0;
      PhonePasswd = 0;

    }
  }
  if (c == 1) {
    label1.updateText(String(S1));

  } else if (c == 2) {
    label1.updateText(String(S2));
  } else if (c == 3) {
    label1.updateText(String(S3));
  } else if (c == 4) {
    label1.updateText(String(S4));
    delay(500);
    if (SetPasswd == PhonePasswd) {
      label1.updateText(String("開門"));
      digitalWrite(5, HIGH);
      delay(5000);
      digitalWrite(5, LOW);

    } else {
      label1.updateText(String("密碼錯誤"));
      delay(1000);
      count = count - 1;
      label1.updateText(String(String() + "還有" + count + "次"));
      if (count == 0) {
        count = 3;
        label1.updateText(String("五秒後輸入"));
        delay(1000);
        for (int count2 = 0; count2 < 5; count2++) {
          time = time - 1;
          label1.updateText(String(String() + "剩餘" + time + "秒"));
          delay(1000);
        }

      }
      delay(2000);

    }
    label1.updateText(String("輸入密碼"));
    c = 0;
    PhonePasswd = 0;
  }
  delay(100);
}
RK iot box remote control

影片展示

最後,來看看RK物聯網教學實驗箱門禁系統影片的操作說明吧!

結語

本實驗箱已備有使用手冊,及多種範例實際應用操作,各位讀者及大專院校老師們欲購買RK物聯網教學實驗箱,請點選以下連結,我們會有專人為您服務:
https://robotkingdom.com.tw/product/rk_iotedubox/

本篇主題到此告一段落,下次再見囉!

Viewing all 678 articles
Browse latest View live