树莓派,opencv,Picamera2利用舵机云台追踪人脸(PID控制)

news2024/9/28 23:25:25

一、需要准备的硬件

  1. Raspiberry Pi 4b
  2. 两个SG90 180度舵机(注意舵机的角度,最好是180度且带限位的,切勿选360度舵机)
  3. 二自由度舵机云台(如下图)
  4. Raspiberry CSI 摄像头
    组装后的效果:
    组装后的效果

二、项目目标

追踪人脸:
当人脸移动时,摄像头通过控制两个伺服电机(分别是偏航和俯仰)把该人脸放到视界的中心位置,本文采用了PID控制伺服电机

三、具体步骤

3.1 下载用于人脸识别的级联分类器

下载级联分类器“haarcascade_frontalface_default.xml”,下载地址:haarcascade_frontalface_default.xml
下载完成后将其与后面的所有文件放到同一目录中。

3.2人脸追踪代码

  1. 创建文件“face_tracking_PID.py” ,代码如下:
#face_tracking_PID.py
#-*- coding: UTF-8 -*-	
# 调用必需库
from multiprocessing import Manager
from multiprocessing import Process
from objcenter import ObjCenter
from pid import PID
from servo import Servo
import argparse
import signal
import time
import sys
import cv2
from picamera2 import Picamera2


# 定义舵机
pan=Servo(pin=19)
tilt=Servo(pin=16)

#定义图像尺寸
dispW=1280
dispH=720

      
# 键盘终止函数
def signal_handler(sig, frame):
    # 输出状态信息
    print("[INFO] You pressed `ctrl + c`! Exiting...")

    # 关闭舵机
    pan.stop()
    tilt.stop()

    # 退出
    sys.exit()

def obj_center(args, objX, objY, centerX, centerY):
    # ctrl+c退出进程
    signal.signal(signal.SIGINT, signal_handler)

    # 启动视频流并缓冲
    print("[INFO] waiting for camera to warm up...")
    cv2.startWindowThread()
    picam2 = Picamera2()
    picam2.preview_configuration.main.size = (dispW,dispH)
    picam2.preview_configuration.main.format = "RGB888"
    picam2.preview_configuration.controls.FrameRate=10
    picam2.preview_configuration.align()
    picam2.configure("preview")
    picam2.start()
    time.sleep(2.0)

    # 初始化人脸中心探测器
    obj = ObjCenter(args["cascade"])
    # 进入循环
    while True:

        # 从视频流抓取图像并旋转
        frame= picam2.capture_array()
        frame = cv2.flip(frame, 1)

        # 找到图像中心
        (H, W) = frame.shape[:2]
        centerX.value = W // 2
        centerY.value = H // 2

        #draw a point in the center of frame
        cv2.circle(frame, (centerX.value, centerY.value), 5, (0, 0, 255), -1)

        # 找到人脸中心
        objectLoc = obj.update(frame, (centerX.value, centerY.value))
        ((objX.value, objY.value), rect) = objectLoc
        print("objx.value", objX.value)
        print("objy.value", objY.value)

        # 绘制人脸外界矩形
        if rect is not None:
            (x, y, w, h) = rect
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            fX = int(x + (w / 2.0))
            fY = int(y + (h / 2.0))
            cv2.circle(frame, (fX, fY), 5, (0, 0, 255), -1)
            # 在人脸中心和视窗中心画一条连线
            cv2.line(frame, (centerX.value, centerY.value),
                     (fX, fY), (0, 255, 0), 2)
            # 显示图像
        cv2.imshow("Pan-Tilt Face Tracking", frame)
        cv2.waitKey(1)

def pid_process(output, p, i, d, objCoord, centerCoord):
    # ctrl+c退出进程
    signal.signal(signal.SIGINT, signal_handler)
    
     # 创建一个PID类的对象并初始化
    p = PID(p.value, i.value, d.value)
    p.initialize()

    # 进入循环
    while True:
        # 计算误差
        error = centerCoord.value - objCoord.value
        # 更新输出值,当error小于50时,误差设为0,以避免云台不停运行。
        if abs(error) < 50:
            error = 0
        output.value = p.update(error)


