2022最新树莓派4B学习笔记:系统备份 OpenCV SPI TFT HTML

2022最新树莓派4B学习笔记:系统备份 OpenCV SPI TFT HTML,第1张

树莓派4B学习笔记 一、首次使用1.系统烧录2.优化配置与备份3.一些基本 *** 作 二、摄像头三、Pi Dashboard好看的仪表盘四、OpenCV安装编译五、SPI_TFT屏幕六、最终代码七、结语

我写学习笔记,总是喜欢长篇,不过我也做了目录,方便直接跳转到想看的地方。
主要都是一些踩过的最新的坑,网上很多教程时间有点久了,不是最新的了,所以记录一下。时间2022.05,硬件是树莓派4B 8G V1.4,32位系统版本发布版是11,代号为 “bullseye”,好多问题解决方案不好找,可以来这里看看有没有能解决问题的灵感。

一、首次使用

包含系统烧录、配置优化等内容

1.系统烧录

推荐使用官方32位镜像,国内资源多。树莓派4B 8G内存版本,虽然说用64位系统性能更好,但是目前教程和资源不够多,有时候也会有一些指令和通常的树莓派不同,新手入门不建议64位系统。

首先从官网下载系统,推荐选择Raspberry Pi OS with desktop https://www.raspberrypi.com/software/operating-systems/

内存卡推荐使用8G卡容量足够,第一次不推荐16G或者32G,因为容易经常备份或者重烧系统,8G方便备份。或者如果自己掌握了更好的备份方法,就无所谓了,比如用DG备份。

同时也从官网下载 Raspberry Pi Imager,推荐用这个烧录系统到TF卡,比网上其它的教程方便多了,按照我使用的树莓派4B来看,用Raspberry Pi Imager配置的SSH和WIFI都是有效的,可以无屏幕启动,其余教程的都无效。
https://www.raspberrypi.com/software/

开机:等待putty的SSH能连上配置:$ sudo raspi-config,显存设置128MB足够不要盲目加大,开启VNC解决VNC不显示:$ sudo nano /boot/config.txt 进入编辑,取消 hdmi_force_hotplug = 1 的注释,解决不接显示器就不显示的问题 2.优化配置与备份

备份真的特别有必要,否则遇到问题真的浪费时间。亲身经历,编译过3次OpenCV,都是因为自己配置原因,用着用着系统挂了或者文件系统出问题,导致重烧系统重新编译,不过也有好处,现在编译OpenCV可太熟练了,哈哈。

我们可以输入$ lsb_release -a查看系统代码,我这是bullseye
换国内源(bullseye千万别换国内源,要不然后悔)
$ sudo nano /etc/apt/sources.list
$ sudo nano /etc/apt/sources.list.d/raspi.list
//替换成下面的国内源
//http://mirrors.ustc.edu.cn/raspbian/raspbian/
//http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/
$ sudo apt-get update
$ sudo apt-get upgrade
安装花生壳内网穿透
花生壳官网:https://hsk.oray.com/download/
//新建个temp文件夹并下载:
$ mkdir temp
$ cd temp
$ wget "https://dl-cdn.oray.com/hsk/linux/phddns_5.1.0_rapi_armhf.deb" -O phddns_5.1.0_rapi_armhf.deb
//安装:
$ sudo dpkg -i phddns_5.1.0_rapi_armhf.deb
//卸载命令:
$ sudo dpkg -r phddns
//查询状态:$ phddns会显示指令列表,如查询状态
$ phddns status
//安装成功后显示SN码,用花生壳管理APP绑定即可
//开机启动:
$ sudo nano /etc/rc.local
//加入定时启动命令,打开
$ sudo nano /etc/crontab	//加入30 01 * * * root phddns start
安装散热风扇
用S8050三极管和GPIO14来控制风扇,可以在GPIO-B基极之间加100-1KΩ电阻。
树莓派5V引脚----风扇红线
风扇黑线--------C集电极
E发射极---------树莓派GND
B基极-----------树莓派GPIO14
系统备份与还原
在windows电脑上,使用Win32DiskImager读取镜像备份。(所以之前推荐8G内存卡)
或者用DiskGenius,①创建img文件;②参照TF卡的分区参数,创建img文件的boot和rootfs分区,boot分区保证大小一致,分区参数、文件格式、扇区等完全一致;③ 复制分区,TF卡的分区复制到img文件的分区;④如果这个镜像要烧录到别的TF卡,记得在windows环境下修改新TF卡的partuuid,具体方法百度。使其和镜像文件中cmdline.txt中的ID一致。⑤烧录,可以使用Win32ImgManager烧录img文件到TF卡。 3.一些基本 *** 作

