我写学习笔记,总是喜欢长篇,不过我也做了目录,方便直接跳转到想看的地方。
主要都是一些踩过的最新的坑,网上很多教程时间有点久了,不是最新的了,所以记录一下。时间2022.05,硬件是树莓派4B 8G V1.4,32位系统版本发布版是11,代号为 “bullseye”,好多问题解决方案不好找,可以来这里看看有没有能解决问题的灵感。
包含系统烧录、配置优化等内容
推荐使用官方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/
备份真的特别有必要,否则遇到问题真的浪费时间。亲身经历,编译过3次OpenCV,都是因为自己配置原因,用着用着系统挂了或者文件系统出问题,导致重烧系统重新编译,不过也有好处,现在编译OpenCV可太熟练了,哈哈。
$ 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
//新建个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
树莓派5V引脚----风扇红线
风扇黑线--------C集电极
E发射极---------树莓派GND
B基极-----------树莓派GPIO14
第一次接触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
这个建议大家装一个,方便查看树莓派状态。
项目地址: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/
原教程地址,教程是最近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的引脚,否则用其它引脚,工作是可以工作,但是刷新一次要一秒钟,真的糟心。硬件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,板子挺不错的,就是教程很少,接下来有的折腾了,我真是自作自受。