树莓派4B学习笔记
- 一、首次使用
-
- 1.系统烧录
- 2.优化配置和备份
- 3.一些基本操作
- 二、摄像头
- 三、Pi Dashboard好看的仪表盘
- 四、OpenCV安装编译
- 五、SPI_TFT屏幕
- 六、最终代码
- 七、结语
我总是喜欢写学习笔记,但我也做了一个目录,方便直接跳到我想看的地方。 主要是踩过的最新坑。网上很多教程时间有点长,不是最新的,记录下来。时间2022.硬件是树莓派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/
建议使用8张内存卡G卡容量足够,第一次不推荐16G或者32G,由于容易经常备份或重烧系统,8G备份方便。或者如果你掌握了更好的备份方法,就没关系了,比如用DG备份。
同时也从官网下载 ,推荐使用此烧录系统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.优化配置和备份
备份真的很有必要,否则遇到问题真的是浪费时间。亲身经历,编译三次OpenCV,因为他们自己的配置,使用系统挂或文件系统问题,导致重燃系统重新编译,但也有好处,现在编译OpenCV可太熟练了,哈哈。
- 我们可以输入$ lsb_release -a查看系统代码,我是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