1. 簡介
建構項目
在本程式碼研究室中,我們將學習使用 TensorFlow Lite for Microcontrollers,在 SparkFun Edge 開發委員會上執行深度學習模型。我們將使用 Jamboard 的內建語音偵測模型,這個模型使用卷積類神經網路來偵測「是」字詞和「否」正透過海板的兩個麥克風說話
微控制器的機器學習技術
機器學習技術可用於打造智慧工具,提供更便捷的生活,例如 Google 助理。不過,這類體驗需要大量的運算或資源,包括強大的雲端伺服器或桌上型電腦。不過,現在也能在微型低電的硬體 (例如微控制器) 上執行機器學習推論。
微控制器非常常見、成本低廉,需要的能源極少,而且十分可靠。這些元件屬於各式各樣的家庭裝置:電器、汽車和玩具。事實上,每年生產約 300 億部採用微控制器的裝置。
透過在極小的微控制器提供機器學習技術,我們得以為生活中數十億部 Google 裝置提供智慧功能,不必依賴昂貴的硬體或可靠的網路連線。想像一下,如果智慧家電可以依照您的日常作息調整,還有智慧型工業感測器可瞭解問題與正常操作之間的差異,而有魔法玩具可以幫助孩子以有趣且愉快的方式學習。
TensorFlow Lite for Microcontrollers (軟體)
TensorFlow 是 Google 的開放原始碼機器學習架構,用於訓練及執行模型。TensorFlow Lite 是一種軟體架構,此為 TensorFlow 的最佳化版本,專門用於在手機等低階裝置 (例如手機) 上執行 tensorflow 模型。
TensorFlow Lite For Microcontrollers 是一種軟體架構,是 TensorFlow 的最佳化版本,主要目標為在微弱的小型硬體 (例如微控制器) 上執行 Tensorflow 模型。它遵守這些內嵌環境中所需的限制,例如二進位檔大小較小、不需要作業系統支援,也不需要任何標準 C 或 C++ 程式庫,或是動態記憶體配置等。
SparkFun Edge (硬體)
SparkFun Edge 是以微控制器為基礎的平台:單一電路板上的小型電腦。搭載處理器、記憶體和 I/O 硬體,可傳送及接收數位訊號至其他裝置。搭載四種軟體控制 LED 燈,為你最愛的 Google 配色。
有別於電腦,微控制器無法執行作業系統,而會直接在硬體上執行您撰寫的程式。您將電腦編寫程式碼,並透過名為程式設計師的裝置將程式碼下載至微控制器。
微控制器並非強大的電腦,這類電腦搭載小型處理器,記憶體容量不多。但因為在設計上盡可能簡單,微控制器只會耗用極少的能源。視程式的工作而定,使用一枚硬幣電池,SparkFun Edge 可以執行數週!
課程內容
- 在電腦上編譯 SparkFun Edge 的範例程式
- 將程式部署至裝置
- 變更程式內容並再次部署
軟硬體需求
您需要具備下列硬體:
- Linux 或 MacOS 電腦
- SparkFun Edge 看板
- SparkFun USB-C 序列基本程式設計師
- USB-C 轉 USB-A 傳輸線 (如果你使用 USB-C 電腦,請改為提供 USB-C 轉 USB-C 傳輸線)
- (選用) 3V 20 公釐鈕扣型鋰電池 (CR2032),以便在沒有程式設計師和傳輸線的情況下執行推論
您需要下列軟體:
- Git (透過指令列執行
git
,藉此檢查是否已安裝 SDK) - Python 3 (透過指令列執行
python3
或python --version
,即可檢查是否已安裝完成) - Python 3 適用的 Pip ( 實用的 StackOverflow 解答)
- 推出 4.2.1 以上版本 (透過指令列執行
make --version
,確認是否已安裝) - SparkFun Serial 基本驅動程式
2. 設定硬體
SparkFun Edge 微控制器隨附預先安裝的二進位檔,可用來執行語音模型。在我們以自己的版本覆寫之前,先執行這個模型。
以下做法可讓主機板節能:
- 將硬幣電池插入電板背面的電池連接器 (電池的「+」面朝上)。如果電板已插入電池,請取出塑膠片並推電池,確認電池已完全插入)。
- 如果您沒有硬幣電池,可以使用 SparkFun USB-C 序列基本程式設計師裝置為 Jamboard 供電。如要將這部裝置連接至 Jamboard,請按照下列步驟操作:
- 找出 SparkFun Edge 側邊的六個圖釘標頭。
- 將 SparkFun USB-C 序列基本插頭插入這些接腳,確保標示「BLK」的針腳和「GRN」並確認各個裝置的排列順序均正確無誤
- 在 SparkFun USB-C 序列基本級和電腦之間連接 USB-C 傳輸線。
裝上電池或連接 USB 程式設計師將電板開機後,電路板會喚醒,並開始透過麥克風聆聽音訊。藍燈應該會開始閃爍。
開發板上的機器學習模型經過訓練,可辨識「是」字詞偵測和偵測結果是否存在這個模式會透過燈號閃爍的 LED 燈傳達測試結果。下表列出各個 LED 顏色的含義:
偵測結果 | LED 顏色 |
「是」 | 黃色 |
「否」 | 紅色 |
不明語音 | 綠色 |
偵測不到語音 | LED 燈未亮起 |
歡迎體驗
將白板舉到嘴巴上,然後說「好」數次你會看到黃色的 LED 閃光燈。說出「是」後,如果系統仍未回應,請嘗試下列做法:
- 將遊戲板維持在約 10 英寸你的嘴巴
- 避免背景噪音過多
- 再說一次「是」連續幾次 (試著說「是,是」)
3. 設定軟體
現在要自行下載、安裝及執行微控制器上的語音模型。為了達到這個目的,我們先下載這個程式的原始碼,以及建構此程式所需的依附元件。程式是以 C++ 語言編寫,必須先編譯為「二進位檔」,才能下載至主面板。二進位檔是包含程式的檔案,該形式可由 SparkFun Edge 硬體直接執行。
以下是適用於 Linux 或 MacOS 的操作說明。
下載 TensorFlow 存放區
您可以在 GitHub 的 TensorFlow 存放區中找到程式碼,位置如下:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/micro
在電腦上開啟終端機,變更為您平常儲存程式設計專案的目錄,下載 TensorFlow 存放區,然後輸入建立的目錄,如下所示:
cd ~ # change into your home (or any other) directory git clone --depth 1 https://github.com/tensorflow/tensorflow.git cd tensorflow
下載 Python 依附元件
我們將使用 Python 3 準備二進位檔,並將二進位檔刷新到裝置上。Python 指令碼取決於可用的特定程式庫。執行下列指令來安裝這些依附元件:
pip3 install pycrypto pyserial --user
4. 建構及準備二進位檔
我們正在建構二進位檔,並執行指令準備將檔案下載到裝置上。
建構二進位檔
如要下載所有必要的依附元件並建立二進位檔,請執行下列指令:
make -f tensorflow/lite/micro/tools/make/Makefile TARGET=sparkfun_edge micro_speech_bin
如果建構成功,輸出內容的最後一行應如下所示:
arm-none-eabi-objcopy tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin -O binary
如要確認二進位檔已成功建立,請執行下列指令:
test -f \ tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin && \ echo "Binary was successfully created" || echo "Binary is missing"
控制台應該會顯示 Binary was successfully created
!如果您看到 Binary is missing
,表示建構程序發生問題,需要偵錯。
準備二進位檔
二進位檔必須使用加密編譯金鑰簽署,才能部署至裝置。我們現在要執行一些指令來簽署二進位檔,以便將二進位檔下載至 SparkFun Edge。
輸入下列指令,設定可供開發使用的一些虛擬加密編譯金鑰:
cp tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/keys_info0.py tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/keys_info.py
接著執行下列指令,建立已簽署的二進位檔:
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/create_cust_image_blob.py \ --bin tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin \ --load-address 0xC000 \ --magic-num 0xCB \ -o main_nonsecure_ota \ --version 0x0
這會建立 main_nonsecure_ota.bin
檔案。現在我們會執行另一個指令來建立最終版本的檔案,讓您以系統啟動載入程式指令碼刷新裝置,我們將在下一個步驟中使用:
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/create_cust_wireupdate_blob.py \ --load-address 0x20000 \ --bin main_nonsecure_ota.bin \ -i 6 \ -o main_nonsecure_wire \ --options 0x1
現在執行指令的目錄中應有一個名為 main_nonsecure_wire.bin
的檔案。我們將刷新到裝置上的檔案。
5. 準備好刷新二進位檔
什麼是閃爍?
SparkFun Edge 會將目前執行的程式儲存在 512 KB 快閃記憶體中。如果您希望 Jamboard 執行新程式,我們必須將新程式傳送到主面板,其中會儲存至快閃記憶體中,並覆寫先前儲存的任何程式。
這項程序稱為「刷新」,我們會用它將程式送到董事會上。
將程式設計師固定到 Jamboard 上
將使用 SparkFun USB-C 系列基本序列程式設計師下載新程式到 Jamboard 上。這部裝置允許您的電腦透過 USB 與微控制器通訊。
如要將這部裝置連接至 Jamboard,請按照下列步驟操作:
- 找出 SparkFun Edge 側邊的六個圖釘標頭。
- 將 SparkFun USB-C 序列基本插頭插入這些接腳,確保標示「BLK」的針腳和「GRN」並確認各個裝置的排列順序均正確無誤
將程式設計師連接至電腦
我們將透過 USB 將主機板連接到電腦。如要編寫主機板,我們需要知道電腦為裝置提供的名稱。最佳做法是列出這部電腦在連接前後的所有裝置,然後看看哪一部是新裝置。
透過 USB 連接裝置前,請執行下列指令:
If you are using Linux: ls /dev/tty* If you are using MacOS: ls /dev/cu*
這會輸出已連接的裝置清單,如下所示:
/dev/cu.Bluetooth-Incoming-Port /dev/cu.MALS /dev/cu.SOC
接著,將程式設計師連接到電腦的 USB 連接埠。再次輸入下列指令:
If you are using Linux: ls /dev/tty* If you are using MacOS: ls /dev/cu*
輸出內容應會顯示一個額外項目,如下例所示。新項目的名稱可能有所不同。這個新項目是裝置名稱。
/dev/cu.Bluetooth-Incoming-Port /dev/cu.MALS /dev/cu.SOC /dev/cu.wchusbserial-1450
首先,建立一個環境變數來識別裝置名稱:
export DEVICENAME=put your device name here
接下來,我們會建立環境變數來指定傳輸速率,也就是資料傳送到裝置的速度:
export BAUD_RATE=921600
6. 刷新二進位檔
執行指令碼來刷新白板
如要刷新遊戲板,必須放到特殊的「系統啟動載入程式」中狀態,準備好用來接收新的二進位檔我們會執行指令碼,將二進位檔傳送至主機板。
讓我們熟悉以下開發板上的按鈕:
請按照下列步驟重設並刷新板:
- 確認主機板已接上程式設計師,且整個設定程序已透過 USB 連接至電腦。
- 開始:按住主機板上標示
14
的按鈕。直到步驟 6 為止。 - 仍在按住標示為
14
的按鈕,如要將遊戲板重設為系統啟動載入程式狀態,請按一下標示為RST
的按鈕重設面板。 - 仍然按住標示
14
的按鈕,將下列指令貼到終端機中,然後按下 Enter 鍵執行 (為了方便起見,您可以先將這個指令貼到終端機,再開始按住按鈕,但在執行這個步驟之前,請勿按 Enter 鍵)
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/uart_wired_update.py -b ${BAUD_RATE} ${DEVICENAME} -r 1 -f main_nonsecure_wire.bin -i 6
- 仍在按住標示
14
的按鈕,畫面上現在應會顯示如下圖所示:
Connecting with Corvette over serial port /dev/cu.usbserial-1440... Sending Hello. Received response for Hello Received Status length = 0x58 version = 0x3 Max Storage = 0x4ffa0 Status = 0x2 State = 0x7 AMInfo = 0x1 0xff2da3ff 0x55fff 0x1 0x49f40003 0xffffffff [...lots more 0xffffffff...] Sending OTA Descriptor = 0xfe000 Sending Update Command. number of updates needed = 1 Sending block of size 0x158b0 from 0x0 to 0x158b0 Sending Data Packet of length 8180 Sending Data Packet of length 8180 [...lots more Sending Data Packet of length 8180...]
- 在看到
Sending Data Packet of length 8180
後停止在白板上按住標示14
的按鈕 (但按住這個按鈕也可以)。程式會繼續在終端機列印路線。最終看起來會像這樣:
[...lots more Sending Data Packet of length 8180...] Sending Data Packet of length 8180 Sending Data Packet of length 6440 Sending Reset Command. Done.
如果看到 Done
,表示成功閃爍。如果程式輸出內容的結尾發生錯誤,請檢查是否已列印 Sending Reset Command
。如果是的話,雖然發生錯誤,但刷卡可能成功。
在 Linux 電腦上,您可能會遇到 NoResponse Error
。這是因為一併安裝 ch34x 序列驅動程式和現有序列驅動程式,可以按照以下方式解析:
步驟 1:重新安裝正確的 ch34x 程式庫版本。在安裝過程中,確保裝置從電腦拔除。
git clone https://github.com/juliagoda/CH341SER.git cd CH341SER/ make sudo insmod ch34x.ko sudo rmmod ch341
步驟 2:將主機板 USB 插入並執行:
dmesg | grep "ch34x"
畫面上應會顯示類似下方的訊息:
[ 1299.444724] ch34x_attach+0x1af/0x280 [ch34x] [ 1299.445386] usb 2-13.1: ch34x converter now attached to ttyUSB0
如果使用的驅動程式不是「ch34x」(例如:ch341),請執行以下指令,停用其他驅動程式:
rmmod <non-ch34x driver name>
拔除並重新插上裝置,並確認使用的驅動程式為「ch34x」。
7. 展示模式
試用程式
成功閃爍白板後,按下標示為
RST
可重新啟動遊戲板並啟動程式。如果藍色 LED 燈開始閃爍,表示已成功閃爍。如果沒有,請向下捲動至下方的「如果失敗時該怎麼辦?」部分。
開發板上的機器學習模型經過訓練,可辨識「是」字詞偵測和偵測結果是否存在這個模式會透過燈號閃爍的 LED 燈傳達測試結果。下表列出各個 LED 顏色的含義:
偵測結果 | LED 顏色 |
「是」 | 黃色 |
「否」 | 紅色 |
不明語音 | 綠色 |
偵測不到語音 | LED 燈未亮起 |
歡迎體驗
將白板舉到嘴巴上,然後說「好」數次你會看到黃色的 LED 閃光燈。說出「是」後,如果系統仍未回應,請嘗試下列做法:
- 將遊戲板維持在約 10 英寸你的嘴巴
- 避免背景噪音過多
- 再說一次「是」連續幾次 (試著說「是,是」)
如果失敗會怎麼樣?
以下列舉幾個可能的問題和偵錯方式:
問題:閃爍後,LED 燈並未亮起。
解決方法:請嘗試按下 RST
按鈕,或中斷 Jamboard 與程式設計師的連線後再重新接上,如果以上方法都無效,請再次嘗試刷新遊戲板。
問題:藍色 LED 燈亮起,但非常昏暗。
解決方法:請在電量不足時更換電池。你也可以使用電腦內建的程式設計師和傳輸線為主機板開機。
8. 讀取偵錯輸出內容 (選用)
如果您遇到問題且需要詳細偵錯程式碼,請參閱本節說明。如要瞭解程式碼執行時微控制器內發生的情況,您可以透過主機板的序列連線顯示偵錯資訊。您可以使用電腦連線至 Jamboard,並顯示主面板傳送的資料。
開啟序列連線
根據預設,SparkFun Edge 程式碼範例會記錄所有語音指令和信心。如要查看 Jamboard 的輸出內容,您可以執行下列指令:
screen ${DEVICENAME} 115200
一開始,您可能會看到類似如下所示的輸出內容:(只有在連線板經過重設後,您才會開始看到偵錯資訊時,才會顯示這個輸出內容)
Apollo3 Burst Mode is Available Apollo3 operating in Burst Mode (96MHz)
試著說出「是」來下達指令或「否」。您應該會看到各指令的主機板列印偵錯資訊:
Heard yes (202) @65536ms
在上述記錄中,yes
是指指令。數字 202
是指偵測到指令的信賴水準 (下限為 200)。最後,65536ms
是指微控制器上次重設後經過的時間。
如要停止查看偵錯輸出內容,請點選 Ctrl+A
,後面緊接著 K
鍵,然後按 Y
鍵。
寫入偵錯記錄檔
您可以在剛剛使用的 command_responseer.cc 檔案中,查看記錄此資訊的程式碼:
tensorflow/lite/micro/examples/micro_speech/sparkfun_edge/command_responder.cc
如要記錄資料,您可以呼叫 error_reporter->Report()
方法。支援使用標準 printf
符記插入字串,您可以在記錄中加入重要資訊:
error_reporter->Report("Heard %s (%d) @%dms", found_command, score, current_time);
當您在下一節中自行變更程式碼時,此方法非常實用。
9. 擴充程式碼 (選用)
現在您已經瞭解如何建構及刷新 SparkFun Edge,可以開始試用程式碼並將其部署至裝置來檢視結果。
讀取程式碼
下列檔案是開始讀取程式碼的絕佳位置:command_responder.cc.
tensorflow/lite/micro/examples/micro_speech/sparkfun_edge/command_responder.cc
您可以在這裡查看 GitHub 的檔案。
偵測到語音指令時,系統會呼叫這個檔案中的方法 RespondToCommand
。系統會根據聽到的「是」、「否」或不明指令,開啟不同的 LED 燈。以下程式碼片段說明運作方式:
if (found_command[0] == 'y') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_YELLOW);
}
if (found_command[0] == 'n') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_RED);
}
if (found_command[0] == 'u') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_GREEN);
}
found_command
引數包含偵測到的指令名稱。查看第一個字元時,這組 if
陳述式會決定哪個 LED 燈要亮起。
使用多個引數呼叫 ResponseToCommand 方法:
void RespondToCommand(tflite::ErrorReporter* error_reporter,
int32_t current_time, const char* found_command,
uint8_t score, bool is_new_command) {
error_reporter
是用來記錄偵錯資訊 (稍後會詳細說明)。current_time
代表偵測到指令的時間。found_command
會說明偵測到的指令。score
能讓我們瞭解系統偵測到指令的信心程度。- 請問這是第一次聽到指令時,
is_new_command
能通知我們。
score
是介於 0 到 255 之間的整數,代表偵測到指令的機率。範例程式碼只有在分數大於 200 時,才會將指令視為有效。根據我們的測試,最有效的命令在 200-210 之間。
修改程式碼
SparkFun Edge 遊戲板有四個 LED 燈。這是因為藍色的 LED 燈會閃爍,這表示裝置已開始辨識。您可以在 command_responder.cc
檔案中查看這項資訊:
static int count = 0;
// Toggle the blue LED every time an inference is performed.
++count;
if (count & 1) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_BLUE);
} else {
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
}
我們的銀行有四個 LED 燈,因此要修改程式,將其做為指定指令 score
的視覺指標。如果分數較低,就會發出一個照明 LED 燈;如果高分,則會發出多個燈號。
為了確保我們能夠知道程式正在執行,我們會持續進行紅色的 LED 閃光燈,而非藍色。相鄰的藍色、綠色和黃色的 LED 燈將用於顯示最近 score
的強度。為了方便起見,只有在關鍵字「是」時,我們才會亮起 LED 燈語音。如果偵測到其他字詞,LED 燈就會清除。
如要進行這項變更,請將 command_responder.cc
檔案中的所有程式碼,替換成以下程式碼片段:
#include "tensorflow/lite/micro/examples/micro_speech/command_responder.h"
#include "am_bsp.h"
// This implementation will light up the LEDs on the board in response to different commands.
void RespondToCommand(tflite::ErrorReporter* error_reporter,
int32_t current_time, const char* found_command,
uint8_t score, bool is_new_command) {
static bool is_initialized = false;
if (!is_initialized) {
// Setup LEDs as outputs
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_RED, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_BLUE, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_GREEN, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_YELLOW, g_AM_HAL_GPIO_OUTPUT_12);
// Ensure all pins are cleared
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_YELLOW);
is_initialized = true;
}
static int count = 0;
// Toggle the red LED every time an inference is performed.
++count;
if (count & 1) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_RED);
} else {
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
}
if (is_new_command) {
// Clear the last three LEDs
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_YELLOW);
error_reporter->Report("Heard %s (%d) @%dms", found_command, score,
current_time);
// Only indicate a 'yes'
if (found_command[0] == 'y') {
// Always light the blue LED
am_hal_gpio_output_set(AM_BSP_GPIO_LED_BLUE);
// Light the other LEDs depending on score
if (score >= 205) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_GREEN);
}
if(score >= 210) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_YELLOW);
}
}
}
}
如果偵測到新指令,is_new_command
的值為 true。請先清除藍色、綠色和黃色的 LED 燈,再根據 found_command
和 score
的值再次亮起。
重建並刷新
變更程式碼後,請執行建構並準備二進位檔中的所有步驟來進行測試。
10. 後續步驟
恭喜,您已成功在微控制器上建構第一個語音偵測工具!
希望你喜歡這份簡短的簡介,瞭解如何使用 TensorFlow Lite for Microcontrollers 進行開發作業。在微控制器中,深度學習的構想是新點子,令人感到振奮,建議您去嘗試看看!
參考文件
- 訓練自己的模型瞭解各種指令,您現在已有使用基本程式的經驗。注意:訓練作業需要幾個小時才能完成!
- 進一步瞭解 TensorFlow Lite for Microcontrollers ( 網站、GitHub)。
- 請嘗試其他範例,並嘗試在 SparkFun Edge 上執行這些範例 (如果支援的話)。
- 請參閱 O'Reilly 一書的《TinyML: Machine Learning with TensorFlow on Arduino and Ultra-Low Power Micro-Controllers》(TinyML:在 Arduino 和超低功率微控制器上運用機器學習的機器),這個單元會介紹機器學習,並逐步完成幾項有趣的專案。本程式碼研究室是以書籍第 7 章和第 8 章為基礎。
謝謝,祝一切順利!