第一次接触linux系统,文件 *** 作还不是很熟悉,记了一下给自己看的,随时复习。

//重启
$ sudo reboot
//关机
$ sudo shutdown -h now
//查看温度
$ vcgencmd measure_temp
//查看磁盘
$ sudo lsblk
$ df -h
$ ls
//查看已经安装的软件
$ dpkg -l

//文件 *** 作命令,如果权限不足,加sudo
//创建文件夹命令
$ mkdir
//查看当前文件夹下的内容命令
$ ls
//删除文件夹命令
$ rm
$ rm -rf //强制删除,不需要确认
//移动命令
$ mv
//复制命令
$ cp
二、摄像头

摄像头开启

//如果驱动正确,会返回supported=1 detected=1
$ vcgencmd get_camera

//拍照
$ raspistill -t 500 -o image.jpg

//视频串流广播:
$ sudo apt-get install vlc
$ sudo raspivid -o - -rot 0 -t 0 -fps 30|cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8080}' :demux=h264  
//使用vlc或者potplayer打开视频网址:http://192.168.31.13:8080
三、Pi Dashboard好看的仪表盘

这个建议大家装一个,方便查看树莓派状态。
项目地址:https://make.quwj.com/project/10
教程也特别多,注意分辨选择可用的php版本就行。
装好nginx和php后,也可以自己做好看的主页,html在B站有很多好看的教程,有一段时间B站疯狂给我推荐。

//安装nginx
$ sudo apt-get update
$ sudo apt-get install nginx

//查看自己的PHP版本列表,如果有7.4就安装7.4,要不然安装7.3会报错
$ sudo apt-cache search php
//安装PHP
$ sudo apt-get install php7.4-fpm php7.4-cli php7.4-curl php7.4-gd php7.4-cgi

//启动nginx,PHP
$ sudo service nginx start
$ sudo service php7.4-fpm restart

//下载git,pi Dashboard,赋予权限
$ sudo apt-get install git
$ cd /var/www/html
$ sudo git clone https://github.com/spoonysonny/pi-dashboard.git
$ sudo chown -R www-data pi-dashboard

//修改下nginx配置,配置location 
$ sudo nano /etc/nginx/sites-available/default
//把里面的location换掉
//也可以把默认的80端口换成自己想要的,比如7200
location / {
index  index.html index.htm index.php default.html default.htm default.php;
}
location ~\.php$ {
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
//同时我还建议,在前面的index里面加上default.html,方便进入主页
index index.html index.htm index.nginx-debian.html;//修改前
index index.html index.htm default.html index.nginx-debian.html;//修改后
//然后复制一份index.nginx-debian.html为index.html
$ sudo cp /var/www/html/index.nginx-debian.html /var/www/html/index.html

//nginx重启下
$ sudo service nginx restart
//进入主页,IP记得改,前面增加的default.html就是为了IP地址不加后缀就能进入nginx主页,会显示欢迎信息
http://192.168.1.123:7200
//进入Pi Dashboard网页,IP记得改
http://192.168.1.123:7200/pi-dashboard/
四、OpenCV安装编译

原教程地址,教程是最近2021年11月的,完美解决了编译过程中的各种报错,感谢大神。大神编译的是opencv-4.0.0,我编译的是opencv-4.5.5,最新版的OpenCV编译起来,问题会相对较少一点,经验证的一点是不会报GCC编译错误。
报错如下:
c++: error: unrecognized command-line option ‘–param=ipcp-unit-growth=100000’; did you mean ‘–param=ipa-cp-unit-growth=’?

树莓派4B8G内存版本,还是很给力的,熟练的情况下,基本2-3小时搞定,并且8G内存够用,不要扩展虚拟内存。
https://zhuanlan.zhihu.com/p/435583767
特别强调,不要换源,当前树莓派系统版本发布版是11,代号为 “bullseye”,目前国内的Raspbian镜像版本要低于这个版本,不要切换源,否则一堆问题。

编译完成后,使用教程参照如下:
https://shumeipai.nxez.com/2018/03/09/real-time-face-recognition-an-end-to-end-project-with-raspberry-pi.html
一些DEMO程序可以从github下载,注意天然有一些python缩进语法错误,自己纠正。
https://github.com/Mjrovai/OpenCV-Face-Recognition

五、SPI_TFT屏幕

教程特别多,只特别强调一点,一定要用硬件SPI的引脚,否则用其它引脚,工作是可以工作,但是刷新一次要一秒钟,真的糟心。硬件SPI引脚,至少能到30FPS

//安装WiringPi库
$ cd ~/temp
$ wget https://project-downloads.drogon.net/wiringpi-latest.deb
$ sudo dpkg –i wiringpi-latest.deb
//检查安装
$ gpio –v
$ gpio readall

//python版本
//注意要用sudo否则程序启动时可能import失败
$ sudo pip3 install wiringpi
//安装spidev
$ sudo pip3 install spidev

//开启SPI并接线
//在配置中打开SPI功能,接线如下。一定要用原生接口,否则速率特别慢
//使用的时候设置
GPIO.setmode(GPIO.BOARD)
//然后只要设置两个引脚
//GPIO.BOARD引脚模式,第29号引脚
PinDC = 29
//GPIO.BOARD引脚模式,第16号引脚
PinReset = 16

六、最终代码

写了一个OpenCV人脸识别并在TFT屏幕上显示实时图像的python程序,算是对以上所有学习的一个总结吧。

''''
python程序
'''
# coding : UTF-8
import time #用于计算spi刷新整个屏幕所用时长
import RPi.GPIO as GPIO #用于 *** 作引脚
import spidev #树莓派与屏幕的交互协议为SPI,说明见:https://github.com/doceme/py-spidev
from PIL import Image, ImageFont, ImageDraw #用于创建画布,或者读取具体路径下的图片。给图片添加文字。

import cv2
import numpy as np
import os
import datetime
import threading

recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer/trainer.yml')
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath);
font = cv2.FONT_HERSHEY_SIMPLEX
id = 0
names = ['None', 'name1', 'name2', 'X', 'Y', 'Z'] 
cam = cv2.VideoCapture(0)
cam.set(3, 160) # set video widht
cam.set(4, 128) # set video height
minW = 0.1*cam.get(3)
minH = 0.1*cam.get(4)

