直接上視頻聽我 BB:
https://www.bilibili.com/video/av75675708
下面開始編故事……
某個週末,走在去加班的路上,腳底突然被某個東西咯噔一下,擡腳一看,竟然是……
撿起來一看……
哈士奇!哈士奇!哈士奇!
竟然是 DF 還在預售的 HuskyLens 人工智能攝像頭(中文名:哈士奇) !
這個故事告訴我們:喜歡加班的創客,運氣不會太差。
HuskyLens 功能介紹
HuskyLens 是什麼?
這裏簡單截取官網介紹的一部分:
HuskyLens 是一款簡單易用的人工智能攝像頭(視覺傳感器),內置 6 種功能:人臉識別、物體追蹤、物體識別、巡線追蹤、顏色識別、標籤(二維碼)識別。僅需一個按鍵即可完成 AI 訓練,擺脫繁瑣的訓練和複雜的視覺算法,讓你更加專注於項目的構思和實現。
詳細介紹,可以查看官網說明: https://www.dfrobot.com.cn/goods-2050.html
哈士奇官方介紹視頻,請直接跳轉 B 站:
http://player.bilibili.com/player.html?aid=66695069&cid=115655109
用 3 個單詞就可以概括 HuskyLens 的特性:Click、Learn & Play。
總之:簡單、易用、真香!
這麼香的工具,到底該怎麼玩呢?請繼續往下看。
注意:以下章節偏技術性,是對體驗視頻的文字性詳細描述,會略微無聊,請收藏後慢慢閱讀。
HuskyLens 數據讀取測試
由於 HuskyLens 目前還沒有正式上市,所以關於它的資料非常少,DF 暫時也沒有放出 HuskyLens 對應的函數庫,更不用說在 Mind+ 等圖形化編程工具中調用了。不過相信不久,我們就可以在 Mind+ 中看到它了,到那時,它的使用就會更加簡單、更加小白了!
暫時我們只能根據它的通信協議,徒手來擼代碼了。
HuskyLens 默認採用串口通信,當然可以在它的設置中改成 I2C 通信。默認的串口波特率爲 57600 bps,格式爲 8N1( 8 位數據位、無校驗、1位停止位 ),默認通信地址爲 0x11
。本文中,我帶領大家使用掌控板來讀取 HuskyLens 的數據。
當然你也可以選擇使用 Arduino、Raspberry Pi、LattePanda、micro:bit 等控制器來讀取 HuskyLens 的數據,原理都是一樣的,就留作課後作業吧,看文本文後,你能不能用其他主控板來讀取 HuskyLens 的數據呢?
電路連接圖如下:
關於掌控板串口的補充說明:
掌控板的主控芯片是 ESP32,ESP32 有 3 個串口,分別是
Serial
、Serial1
、Serial2
。Serial
我們一般用來下載程序,Serial1
默認使用了GPIO 9
和GPIO 10
,但是 ESP32 的GPIO 6~11
一般用於連接外部 Flash 芯片,所以我們這裏使用Serial2
與 HuskyLens 連接通信。另外,ESP32 可以將串口 RX 映射到幾乎所有 IO 口上,TX 映射到GPIO 0~31
上(此處沒有進行驗證)。所以,這裏我們將掌控板
Serial2
的 RX 映射到 P14 引腳,將 TX 映射到 P13 引腳。參考資料: https://blog.csdn.net/Naisu_kun/article/details/86004049
我們來看一下 HuskyLens 的通信協議。它主要有兩種模式,正如視頻中看到的那樣,大部分情況下, HuskyLens 屏幕上會顯示一個方框(Block 模式),在巡線模式下,它會顯示一個箭頭(Arrow 模式)。這兩種模式下,數據的長度和格式基本是一致的,這裏我們以 Block 模式爲例進行講解,Arrow 模式原理一樣,此處不再贅述。
我們來看一下它的數據格式,可以看到它是以0x55
和0xAA
開頭的一串數據,緊接着是它的通信地址 Address、數據長度 Data Length、命令代碼 Command,以及我們最關心的數據 Data。最後是一個校驗位。
每個數據的含義如下:
Block 模式:
Arrow 模式:
上面兩個表格中紅框框出來的是相應的數據 Data 1 ~ Data n,其中:
- X Center 表示方框 Block 的幾何中心的 X 座標;
- Y Center 表示方框 Block 的幾何中心的 Y 座標;
- Width 表示方框 Block 的寬度;
- Height 表示方框 Block 的高度;
- LearnedIndex 表示識別目標的編號。
我們根據通信協議,編寫程序來讀取一下 HuskyLens 返回的數據。
首先打開 Mixly 自帶的 Arduino IDE,選擇 Arduino HandBit (掌控板)進行編程。
最新版 Mixly 1.0 下載地址: https://mixly.readthedocs.io/zh_CN/latest/basic/02Installation-update.html
程序如下:
void setup()
{
Serial.begin(57600);
Serial2.begin(57600, SERIAL_8N1, P14, P13);
}
void loop()
{
if (Serial2.available() > 0) {
Serial.println(Serial2.read(), HEX);
}
}
將程序上傳到掌控板,打開串口監視器,可以看到會返回類似下圖中的數據:
但是這些數據代表着什麼意思呢?我們將關鍵數據圈出來:0x55、0xAA、0x11、0x0A、0x10、0xA3、0x00、0x7B、0x00、0x46、0x00、0x46、0x00、0x01、0x00、0xD5。其中:
0x55
爲 Header;0xAA
爲 Header 2;0x11
爲 Address;0x0A
爲 Data Length;0x10
爲 Command,Block 模式下爲0x10
,Arrow 模式下爲0x11
;0xA3
、0x00
分別爲 X Center 的低 8 位和高 8 位,0x00A3
換算後爲 163,代表 X 座標爲 163;0x7B
、0x00
爲 Y Center 的低 8 位和高 8 位,0x007B
換算後爲 123,代表 Y 座標爲 123;0x46
、0x00
爲 Width 的低 8 位和高 8 位,0x0046
換算後爲 70,代表方框寬度爲 70;0x46
、0x00
爲 Height 的低 8 位和高 8 位,0x0046
換算後爲 70,代表方框高度爲 70;0x01
、0x00
爲 LearnedIndex 的低 8 位和高 8 位,0x0001
換算後爲 1,代表識別物體的編號爲 1;0xD5
爲 Checksum 的低 8 位,我們將上面所有數據相加求和:0x55 + 0xAA + 0x11 + 0x0A + 0x10 + 0xA3 + 0x00 + 0x7B + 0x00 + 0x46 + 0x00 + 0x46 + 0x00 + 0x01 + 0x00 =0x02D5
,低 8 位是0xD5
說明校驗通過。
至此,我們就完成了 HuskyLens 數據的簡單讀取。
但是每次都要這麼去讀取數據,然後再進行手工計算麼?這還怎麼做人工智能項目啊?
當然不是的,我們發現,讀取這些數據就是都是一件件重複的事情,而程序最擅長的就是做重複的事情了。
HuskyLens 數據解析
我們將上面的程序簡單調整一下,duang~ 就完成了!(程序源代碼詳見文末下載鏈接,建議配套代碼一起閱讀下文)
我們先定義了一些變量,用來存放數據。這些變量的名字,基本可以自解釋其含義,此處不再贅述說明。
#define LENG 15 // 0x55 + 15 bytes equal to 16 bytes
unsigned char buf[LENG];
int xCenterOrXOrigin = 0;
int yCenterOrYOrigin = 0;
int widthOrXTarget = 0;
int heightOrYTarget = 0;
int learnedIndex = 0;
在 setup()
中,主要是對兩個串口進行初始化:
void setup()
{
Serial.begin(57600);
Serial2.begin(57600, SERIAL_8N1, P14, P13);
}
然後在 loop()
中,不斷去讀取掌控板串口 2(Serial2)中返回的數據。首先要判斷第 1 個讀到的數據是否是默認的 Header 0x55
,我們用了find()
函數:
// Header
// Read data until get the first header of data packet 0x55
if (Serial2.find(0x55))
{
// ......
}
如果讀取到0x55
,那麼就把剩下的 15 個數據都讀取進來,並存儲到 buf
變量中。因爲一個有效的通信命令共16個數據,大家可以在通信協議中數一下。
// Read the next 15 data
Serial2.readBytes(buf, LENG);
接着去檢查第 2 個數據是否是默認的 Header 2 0xAA
。這裏需要注意的是,我們並沒有把第 1 個 Header 數據 0x55
存儲到 buf
變量中,所以 buf[0]
不是 0x55
,而是 0xAA
。
// Header 2
// Check the second header of data packet 0xAA
if (buf[0] == 0xAA)
{
// ......
}
如果第 2 個數據也是默認的包頭的話,再去對剩下的數據進行校驗,這裏調用校驗和函數 checkSum()
,具體這個函數怎麼實現的,下面再講。
// Checksum
if (checkSum(buf, LENG))
{
// ......
} else {
Serial.println("Checksum Errorrrrr!");
}
校驗和通過後,就可以對數據進行處理和計算了。在下面的程序中,我們先把原始數據打印出來:
// print the command list
Serial.print(0x55, HEX);
Serial.print(" ");
for (int i = 0; i < LENG; i++)
{
Serial.print(buf[i], HEX);
Serial.print(" ");
}
Serial.println();
然後通過 5 個函數分別去計算 Data 中的幾個值。最後再將這些數據在串口監視器中打印出來。
// get the values
xCenterOrXOrigin = getX(buf);
yCenterOrYOrigin = getY(buf);
widthOrXTarget = getWidthOrXTarget(buf);
heightOrYTarget = getHeightOrYTarget(buf);
learnedIndex = getLearnedIndex(buf);
// print the values
Serial.print("x: ");
Serial.print(xCenterOrXOrigin);
Serial.print(" ");
Serial.print("y: ");
Serial.print(yCenterOrYOrigin);
Serial.print(" ");
Serial.print("width: ");
Serial.print(widthOrXTarget);
Serial.print(" ");
Serial.print("height: ");
Serial.print(heightOrYTarget);
Serial.print(" ");
Serial.print("learnedIndex: ");
Serial.print(learnedIndex);
Serial.print(" ");
Serial.println();
Serial.println("-----------------------------");
Serial.println();
這樣我們就把數據讀取出來啦。
幾個函數說明
checkSum()
checkSum()
函數的功能就是校驗讀取到的數據是否正確。只要簡單講讀到的數據相加,並最終取和的低 8 位,檢查是否和讀到的最後一個數據相等即可。相等的話,將標記變量 receiveflag
賦值爲 1 即可,否則賦值爲 0,並返回 receiveflag
的結果。
// check sum
char checkSum(unsigned char *buf, char leng)
{
char receiveflag = 0;
int sum = 0;
int sumLow = 0;
for (int i = 0; i < (leng - 1); i++)
{
sum = sum + buf[i];
}
sum = sum + 0x55;
sumLow = sum & 0x00FF;
if (sumLow == buf[leng - 1])
{
sum = 0;
receiveflag = 1;
}
return receiveflag;
}
getX()
getY()
getWidthOrXTarget()
getHeightOrYTarget()
getLearnedIndex()
這 5 個函數的原理基本一致,其函數名基本可以自解釋含義,此處也不再贅述解釋每個函數的功能。
這裏以第 1 個函數 getX()
爲例,介紹這幾個函數內部的原理。
通過查看通信協議,我們可以知道,X 座標的數值,分別存儲在 buf
變量中的第 5 個和第 6 個(實際是完整數據的 6 個和第 7 個數據,但是 buf
變量中沒有存儲第 1 個數據 0x55
),所以這裏取 buf[4]
和 buf[5]
來進行計算即可。 由於是 16 進制的數據,所以可以通過移位進行計算,當然這裏的 << 8
等價爲 * 256
。最後將計算出來的值返回即可。
// X Center of Block [Block mode]
// or X Origin of Arrow [Arrow mode: Line tracking mode]
int getX(unsigned char *thebuf)
{
int xCenterValue;
// calculate X Center of Block value
xCenterValue = ((thebuf[5] << 8) + thebuf[4]);
return xCenterValue;
}
其他幾個數據,也是同樣道理,只要取對應的數據位進行計算即可。
總結
通過這一章節,我們根據 HuskyLens 的通信協議,把 HuskyLens 返回的數據都讀取出來了。那麼我們可以利用這些數據做一些什麼有趣好玩的事情呢?
物體追蹤,人臉識別,物體識別,巡線追蹤,顏色識別,標籤識別等功能,要怎麼用呢?
交互手勢控制、自主機器人、智能門禁、交互式玩具等又要怎麼實現呢?
我們下期見!
代碼下載
請到知識星球下載本教程對應的源代碼:https://t.zsxq.com/RB6EaYf