def set_servos(panAngle, tiltAngle):
    # ctrl+c退出进程
    signal.signal(signal.SIGINT, signal_handler)

    #进入循环
    while True:
        # 偏角变号
        yaw = -1 * panAngle.value
        pitch = -1 * tiltAngle.value

        # 设置舵机角度。
        pan.set_angle(yaw)
        tilt.set_angle(pitch)
            

# 启动主程序
if __name__ == "__main__":
    # 建立语法分析器
    ap = argparse.ArgumentParser()
    ap.add_argument("-c", "--cascade", type=str, required=True,help="path to input Haar cascade for face detection")
    args = vars(ap.parse_args())

    # 启动多进程变量管理
    with Manager() as manager: #相当于manager=Manager(),with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源。
        # 舵机角度置零
        pan.set_angle(0)
        tilt.set_angle(0)
        
        # 为图像中心坐标赋初值
        centerX = manager.Value("i", 0) #"i"即为整型integer
        centerY = manager.Value("i", 0)

        # 为人脸中心坐标赋初值
        objX = manager.Value("i", 0)
        objY = manager.Value("i", 0)

        # panAngle和tiltAngle分别是两个舵机的PID控制输出量	    
        panAngle = manager.Value("i", 0)
        tiltAngle = manager.Value("i", 0)

       # 设置一级舵机的PID参数
        panP = manager.Value("f", 0.015)  # "f"即为浮点型float
        panI = manager.Value("f", 0.01)
        panD = manager.Value("f", 0.0008)

        # 设置二级舵机的PID参数
        tiltP = manager.Value("f", 0.025)
        tiltI = manager.Value("f", 0.01)
        tiltD = manager.Value("f", 0.008)

        # 创建4个独立进程
        # 1. objectCenter  - 探测人脸
        # 2. panning       - 对一级舵机进行PID控制,控制偏航角
        # 3. tilting       - 对二级舵机进行PID控制,控制俯仰角
        # 4. setServos     - 根据PID控制的输出驱动舵机
                          
        processObjectCenter = Process(target=obj_center,args=(args, objX, objY, centerX, centerY))
        processPanning = Process(target=pid_process,args=(panAngle, panP, panI, panD, objX, centerX))
        processTilting = Process(target=pid_process,args=(tiltAngle, tiltP, tiltI, tiltD, objY, centerY))
        processSetServos = Process(target=set_servos, args=(panAngle, tiltAngle))

        # 开启4个进程
        processObjectCenter.start()
        processPanning.start()
        processTilting.start()
        processSetServos.start()

        # 添加4个进程
        processObjectCenter.join()
        processPanning.join()
        processTilting.join()
        processSetServos.join()

  1. 创建文件“objcenter.py”,代码如下:
#objcenter.py
#-*- coding: UTF-8 -*-
# 调用必需库
import cv2

class ObjCenter:
	def __init__(self, haarPath):
		# 加载人脸探测器
		self.detector = cv2.CascadeClassifier(haarPath)

	def update(self, frame, frameCenter):
		# 将图像转为灰度图
		gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

		# 探测图像中的所有人脸
		rects = self.detector.detectMultiScale(gray, scaleFactor=1.05,
			minNeighbors=9, minSize=(30, 30),
			flags=cv2.CASCADE_SCALE_IMAGE)

		# 是否检测到人脸
		if len(rects) > 0:
			# 获取矩形的参数
			# x,y为左上角点坐标,w,h为宽度和高度
	        # 计算图像中心
			(x, y, w, h) = rects[0]
			faceX = int(x + (w / 2.0))
			faceY = int(y + (h / 2.0))

			# 返回人脸中心
			return ((faceX, faceY), rects[0])

		# 如果没有识别到人脸,返回图像中心
		return (frameCenter, None)
  1. 创建“pid.py”,代码如下:
#pid.py
#-*- coding: UTF-8 -*-
# 调用必需库
import time

class PID:
	def __init__(self, kP=1, kI=0, kD=0):
		# 初始化参数
		self.kP = kP
		self.kI = kI
		self.kD = kD

	def initialize(self):
		# 初始化当前时间和上一次计算的时间
		self.currTime = time.time()
		self.prevTime = self.currTime

		# 初始化上一次计算的误差
		self.prevError = 0

		# 初始化误差的比例值,积分值和微分值
		self.cP = 0
		self.cI = 0
		self.cD = 0

	def update(self, error, sleep=0.2):
		# 暂停
		time.sleep(sleep)

		# 获取当前时间并计算时间差
		self.currTime = time.time()
		deltaTime = self.currTime - self.prevTime

		# 计算误差的微分
		deltaError = error - self.prevError

		# 比例项
		self.cP = error

		# 积分项
		self.cI += error * deltaTime

		# 微分项
		self.cD = (deltaError / deltaTime) if deltaTime > 0 else 0

		# 保存时间和误差为下次更新做准备
		self.prevTime = self.currTime
		self.prevError = error

		# 返回输出值
		return sum([
			self.kP * self.cP,
			self.kI * self.cI,
			self.kD * self.cD])
  1. 上述代码中的from servo import Servo导入servo,这个库是没有的,我们要手动创建这个库,在object_tracking.py所在的目录下新建servo.py文件,复制下面的代码到文件中