screenWidth = 160 #屏幕长度
screenHeight = 128 #屏幕宽度
PinDC = 29 #GPIO.BOARD引脚模式,第29号引脚
PinReset = 16  #GPIO.BOARD引脚模式,第16号引脚

count = 0
count2 = 0
FPS = 0

def hardReset(): #重置电平时序
    GPIO.output(PinReset, 0)
    time.sleep(.2)
    GPIO.output(PinReset, 1)
    time.sleep(.5)

def sendCommand(command, *bytes): #发送指令(DC为低电平)和数据(DC为高电平)
    GPIO.output(PinDC, 0)
    spi.writebytes([command])
    if len(bytes) > 0:
        GPIO.output(PinDC, 1)
        spi.writebytes(list(bytes))

def reset(): #屏幕初始化
    sendCommand(0x11);
    sendCommand(0x26, 0x04);  # Set Default Gamma
    sendCommand(0xB1, 0x0e, 0x10);  # Set Frame Rate
    sendCommand(0xC0, 0x08, 0x00);  # Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD
    sendCommand(0xC1, 0x05);  # Set BT[2:0] for AVDD & VCL & VGH & VGL
    sendCommand(0xC5, 0x38, 0x40);  # Set VMH[6:0] & VML[6:0] for VOMH & VCOML
    sendCommand(0x3a, 0x05);  # Set Color Format
    sendCommand(0x36, 0xc8);  # RGB
    sendCommand(0x2A, 0x00, 0x00, 0x00, 0x7F);  # Set Column Address
    sendCommand(0x2B, 0x00, 0x00, 0x00, 0x9F);  # Set Page Address
    sendCommand(0xB4, 0x00);
    sendCommand(0xf2, 0x01);  # Enable Gamma bit
    sendCommand(0xE0, 0x3f, 0x22, 0x20, 0x30, 0x29, 0x0c, 0x4e, 0xb7, 0x3c, 0x19, 0x22, 0x1e, 0x02, 0x01, 0x00);
    sendCommand(0xE1, 0x00, 0x1b, 0x1f, 0x0f, 0x16, 0x13, 0x31, 0x84, 0x43, 0x06, 0x1d, 0x21, 0x3d, 0x3e, 0x3f);
    sendCommand(0x29);  # Display On
    sendCommand(0x2C);

def sendManyBytes(bytes): #发送屏幕数据
    GPIO.output(PinDC, 1)
    spi.writebytes(bytes)

