【毕业设计】树莓派智能捡垃圾机器人 - 机器视觉 单片机 物联网
文章目錄
- 0 前言
- 1 簡介
- 2 主要器件
- 3 實(shí)現(xiàn)效果
- 4 設(shè)計(jì)原理
- 1 在 Edge Impulse 中構(gòu)建平衡良好的數(shù)據(jù)集
- 2 在 Edge Impulse 中設(shè)計(jì)脈沖(神經(jīng)網(wǎng)絡(luò)模型)
- 3 使用遷移學(xué)習(xí)訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型(脈沖)
- 4 將 Raspberry Pi 4 連接到 Edge Impulse
- 5 在 Raspberry Pi 4 上部署和運(yùn)行模型
- 6 在 Raspberry Pi 4 上設(shè)置 RPLIDAR A1M8 360 度激光雷達(dá)
- 7 使用RPLIDAR A1M8檢測障礙物并控制機(jī)器人底盤
- 8 使用 Arduino 為機(jī)器人構(gòu)建跌倒檢測系統(tǒng)
- 5 部分核心代碼
- 8 最后
0 前言
🔥 這兩年開始畢業(yè)設(shè)計(jì)和畢業(yè)答辯的要求和難度不斷提升,傳統(tǒng)的畢設(shè)題目缺少創(chuàng)新和亮點(diǎn),往往達(dá)不到畢業(yè)答辯的要求,這兩年不斷有學(xué)弟學(xué)妹告訴學(xué)長自己做的項(xiàng)目系統(tǒng)達(dá)不到老師的要求。
為了大家能夠順利以及最少的精力通過畢設(shè),學(xué)長分享優(yōu)質(zhì)畢業(yè)設(shè)計(jì)項(xiàng)目,今天要分享的是
🚩 基于樹莓派的智能撿垃圾機(jī)器人
🥇學(xué)長這里給一個(gè)題目綜合評(píng)分(每項(xiàng)滿分5分)
- 難度系數(shù):5分
- 工作量:3分
- 創(chuàng)新點(diǎn):5分
🧿 選題指導(dǎo), 項(xiàng)目分享:
https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md
1 簡介
通過這款自動(dòng)駕駛機(jī)器人,可進(jìn)行物體檢測識(shí)別和監(jiān)控垃圾。它還部署了視頻流和跌倒檢測系統(tǒng)。
使用 Edge Impulse 構(gòu)建了一個(gè)神經(jīng)網(wǎng)絡(luò)模型,用于在這三類下通過物體檢測來檢測垃圾:
- 瓶子(玻璃和塑料)
- 罐(金屬)
- 包裝(塑料、紙、紙板等)
Edge Impulse 還可以在連接到 Raspberry Pi 后提供實(shí)時(shí)視頻流。因此,無需創(chuàng)建網(wǎng)絡(luò)攝像頭服務(wù)器 (Motion) 即可使用 Raspberry Pi 為該項(xiàng)目進(jìn)行直播。
2 主要器件
- 樹莓派4
- RPLIDAR A1M8 360 度激光雷達(dá)
- Arduino Nano
- DFRobot Black Gladiator
- SSD1306 OLED 屏幕(128x32)
3 實(shí)現(xiàn)效果
4 設(shè)計(jì)原理
1 在 Edge Impulse 中構(gòu)建平衡良好的數(shù)據(jù)集
Edge Impulse是一個(gè)免費(fèi)的嵌入式機(jī)器學(xué)習(xí)開發(fā)平臺(tái),供開發(fā)人員(新手或?qū)<?#xff09;從學(xué)習(xí)到部署。它具有許多功能和內(nèi)置神經(jīng)網(wǎng)絡(luò)模型,可滿足各種要求,例如用于多目標(biāo)檢測的遷移學(xué)習(xí)。此外,該平臺(tái)還提供來自連接設(shè)備攝像頭的實(shí)時(shí)視頻流。因此,本項(xiàng)目使用 Edge Impulse 來識(shí)別和監(jiān)控垃圾。
在構(gòu)建用于物體檢測的神經(jīng)網(wǎng)絡(luò)模型之前,需要?jiǎng)?chuàng)建一個(gè)平衡良好的數(shù)據(jù)集來檢測多個(gè)垃圾類別:
瓶子(玻璃和塑料)
罐(金屬)
包裝(塑料、紙、紙板等)
通過精心挑選與上述垃圾類別相關(guān)的最合適的圖像,結(jié)合了廢物和垃圾的兩個(gè)不同數(shù)據(jù)集:
垃圾分類數(shù)據(jù)集
TACO 垃圾數(shù)據(jù)集
選擇后,每個(gè)類別大約有100張圖像,總共292張。通常,像這樣的小數(shù)據(jù)集無法在垃圾檢測中得到準(zhǔn)確的結(jié)果。然而,Edge Impulse 在訓(xùn)練模型時(shí)采用了遷移學(xué)習(xí),所以得到了相當(dāng)不錯(cuò)的結(jié)果,而且準(zhǔn)確率很高。
首先,注冊(cè)Edge Impulse并創(chuàng)建一個(gè)新項(xiàng)目(垃圾檢測機(jī)器人)。
為了能夠使用對(duì)象檢測模型,請(qǐng)轉(zhuǎn)到儀表板 ? 項(xiàng)目信息 ? 標(biāo)簽方法并選擇Bounding box (object detection) 。
然后,轉(zhuǎn)到數(shù)據(jù)獲取并選擇上傳數(shù)據(jù)(上傳現(xiàn)有數(shù)據(jù))。
成功上傳垃圾數(shù)據(jù)集后,用提到的三個(gè)垃圾類別標(biāo)記每個(gè)圖像 -瓶子、罐頭、包裝。在 Edge Impulse 中,標(biāo)記一個(gè)對(duì)象,類似在它周圍拖動(dòng)一個(gè)框并輸入一個(gè)標(biāo)簽一樣簡單。此外,Edge Impulse 在標(biāo)記對(duì)象時(shí)在后臺(tái)運(yùn)行跟蹤算法,因此它會(huì)自動(dòng)為不同圖像中的相同對(duì)象移動(dòng)框。
?轉(zhuǎn)至數(shù)據(jù)獲取?標(biāo)簽隊(duì)列(Object detection labeling)。它顯示了數(shù)據(jù)集中剩余的所有未標(biāo)記圖像。
?然后,選擇一個(gè)未標(biāo)記的圖像,拖動(dòng)框,單擊Save labels ,然后重復(fù)此操作,直到整個(gè)數(shù)據(jù)集都被標(biāo)記。
完成標(biāo)記后,可以看到在數(shù)據(jù)采集下列出了一個(gè)平衡良好的數(shù)據(jù)集,用于垃圾檢測。
2 在 Edge Impulse 中設(shè)計(jì)脈沖(神經(jīng)網(wǎng)絡(luò)模型)
脈沖是邊緣脈沖中的自定義神經(jīng)網(wǎng)絡(luò)模型。在這個(gè)項(xiàng)目中,設(shè)計(jì)了一個(gè)脈沖,它獲取原始圖像數(shù)據(jù),調(diào)整圖像大小,使用預(yù)處理塊來處理圖像,然后利用學(xué)習(xí)塊對(duì)新數(shù)據(jù)進(jìn)行分類:
圖像預(yù)處理塊 ? 取彩色圖像中的數(shù)據(jù),可選地使圖像灰度化,然后將數(shù)據(jù)轉(zhuǎn)化為特征數(shù)組。
遷移學(xué)習(xí)學(xué)習(xí)塊 -對(duì)象檢測(圖像)? 接收所有圖像并學(xué)習(xí)區(qū)分三種(瓶子、罐子、包裝)垃圾類別。
預(yù)處理塊總是為相同的輸入返回相同的值(例如,將彩色圖像轉(zhuǎn)換為灰度圖像),而學(xué)習(xí)塊則從過去的經(jīng)驗(yàn)中學(xué)習(xí)。除了內(nèi)置的預(yù)處理塊,Edge Impulse 還允許用戶創(chuàng)建自定義預(yù)處理塊(步驟)。
?進(jìn)入創(chuàng)建脈沖,設(shè)置圖像寬度和圖像高度為320,調(diào)整大小模式為適合最短軸。然后,添加圖像和對(duì)象檢測(圖像)塊。最后,單擊Save Impulse 。
配置處理塊和功能
? 要配置處理塊,請(qǐng)轉(zhuǎn)到Impulse design下的Image ,選擇顏色深度為RGB ,然后單擊Save parameters 。處理塊為模型適當(dāng)?shù)馗袷交紙D像數(shù)據(jù)。
然后,在特征生成屏幕上,單擊生成特征以:
- 調(diào)整圖像數(shù)據(jù)大小,
- 將處理塊應(yīng)用于圖像數(shù)據(jù),
- 并創(chuàng)建完整的垃圾數(shù)據(jù)集的 3D 可視化。
3 使用遷移學(xué)習(xí)訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型(脈沖)
它正在努力從頭開始構(gòu)建準(zhǔn)確的計(jì)算機(jī)視覺模型,因?yàn)樵撃P托枰鞣N各樣的輸入數(shù)據(jù)才能很好地泛化,并且在 GPU 上訓(xùn)練此類模型可能需要數(shù)天時(shí)間。然而,Edge Impulse 在訓(xùn)練用于對(duì)象檢測的神經(jīng)網(wǎng)絡(luò)模型時(shí)采用了遷移學(xué)習(xí)。轉(zhuǎn)移學(xué)習(xí)方法重新訓(xùn)練訓(xùn)練有素的神經(jīng)網(wǎng)絡(luò)模型的上層以進(jìn)行對(duì)象檢測,從而產(chǎn)生更可靠的模型,這些模型可以在很短的時(shí)間內(nèi)進(jìn)行訓(xùn)練并使用更小的數(shù)據(jù)集。
盡管遷移學(xué)習(xí)使訓(xùn)練對(duì)象檢測模型變得輕松,但使用機(jī)器學(xué)習(xí)識(shí)別和監(jiān)控垃圾仍然具有挑戰(zhàn)性。由于垃圾種類的顏色、形狀和材料各不相同,因此我從兩個(gè)不同的數(shù)據(jù)集中精心挑選了 292 張最合適的圖像,如前面的步驟所述。處理完我的數(shù)據(jù)集后,我用整個(gè)數(shù)據(jù)集訓(xùn)練模型以區(qū)分三種不同的垃圾類別(瓶子、罐頭、包裝)。
使用數(shù)據(jù)集訓(xùn)練模型后,Edge Impulse 將精度分?jǐn)?shù)(準(zhǔn)確度)評(píng)估為60.2% 。
? 進(jìn)入Impulse design下的Object detection ,選擇默認(rèn)的基礎(chǔ)模型,并設(shè)置:
訓(xùn)練周期數(shù)? 40
學(xué)習(xí)率? 0.01
驗(yàn)證神經(jīng)網(wǎng)絡(luò)模型
由于使用整個(gè)數(shù)據(jù)集來訓(xùn)練模型,因此上傳了新圖像作為測試數(shù)據(jù)集來驗(yàn)證模型。
在驗(yàn)證模型后,推斷它區(qū)分瓶子(塑料和玻璃)和罐子(金屬)的準(zhǔn)確率超過 88%。然而,它很難檢測包裝(塑料、紙、紙板等),因?yàn)榘b在很多方面都不同——形狀、顏色、材料等。由于收集的種類繁多,包裝的準(zhǔn)確度在 45% 到 55% 之間。因此,我仍在收集數(shù)據(jù)以改進(jìn)我的數(shù)據(jù)集和包裝的準(zhǔn)確性。
使用測試數(shù)據(jù)集(大約 50 張圖像),Edge Impulse 評(píng)估模型精度為84.11% 。
? 要驗(yàn)證模型,請(qǐng)轉(zhuǎn)到模型測試并選擇Classify all 。
4 將 Raspberry Pi 4 連接到 Edge Impulse
經(jīng)過設(shè)計(jì)、訓(xùn)練、驗(yàn)證和驗(yàn)證后,剩下的就是將模型部署到了樹莓派 4。由于 Edge Impulse 官方支持樹莓派 4,因此使用此開發(fā)板部署和運(yùn)行模型非常簡單。
?首先,打開終端,運(yùn)行以下命令安裝依賴和模塊:
安裝依賴項(xiàng)后,將USB網(wǎng)絡(luò)攝像頭連接到Raspberry Pi 4并運(yùn)行以下命令:邊緣脈沖Linux
然后,登錄并使用終端向?qū)нx擇一個(gè)Edge Impulse項(xiàng)目(垃圾檢測機(jī)器人)。
要驗(yàn)證樹莓派 4 是否成功連接到所選 Edge Impulse 項(xiàng)目,請(qǐng)轉(zhuǎn)到項(xiàng)目頁面并單擊Devices 。
5 在 Raspberry Pi 4 上部署和運(yùn)行模型
? 要在 Raspberry Pi 4 本地部署和運(yùn)行模型,請(qǐng)打開終端并在下面輸入以下命令:
edge-impulse-linux-runner
? 然后,Edge Impulse 會(huì)自動(dòng)編譯具有完整硬件加速的模型并將其下載到 Raspberry Pi 4。在這方面,該模型以最小的延遲和功耗運(yùn)行。
在 Raspberry Pi 4 上部署模型后,將它和 USB 網(wǎng)絡(luò)攝像頭連接到機(jī)器人底盤。就可運(yùn)行模型,就可以在三個(gè)垃圾類別之間進(jìn)行分類。
瓶子(玻璃和塑料)
罐(金屬)
包裝(塑料、紙、紙板等)
由于 Edge Impulse 在模型運(yùn)行時(shí)提供帶有來自連接的網(wǎng)絡(luò)攝像頭的分類結(jié)果的實(shí)時(shí)視頻流,因此當(dāng)垃圾檢測機(jī)器人運(yùn)行時(shí),不需要?jiǎng)?chuàng)建帶有 Motion 或其他模塊的網(wǎng)絡(luò)攝像頭服務(wù)器來顯示分類結(jié)果。
? 要顯示實(shí)時(shí)視頻流和分類結(jié)果,直接在運(yùn)行模型后轉(zhuǎn)到終端中給定的 URL:
6 在 Raspberry Pi 4 上設(shè)置 RPLIDAR A1M8 360 度激光雷達(dá)
為了讓垃圾檢測機(jī)器人自主移動(dòng),使用了 RPLIDAR A1M8-R6 - 360 度激光掃描儀(激光雷達(dá))。該激光雷達(dá)可以在 12 米范圍內(nèi)執(zhí)行 360 度掃描,每秒生成多達(dá) 8000 個(gè)樣本。
沒有繪制環(huán)境來導(dǎo)航機(jī)器人(SLAM),而是使用激光雷達(dá)來檢測三個(gè)不同方向(右、左和前)的障礙物,因?yàn)槲蚁胱寵C(jī)器人在不受環(huán)境(室內(nèi)或室外)任何限制的情況下運(yùn)行)。
為了獲得 RPLIDAR A1M8 和 Raspberry Pi 生成的 360 度掃描數(shù)據(jù),我使用了Adafruit CircuitPython RPLIDAR庫。
在使用庫收集數(shù)據(jù)之前,將 RPLIDAR A1M8 連接到計(jì)算機(jī) (Windows) 并運(yùn)行Frame Grabber應(yīng)用程序以在掃描周圍環(huán)境的同時(shí)檢查角度方向和距離測量值。
7 使用RPLIDAR A1M8檢測障礙物并控制機(jī)器人底盤
RPLIDAR A1M8 不是采用步進(jìn)讀取方法,而是通過直流電機(jī)驅(qū)動(dòng)旋轉(zhuǎn)掃描儀,并根據(jù)掃描儀獲得的角度生成距離讀數(shù)。這樣,單次旋轉(zhuǎn)不能保證為每個(gè)可能的角度(從 0 到 360)產(chǎn)生距離值。只需旋轉(zhuǎn)幾次,即可使用此激光雷達(dá)進(jìn)行完整的 360 度掃描。因此,在不調(diào)試生成的掃描數(shù)據(jù)點(diǎn)的情況下,使用 RPLIDAR A1M8 進(jìn)行避障可能會(huì)非常棘手和困難。
在設(shè)置好RPLIDAR A1M8并包括所需的模塊后,對(duì)掃描儀每轉(zhuǎn)產(chǎn)生的不完整的360度掃描數(shù)據(jù)進(jìn)行調(diào)試和處理,以檢測三個(gè)不同方向的障礙物。對(duì)于每個(gè)方向,定義了一個(gè)起始角和一個(gè)終止角(順時(shí)針):
右 ? 開始:60 ,結(jié)束:120
左 ? 開始:240 ,結(jié)束:300
前 ? 開始:340結(jié)束:20
在每個(gè)方向范圍內(nèi),代碼搜索準(zhǔn)確的距離測量作為起點(diǎn)和終點(diǎn):
起點(diǎn) ? 從起始角到起始角 + 15 ,
終點(diǎn) ? 從終點(diǎn)角度到終點(diǎn)角度 - 15 .
這樣,代碼覆蓋每個(gè)方向的 30 度范圍,以得出準(zhǔn)確的距離測量值(起點(diǎn)和終點(diǎn)),不會(huì)出現(xiàn)錯(cuò)誤或遺漏。然后,如果引出的方向起點(diǎn)和終點(diǎn)小于給定閾值(40 cm),則代碼激活機(jī)器人底盤(L298N)以避開該方向檢測到的障礙物。
在完成代碼并將激光雷達(dá)(RPLIDAR A1M8)安裝到機(jī)器人底盤上后,在外殼上測試了垃圾檢測機(jī)器人的避障系統(tǒng)。
8 使用 Arduino 為機(jī)器人構(gòu)建跌倒檢測系統(tǒng)
完成上述步驟后,為垃圾檢測機(jī)器人添加一個(gè) 6 軸加速度計(jì)作為跌倒檢測系統(tǒng),以防止碰撞。跌倒檢測系統(tǒng)還顯示 X、Y 和 Z 軸的加速度測量值。
下載所需的庫以從 DFRobot 串行 6 軸加速度計(jì)獲取數(shù)據(jù)。
下載控制 SSD1306 OLED 屏幕所需的庫。
為了構(gòu)建機(jī)器人的跌倒檢測系統(tǒng),我將 DFRobot 串行 6 軸加速度計(jì)、SSD1306 OLED 屏幕(128x32)和蜂鳴器連接到 Arduino Nano。為了提供 Arduino Nano,將它連接到 Raspberry Pi 4:
5 部分核心代碼
/*!@file getLightIntensity.ino@Set the frequency of data output by the sensor, read the acceleration, angular velocity, and angle of X, Y, and Z axes.@n Experimental phenomenon: when the sensor starts, it outputs data at the set frequency and the data will be displayed on serial monitor@copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)@licence The MIT License (MIT)@author [huyujie](yujie.hu@dfrobot.com)@version V1.0@date 2020-12-03@https://github.com/DFRobot */ #include <DFRobot_WT61PC.h> #include <SoftwareSerial.h>//Use software serial port RX:10,TX:11 SoftwareSerial mySerial(10, 11);DFRobot_WT61PC sensor(&mySerial);void setup() {//Use Serial as debugging serial port Serial.begin(115200);//Use software serial port mySerial as communication seiral port mySerial.begin(9600);//Revise the data output frequncy of sensor FREQUENCY_0_1HZ for 0.1Hz, FREQUENCY_0_5HZ for 0.5Hz, FREQUENCY_1HZ for 1Hz, FREQUENCY_2HZ for 2Hz, // FREQUENCY_5HZ for 5Hz, FREQUENCY_10HZ for 10Hz, FREQUENCY_20HZ for 20Hz, FREQUENCY_50HZ for 50Hz, // FREQUENCY_100HZ for 100Hz, FREQUENCY_125HZ for 125Hz, FREQUENCY_200HZ for 200Hz.sensor.modifyFrequency(FREQUENCY_10HZ); }void loop() {if (sensor.available()) {Serial.print("Acc\t"); Serial.print(sensor.Acc.X); Serial.print("\t"); Serial.print(sensor.Acc.Y); Serial.print("\t"); Serial.println(sensor.Acc.Z); //acceleration information of X,Y,ZSerial.print("Gyro\t"); Serial.print(sensor.Gyro.X); Serial.print("\t"); Serial.print(sensor.Gyro.Y); Serial.print("\t"); Serial.println(sensor.Gyro.Z); //angular velocity information of X,Y,ZSerial.print("Angle\t"); Serial.print(sensor.Angle.X); Serial.print("\t"); Serial.print(sensor.Angle.Y); Serial.print("\t"); Serial.println(sensor.Angle.Z); //angle information of X, Y, Z Serial.println(" ");} } void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {// Pixel is in-bounds. Rotate coordinates if needed.switch (getRotation()) {case 1:ssd1306_swap(x, y);x = WIDTH - x - 1;break;case 2:x = WIDTH - x - 1;y = HEIGHT - y - 1;break;case 3:ssd1306_swap(x, y);y = HEIGHT - y - 1;break;}switch (color) {case SSD1306_WHITE:buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));break;case SSD1306_BLACK:buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));break;case SSD1306_INVERSE:buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));break;}} }/*!@brief Clear contents of display buffer (set all pixels to off).@return None (void).@note Changes buffer contents only, no immediate effect on display.Follow up with a call to display(), or with other graphicscommands as needed by one's own application. */ void Adafruit_SSD1306::clearDisplay(void) {memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); }/*!@brief Draw a horizontal line. This is also invoked by the Adafruit_GFXlibrary in generating many higher-level graphics primitives.@param xLeftmost column -- 0 at left to (screen width - 1) at right.@param yRow of display -- 0 at top to (screen height -1) at bottom.@param wWidth of line, in pixels.@param colorLine color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.@return None (void).@note Changes buffer contents only, no immediate effect on display.Follow up with a call to display(), or with other graphicscommands as needed by one's own application. */ void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w,uint16_t color) {bool bSwap = false;switch (rotation) {case 1:// 90 degree rotation, swap x & y for rotation, then invert xbSwap = true;ssd1306_swap(x, y);x = WIDTH - x - 1;break;case 2:// 180 degree rotation, invert x and y, then shift y around for height.x = WIDTH - x - 1;y = HEIGHT - y - 1;x -= (w - 1);break;case 3:// 270 degree rotation, swap x & y for rotation,// then invert y and adjust y for w (not to become h)bSwap = true;ssd1306_swap(x, y);y = HEIGHT - y - 1;y -= (w - 1);break;}if (bSwap)drawFastVLineInternal(x, y, w, color);elsedrawFastHLineInternal(x, y, w, color); }/*!@brief Draw a horizontal line with a width and color. Used by publicmethods drawFastHLine,drawFastVLine@param xLeftmost column -- 0 at left to (screen width - 1) at right.@param yRow of display -- 0 at top to (screen height -1) at bottom.@param wWidth of line, in pixels.@param colorLine color, one of: SSD1306_BLACK, SSD1306_WHITE orSSD1306_INVERSE.@return None (void).@note Changes buffer contents only, no immediate effect on display.Follow up with a call to display(), or with other graphicscommands as needed by one's own application. */ void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w,uint16_t color) {if ((y >= 0) && (y < HEIGHT)) { // Y coord in bounds?if (x < 0) { // Clip leftw += x;x = 0;}if ((x + w) > WIDTH) { // Clip rightw = (WIDTH - x);}if (w > 0) { // Proceed only if width is positiveuint8_t *pBuf = &buffer[(y / 8) * WIDTH + x], mask = 1 << (y & 7);switch (color) {case SSD1306_WHITE:while (w--) {*pBuf++ |= mask;};break;case SSD1306_BLACK:mask = ~mask;while (w--) {*pBuf++ &= mask;};break;case SSD1306_INVERSE:while (w--) {*pBuf++ ^= mask;};break;}}} }/*!@brief Draw a vertical line. This is also invoked by the Adafruit_GFXlibrary in generating many higher-level graphics primitives.@param xColumn of display -- 0 at left to (screen width -1) at right.@param yTopmost row -- 0 at top to (screen height - 1) at bottom.@param hHeight of line, in pixels.@param colorLine color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.@return None (void).@note Changes buffer contents only, no immediate effect on display.Follow up with a call to display(), or with other graphicscommands as needed by one's own application. */ void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h,uint16_t color) {bool bSwap = false;switch (rotation) {case 1:// 90 degree rotation, swap x & y for rotation,// then invert x and adjust x for h (now to become w)bSwap = true;ssd1306_swap(x, y);x = WIDTH - x - 1;x -= (h - 1);break;case 2:// 180 degree rotation, invert x and y, then shift y around for height.x = WIDTH - x - 1;y = HEIGHT - y - 1;y -= (h - 1);break;case 3:// 270 degree rotation, swap x & y for rotation, then invert ybSwap = true;ssd1306_swap(x, y);y = HEIGHT - y - 1;break;}if (bSwap)drawFastHLineInternal(x, y, h, color);elsedrawFastVLineInternal(x, y, h, color); }/*!@brief Draw a vertical line with a width and color. Used by public methoddrawFastHLine,drawFastVLine@param xLeftmost column -- 0 at left to (screen width - 1) at right.@param __yRow of display -- 0 at top to (screen height -1) at bottom.@param __h height of the line in pixels@param colorLine color, one of: SSD1306_BLACK, SSD1306_WHITE orSSD1306_INVERSE.@return None (void).@note Changes buffer contents only, no immediate effect on display.Follow up with a call to display(), or with other graphicscommands as needed by one's own application. */ void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y,int16_t __h, uint16_t color) {if ((x >= 0) && (x < WIDTH)) { // X coord in bounds?if (__y < 0) { // Clip top__h += __y;__y = 0;}if ((__y + __h) > HEIGHT) { // Clip bottom__h = (HEIGHT - __y);}if (__h > 0) { // Proceed only if height is now positive// this display doesn't need ints for coordinates,// use local byte registers for faster jugglinguint8_t y = __y, h = __h;uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x];// do the first partial byte, if necessary - this requires some maskinguint8_t mod = (y & 7);if (mod) {// mask off the high n bits we want to setmod = 8 - mod;// note - lookup table results in a nearly 10% performance// improvement in fill* functions// uint8_t mask = ~(0xFF >> mod);static const uint8_t PROGMEM premask[8] = {0x00, 0x80, 0xC0, 0xE0,0xF0, 0xF8, 0xFC, 0xFE};uint8_t mask = pgm_read_byte(&premask[mod]);// adjust the mask if we're not going to reach the end of this byteif (h < mod)mask &= (0XFF >> (mod - h));switch (color) {case SSD1306_WHITE:*pBuf |= mask;break;case SSD1306_BLACK:*pBuf &= ~mask;break;case SSD1306_INVERSE:*pBuf ^= mask;break;}pBuf += WIDTH;}if (h >= mod) { // More to go?h -= mod;// Write solid bytes while we can - effectively 8 rows at a timeif (h >= 8) {if (color == SSD1306_INVERSE) {// separate copy of the code so we don't impact performance of// black/white write version with an extra comparison per loopdo {*pBuf ^= 0xFF; // Invert bytepBuf += WIDTH; // Advance pointer 8 rowsh -= 8; // Subtract 8 rows from height} while (h >= 8);} else {// store a local value to work withuint8_t val = (color != SSD1306_BLACK) ? 255 : 0;do {*pBuf = val; // Set bytepBuf += WIDTH; // Advance pointer 8 rowsh -= 8; // Subtract 8 rows from height} while (h >= 8);}}if (h) { // Do the final partial byte, if necessarymod = h & 7;// this time we want to mask the low bits of the byte,// vs the high bits we did above// uint8_t mask = (1 << mod) - 1;// note - lookup table results in a nearly 10% performance// improvement in fill* functionsstatic const uint8_t PROGMEM postmask[8] = {0x00, 0x01, 0x03, 0x07,0x0F, 0x1F, 0x3F, 0x7F};uint8_t mask = pgm_read_byte(&postmask[mod]);switch (color) {case SSD1306_WHITE:*pBuf |= mask;break;case SSD1306_BLACK:*pBuf &= ~mask;break;case SSD1306_INVERSE:*pBuf ^= mask;break;}}}} // endif positive height} // endif x in bounds }/*!@brief Return color of a single pixel in display buffer.@param xColumn of display -- 0 at left to (screen width - 1) at right.@param yRow of display -- 0 at top to (screen height -1) at bottom.@return true if pixel is set (usually SSD1306_WHITE, unless display invertmode is enabled), false if clear (SSD1306_BLACK).@note Reads from buffer contents; may not reflect current contents ofscreen if display() has not been called. */ bool Adafruit_SSD1306::getPixel(int16_t x, int16_t y) {if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {// Pixel is in-bounds. Rotate coordinates if needed.switch (getRotation()) {case 1:ssd1306_swap(x, y);x = WIDTH - x - 1;break;case 2:x = WIDTH - x - 1;y = HEIGHT - y - 1;break;case 3:ssd1306_swap(x, y);y = HEIGHT - y - 1;break;}return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7)));}return false; // Pixel out of bounds }8 最后
總結(jié)
以上是生活随笔為你收集整理的【毕业设计】树莓派智能捡垃圾机器人 - 机器视觉 单片机 物联网的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 vue 创建你的第一个 PWA 应
- 下一篇: CCRC信息安全服务资质--应急处理