#!/usr/bin/env python3
import pigpio
from time import sleep
# Start the pigpiod daemon
import subprocess
result = None
status = 1
for x in range(3):
    p = subprocess.Popen('sudo pigpiod', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    result = p.stdout.read().decode('utf-8')
    status = p.poll()
    if status == 0:
        break
    sleep(0.2)
if status != 0:
    print(status, result)
'''
> Use the DMA PWM of the pigpio library to drive the servo
> Map the servo angle (0 ~ 180 degree) to (-90 ~ 90 degree)

'''

class Servo():
    MAX_PW = 1250  # 0.5/20*100
    MIN_PW = 250 # 2.5/20*100
    _freq = 50 # 50 Hz, 20ms
 
    def __init__(self, pin, min_angle=-90, max_angle=90):

        self.pi = pigpio.pi()
        self.pin = pin 
        self.pi.set_PWM_frequency(self.pin, self._freq)
        self.pi.set_PWM_range(self.pin, 10000)      
        self.angle = 0
        self.max_angle = max_angle
        self.min_angle = min_angle
        self.pi.set_PWM_dutycycle(self.pin, 0)

    def set_angle(self, angle):
        if angle > self.max_angle:
            angle = self.max_angle
        elif angle < self.min_angle:
            angle = self.min_angle
        self.angle = angle
        duty = self.map(angle, -90, 90, 250, 1250)
        self.pi.set_PWM_dutycycle(self.pin, duty)


    def get_angle(self):
        return self.angle
    
    def stop(self):
        self.pi.set_PWM_dutycycle(self.pin, 0)
        self.pi.stop()

    # will be called automatically when the object is deleted
    # def __del__(self):
    #     pass

    def map(self, x, in_min, in_max, out_min, out_max):
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min


if __name__ =='__main__':
    from vilib import Vilib
    # Vilib.camera_start(vflip=True,hflip=True) 
    # Vilib.display(local=True,web=True)

    pan = Servo(pin=13, max_angle=90, min_angle=-90)
    tilt = Servo(pin=12, max_angle=30, min_angle=-90)
    panAngle = 0
    tiltAngle = 0
    pan.set_angle(panAngle)
    tilt.set_angle(tiltAngle)
    sleep(1)

    while True:
        for angle in range(0, 90, 1):
            pan.set_angle(angle)
            tilt.set_angle(angle)
            sleep(.01)
        sleep(.5)
        for angle in range(90, -90, -1):
            pan.set_angle(angle)
            tilt.set_angle(angle)
            sleep(.01)
        sleep(.5)
        for angle in range(-90, 0, 1):
            pan.set_angle(angle)
            tilt.set_angle(angle)
            sleep(.01)
        sleep(.5)

  1. 在树莓派相应文件目录中输入`“python face_tracking_PID.py --cascade haarcascade_frontalface_default.xml",即可实现对人脸对象自动追踪。相较之前的非PID控制而言,系统运行会更顺滑一些。在本例中采用的命令参数输入的方式,可以方便有多个人脸识别级联分类器时随时切换。
  2. 当运行时,可能会有摄像头随机摆动的现象出现,这是因为人脸识别级联分类器的识别过程中的误识别,对于普通用户我们还无能为力,只能是避开经常被误该识别的物体。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Httprunner4.3.6产生大量python进程的解决方法

hrp run执行测试用例目录的时候&#xff0c;发现产生了大量的python进程&#xff0c;导致内存爆了 具体原因是因为每执行一个测试用例&#xff0c;就会注册一次plugin(产生一个python进程&#xff09;&#xff0c;引用测试用例也是一样&#xff0c;会产生一个python进程&#…

Flutter笔记:Web支持原理与实践

Flutter笔记 Web支持原理与实践 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com CSDN&#xff1a;https://blog.csdn.net/qq_28550263/article/details/135037756 华为开发者社区…

大数据深度解析NLP文本摘要技术:定义、应用与PyTorch实战

文章目录 大数据深度解析NLP文本摘要技术&#xff1a;定义、应用与PyTorch实战1. 概述1.1 什么是文本摘要&#xff1f;1.2 为什么需要文本摘要&#xff1f; 2. 发展历程2.1 早期技术2.2 统计方法的崛起2.3 深度学习的应用2.4 文本摘要的演变趋势 3. 主要任务3.1 单文档摘要3.2 …

Kali Linux—借助 SET+MSF 进行网络钓鱼、生成木马、获主机shell、权限提升、远程监控、钓鱼邮件等完整渗透测试(三)

钓鱼邮件 当攻击者制作了钓鱼网站、木马程序后&#xff0c;便会想法设法将其传给受害者&#xff0c;而常见的传播方式便是钓鱼网站了。安全意识较差的用户在收到钓鱼邮件后点击邮件中的钓鱼链接、下载附件中的木马程序&#xff0c;便可能遭受攻击&#xff01; 工具简介 Swak…

uni-app 工程目录结构介绍

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

盒子 Box

UVa1587 思路&#xff1a; 1.输入每个面的长宽并将每个面较长的一边放在前面 2.判断是否存在三对面分别相等 3.判断是否存在三组四棱相等 #include <stdio.h> #include <stdlib.h> #define maxn 100int cmp(const void* e1, const void* e2) {return (int)(*(d…

力扣:51. N 皇后

题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的…

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错的问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序&#xff0c;查看库与库的依赖关系&#xff0c;找出出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总&#xff08;专栏文章列表&…

Neovim+ctag浏览、编辑源代码

Neovimctag浏览、编辑源代码 一 配置安装vim及 ctags vim应该可以不用装&#xff0c;直接装neovim&#xff0c;这里我是先装了vim再装的neovim Ctags必须装&#xff0c;后面用neovim telescope索引函数时才有效 vim复制系统粘贴板&#xff1a;vim输入模式下&#xff0c;按shi…

四、UART_阻塞发送中断接收

1、开发环境 (1)Keil MDK: V5.38.0.0 (2)MCU: mm320163D7P 2、实验目的&原理图 2.1、实验目的 (1)上位机串口助手给MCU发送信息&#xff0c;MCU串口通过通过串口助手接收后&#xff0c;将接收到的内容通过串口助手发送到上位机。 (2)串口在whil循环中每隔1秒发送一次…

怎么使用jupter notebook并配置环境变量

有的时候需要使用Jupyter Notebook运行代码&#xff0c;Jupyter Notebook的主要特点&#xff1a; ① 编程时具有语法高亮、缩进、tab补全的功能。 ② 可直接通过浏览器运行代码&#xff0c;同时在代码块下方展示运行结果。 ③ 以富媒体格式展示计算结果。富媒体格式包括&…

社会人士可以考英语四六级吗?怎么考四六级

目录 一、社会人士能考英语四六级吗二、社会人士可以参加哪些英语等级考试第一.考个商务英语类证书第二.社会上比较认可的还有翻译证书第三.出国常用的英语凭证第四.职称英语.第五.PETS. 大学英语四六级是为提高我国大学英语课程的教学质量服务。那么社会人士能不能报考英语四六…

【UML】第12篇 序列图(1/2)——基本概念和构成

目录 一、什么是序列图&#xff08;Sequence Diagram&#xff09; 1.1 定义 1.2 主要用途 1.3 序列图和BPMN的区别和联系 二、序列图的构成 2.1 对象 2.2 生命线 2.3 消息 2.4 激活 序列图&#xff0c;是我个人认为的用处最多的一种图。产品和研发的同学&#xff0c;都…

java实现深度优先搜索 (DFS) 算法

度优先搜索&#xff08;Depth First Search&#xff0c;DFS&#xff09;算法是一种用于遍历或搜索图或树的算法。这种算法从一个节点开始&#xff0c;沿着一条路径尽可能深地搜索&#xff0c;直到遇到不能继续前进的节点时返回上一个节点&#xff0c;然后继续搜索其他路径。具体…

网络通信协议

WebSocket通信 WebSocket是一种基于TCP的网络通信协议&#xff0c;提供了浏览器和服务器之间的全双工通信&#xff08;full-duplex&#xff09;能力。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接&#xff…

算法通关村-番外篇排序算法

大家好我是苏麟 , 今天带来番外篇 . 冒泡排序 BubbleSort 最基本的排序算法&#xff0c;最常用的排序算法 . 我们以关键字序列{26,53,48,11,13,48,32,15}看一下排序过程: 动画演示 : 代码如下 : (基础版) class Solution {public int[] sortArray(int[] nums) {for(int i …

ArkUI - 状态管理

目录 一、State装饰器 二、自定义组件 三、Prop和Link、Provide和Consume 四、Observed和ObjectLink 五、页面路由 跳转模式 实例模式 使用步骤 一、State装饰器 这里涉及到两个概念 状态 和 视图 状态&#xff08;State&#xff09;&#xff1a;指驱动视图更新的数…

【附三菱 MX OPC Server 6.04的安装包】MX-OPC下载以及用GX Works2和组态王进行仿真连接

使用MX-OPC来完成三菱和组态王的仿真连接。 文章目录 目录 文章目录 软件下载 1.OPC设置 2.GX Works 2 设置 3.GX Works 2 和OPC 连接测试 4.和组态王进行仿真连接 5.安装OPC后&#xff0c;GX Works2 无法打开 提示堆栈不足 6.收尾&#xff08;组态王变量的删除&#xff0…

如何使用java来实现windows系统关机

可以使用Java代码来调用操作系统的命令行来实现Windows关机操作。具体步骤如下&#xff1a; import java.io.IOException;public class ShutdownWindows {public static void main(String[] args) {try {// 调用命令行执行关机命令Process process Runtime.getRuntime().exec…

Python - 深夜数据结构与算法之 Graph

目录 一.引言 二.图的简介 1.Graph 图 2.Undirected graph 无向图 3.Directed Graph 有向图 4.DFS / BFS 遍历 三.经典算法实战 1.Num-Islands [200] 2.Land-Perimeter [463] 3.Largest-Island [827] 四.总结 一.引言 Graph 无论是应用还是算法题目在日常生活中比较…