def drawImg(img160x128): #入参为160x128像素的image对象
    picReadStartTime = time.time()
    bytes = []
    i = 0  
    for x in range(0, screenWidth):
        for y in range(screenHeight - 1, -1, -1):
            colorValue = img160x128.getpixel((x, y))
            red = colorValue[0]
            green = colorValue[1]
            blue = colorValue[2]
            red = red >> 3;  # st7735s的红色占5位
            green = green >> 2;  # st7735s的绿色占6位
            blue = blue >> 3;  # st7735s的蓝色占5位
            highBit = 0 | (blue << 3) | (green >> 3);  # 每个像素写入个字节,highBit高字节,lowBit低字节
            lowBit = 0 | (green << 5) | red;
            bytes.append(highBit)
            bytes.append(lowBit)
    picReadTimeConsuming = time.time() - picReadStartTime
    startTime = time.time()
    
    # screenWidth*screenHeight*2 每个像素写入个字节。以下for循环是为了控制每次传入的数组长度,防止这个报错,:OverflowError: Argument list size exceeds 4096 bytes.
    for j in range(2000, screenWidth * screenHeight * 2, 2000):  
        sendManyBytes(bytes[i:j])
        i = i + 2000
    sendManyBytes(bytes[i:screenWidth * screenHeight * 2])
    SpiTimeConsuming = time.time() - startTime
    print("picReadTimeConsuming = %.3fs , SpiTimeConsuming = %.3fs" % (picReadTimeConsuming, SpiTimeConsuming))

def TFTThread():
    while True:
        if(count2 == 1):
            #TFT从内存/dev/中读取OpenCV储存的图片并显示,因为不会把OpenCV的图片直接转成TFT能现实的,所以我用得这种笨方法。
            cv2.imwrite("/dev/cv2temp.jpg", img[0:128,0:160])
            image = Image.open("/dev/cv2temp.jpg")
            image = image.convert('RGBA');
            drawImg(image)
            #print('thread0')
            time.sleep(0.05)
            #print('thread0.05')
        if(count2 == 100):
            print('thread1 end')
            #退出线程也是用的这种笨方法,哈哈。
            break
    
if __name__ == '__main__':
    try:
        GPIO.setmode(GPIO.BOARD)
        GPIO.setwarnings(False)
        GPIO.setup(PinDC, GPIO.OUT)
        GPIO.setup(PinReset, GPIO.OUT)

        spi = spidev.SpiDev() #https://github.com/doceme/py-spidev
        spi.open(0, 0) 
        spi.max_speed_hz = 24000000 #通信时钟最大频率
        spi.mode = 0x00 #SPI的模式,ST7735S为模式0
        hardReset()
        reset()
        
        img =cv2.imread('trainer/logo.jpg') #logo
        
        thread1 = threading.Thread(target = TFTThread)
        thread1.start()
        
        while True:
            if(count == 0 ):
                time1 = datetime.datetime.now().second*1000+datetime.datetime.now().microsecond/1000
            ret, img =cam.read()
            img = cv2.flip(img, -1) # Flip vertically
            gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
            faces = faceCascade.detectMultiScale( 
                gray,
                scaleFactor = 1.2,
                minNeighbors = 5,
                minSize = (int(minW), int(minH)),
            )

            for(x,y,w,h) in faces:
                cv2.rectangle(img, (x,y), (x+w,y+h), (255,125,0), 2)
                id, confidence = recognizer.predict(gray[y:y+h,x:x+w])
                # Check if confidence is less them 100 ==> "0" is perfect match 
                if (confidence < 100):
                    id = names[id]
                    confidence = "  {0}%".format(round(100 - confidence))
                else:
                    id = "unknown"
                    confidence = "  {0}%".format(round(100 - confidence))
        
                cv2.putText(img, str(id), (x+5,y-5), font, 0.5, (255,255,255), 2)
                cv2.putText(img, str(confidence), (x+5,y+h-5), font, 0.5, (255,255,0), 1)
    
            count += 1
            count2 += 1
            if(count >= 10):
                count = 0
                time2 = datetime.datetime.now().second*1000+datetime.datetime.now().microsecond/1000
                FPS = int(10000/(time2-time1))
            cv2.putText(img, "FPS:"+str(FPS), (0,15), font, 0.5, (255,0,0), 2)
            if(count2 >= 2):
                count2 = 0
                #在这里触发线程TFTThread()
                
            cv2.imshow('camera',img)
            
            k = cv2.waitKey(10) & 0xff # Press 'ESC' for exiting video
            if k == 27:
                count2 = 100
                thread1.join()
                break

    except KeyboardInterrupt:
        pass

    # 清理GPIO口
    #GPIO.cleanup()
    print("\n [INFO] Exiting Program and cleanup stuff")
    cam.release()
    cv2.destroyAllWindows()
七、结语

树莓派是智能小车带的,单独拿出来玩,感觉比小车好玩。
耐不住最近树莓派涨价的厉害,作死把我的树莓派卖了,赚了二三十,还白嫖了一辆4WD小车的硬件。
然后从网上又淘了2块NanoPi S2,板子挺不错的,就是教程很少,接下来有的折腾了,我真是自作自受。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/926809.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存