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

一、项目目标

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

  • Mediapipe Hand简介

MediaPipe 手部标志任务可检测图像中手部的标志。 您可以使用此任务来定位手的关键点并在其上渲染视觉效果。 该任务使用机器学习(ML)模型作为静态数据或连续流对图像数据进行操作,并输出图像坐标中的手部标志、世界坐标中的手部标志以及多个检测到的手的惯用手(左/右手)。
在这里插入图片描述

二、 需要准备的软、硬件

  1. Raspiberry Pi 4b
  2. 两个SG90 180度舵机(注意舵机的角度,最好是180度且带限位的,切勿选360度舵机)
  3. 二自由度舵机云台(如下图)
  4. Raspiberry CSI 摄像头
  5. mediapipe库, 安装方法可以参照此链接
    组装后的效果:
    组装后的效果

三、具体步骤

  1. 创建“hand_tracking_PID.py”文件,代码如下,我在本文中追踪的是大拇指指尖,如果你想追踪其它部位,只须将fingerID参数设置成你想追踪的数字即可。具体数字分布如下图。

hand landmark模型

#-*- coding: UTF-8 -*-	
# 调用必需库
#hand_tracking_PID.py
from multiprocessing import Manager
from multiprocessing import Process
from handobj import HandObj
from pid import PID
from servo import Servo
import signal
import time
import sys
import cv2
import mediapipe as mp
from picamera2 import Picamera2# 定义舵机
pan=Servo(pin=19)
tilt=Servo(pin=16)#定义图像尺寸
dispW=1280
dispH=720# 定义手指ID
fingerID=4# 键盘终止函数
def signal_handler(sig, frame):# 输出状态信息print("[INFO] You pressed `ctrl + c`! Exiting...")# 关闭舵机pan.stop()tilt.stop()# 退出sys.exit()def hand_obj(objX,objY,centerX,centerY):# ctrl+c退出进程signal.signal(signal.SIGINT, signal_handler)# 启动视频流并缓冲print("[INFO] waiting for camera to warm up...")cv2.startWindowThread()picam2 = Picamera2()preview_config = picam2.create_preview_configuration(main={"size": (dispW, dispH),"format":"RGB888"})picam2.configure(preview_config)picam2.start()time.sleep(2.0)#初始化手掌对象探测器hand=HandObj(fingerID)#进入循环while True:# 从视频流抓取图像并旋转frame = picam2.capture_array()frame = cv2.flip(frame, 1)# 找到图像中心(H, W) = frame.shape[:2]centerX.value = W // 2centerY.value = H // 2# 画出图像中心点cv2.circle(frame, (centerX.value, centerY.value), 5, (0, 0, 255), -1)# 找到手指对象点objectLoc = hand.update(frame, (centerX.value, centerY.value))((objX.value, objY.value), handlms) = objectLoc# 画出手指关注的对象点,这是里前面定义的ID:4,即大拇指指尖if handlms is not None:      cv2.circle(frame, (objX.value, objY.value), 15, (255, 0, 255), cv2.FILLED)cv2.imshow('Hand', 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 = 0output.value = p.update(error)def set_servos(panAngle, tiltAngle):# ctrl+c退出进程signal.signal(signal.SIGINT, signal_handler)#进入循环while True:# 偏角变号yaw = -1 * panAngle.valuepitch = -1 * tiltAngle.value# 设置舵机角度。pan.set_angle(yaw)tilt.set_angle(pitch)# 启动主程序
if __name__ == "__main__":# 启动多进程变量管理with Manager() as manager:  # 相当于manager=Manager(),with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源。# 舵机角度置零pan.set_angle(0)tilt.set_angle(0)# 为图像中心坐标赋初值centerX = manager.Value("i", 0)  # "i"即为整型integercenterY = 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"即为浮点型floatpanI = 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=hand_obj, 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. 创建“handobj.py”,代码如下:
#handobj.py
#-*- coding: UTF-8 -*-
# 调用必需库
import mediapipe as mpclass HandObj:def __init__(self,fingerID):# 初始化手掌关键点坐标self.myHands=mp.solutions.hands# 初始化手掌关键点坐标和手掌关键点连接情况self.hands=self.myHands.Hands()# 初始化手掌关键点绘制库self.mpDraw=mp.solutions.drawing_utils# 初始化手掌关键点IDself.fingerID=fingerIDdef update(self, frame, frameCenter):# 处理视频流results = self.hands.process(frame)if results.multi_hand_landmarks:for handLms in results.multi_hand_landmarks:# 绘制手掌关键点self.mpDraw.draw_landmarks(frame, handLms, self.myHands.HAND_CONNECTIONS)for id, lm in enumerate(handLms.landmark):h, w, c = frame.shapecx, cy = int(lm.x * w), int(lm.y * h)if id == self.fingerID:#绘制手掌关键点并返回手掌关键点坐标return ((cx, cy), handLms)return(frameCenter,None)
  1. 创建“pid.py”,代码如下:
#-*- coding: UTF-8 -*-
# 调用必需库
import timeclass PID:def __init__(self, kP=1, kI=0, kD=0):# 初始化参数self.kP = kPself.kI = kIself.kD = kDdef initialize(self):# 初始化当前时间和上一次计算的时间self.currTime = time.time()self.prevTime = self.currTime# 初始化上一次计算的误差self.prevError = 0# 初始化误差的比例值,积分值和微分值self.cP = 0self.cI = 0self.cD = 0def update(self, error, sleep=0.5):# 暂停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.currTimeself.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:breaksleep(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*100MIN_PW = 250 # 2.5/20*100_freq = 50 # 50 Hz, 20msdef __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 = 0self.max_angle = max_angleself.min_angle = min_angleself.pi.set_PWM_dutycycle(self.pin, 0)def set_angle(self, angle):if angle > self.max_angle:angle = self.max_angleelif angle < self.min_angle:angle = self.min_angleself.angle = angleduty = self.map(angle, -90, 90, 250, 1250)self.pi.set_PWM_dutycycle(self.pin, duty)def get_angle(self):return self.angledef 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):#     passdef map(self, x, in_min, in_max, out_min, out_max):return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_minif __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 = 0tiltAngle = 0pan.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. 运行效果如下图,如果不想在运行过程中显示网格与关注的手指节点,可以把相应的代码注释掉

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么搭建实时渲染云传输服务器

实时渲染云传输技术方案&#xff0c;在数字孪生、虚拟仿真领域使用越来越多&#xff0c;可能很多想使用该技术方案项目还不知道具体该怎么搭建云传输服务器&#xff0c;具体怎么使用实时云渲染平台系统。点量云小芹将对这两个问题做集中分享。 一、实时渲染服务器怎么搭建&…

PMP®项目管理,2024年1月4日开课啦~想了解的提前查看!

PMP项目管理认证 1&#x1f237;4日开课~ 想报名的提前预约啦 &#x1f447;&#x1f447;&#x1f447; &#x1f446;&#xff08;以上是PMP课程内容&#xff09; 课程介绍 PMP&#xff08;Project Management Professional&#xff09;是由美国项目管理协会&#xff08;…

关于 Appium 各种版本的安装,都在这里

大家在初次接触 Appium 时会看到网上各种帖子讲解如何安装 Appium&#xff0c;各种 Appium 版本的安装教程满天飞&#xff0c;而很多帖子中提供的安装教程是已经过时了的&#xff0c;容易误导初学者。 这篇文章带着你一起全面了解 Appium 各种版本如何选择如何安装。 一句话概述…

Vue3的v-if 和v-for优先级

文章目录 在vue2中&#xff0c;v-for 具有比 v-if 更高的优先级在vue3中&#xff0c;v-if 具有比 v-for 更高的优先级。 在vue2中&#xff0c;v-for 具有比 v-if 更高的优先级 在vue2中应尽量避免二者同时使用&#xff0c;同时使用时编译器会报错&#xff1a; v-for是用于循环…

kubernetes(k8s) Yaml 文件详解

YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读。 1、查看API 资源版本标签 kubectl api-versions 2、编写资源配置清单 kubectl create -f nginx-test.yaml --validatefalse 2.3 查看创建的po…

众和策略:12月新批国产网游版号数量过百

上星期五&#xff08;22日&#xff09;&#xff0c;A股冲高回落&#xff0c;三大股指挨近午盘拉升走高&#xff0c;午后再度回落走低&#xff0c;沪指尾盘跌幅收窄。到收盘&#xff0c;沪指跌0.13%报2914.78点&#xff0c;深成指跌0.39%报9221.31点&#xff0c;创业板指跌0.37%…

低代码开发:数字化转型的引擎

引言 在当今数字化时代&#xff0c;组织面临着不断变化的市场需求和技术挑战。数字化转型已成为维持竞争力的关键&#xff0c;而低代码开发正在崭露头角&#xff0c;成为加速创新和数字化转型的有力工具。本文将深入探讨低代码开发的核心概念、优势和应用&#xff0c;以揭示它…

LLM之RAG实战(八)| 使用Neo4j和LlamaIndex实现多模态RAG

人工智能和大型语言模型领域正在迅速发展。一年前&#xff0c;没有人使用LLM来提高生产力。时至今日&#xff0c;很难想象我们大多数人或多或少都在使用LLM提供服务&#xff0c;从个人助手到文生图场景。由于大量的研究和兴趣&#xff0c;LLM每天都在变得越来越好、越来越聪明。…

客户跟进没效果?这三招请收好!!

在现代商业环境中&#xff0c;与客户进行有效的跟进至关重要。但是&#xff0c;有时候不论我们多么努力地跟进&#xff0c;却依然无法获得预期的结果。 今天就给大家分享三个高效跟进客户的方法&#xff0c;帮助大家提高效率&#xff01; 首先&#xff0c;了解客户需求是关键…

ubuntu 安装apisix -亲测可用

官方未提供在ubuntu系统中安装apisix的方式&#xff0c;似乎只能通过源码方式安装&#xff0c;但是并不推荐&#xff0c;非常容易失败&#xff0c; 具体操作方式如下&#xff1a; ubuntu和Debian其实类似的&#xff0c;可使用DEB方式安装&#xff0c;如下截图 注意&#xff1…

精品Nodejs实现的微信小程序的运动减肥管理系统设计与实现菜谱美食健康

《[含文档PPT源码等]精品Nodejs实现的微信小程序的运动减肥管理系统设计与实现[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 操作系统&#xff1a;Windows 10、Windows …

数据资产入表,这泼天的富贵大数据团队怎样才能接住?

​“ 大数据团队是成本中心还是价值中心&#xff1f; 数据资产入表&#xff0c;国家是怎么说的&#xff1f; 数据平台应具备哪些能力&#xff0c;才能助力企业最大化数据资产价值&#xff1f;” 2022年12月&#xff0c;国务院发文关于发挥数据要素作用的指导意见&#xff08;…

Java基础回顾——JDBC

文章目录 介绍使用JDBC事务JDBC BatchJDBC连接池 介绍 Java为关系数据库定义了一套标准的访问接口&#xff1a;JDBC&#xff08;Java Database Connectivity&#xff09; JDBC是Java程序访问数据库的标准接口 好处&#xff1a; 各数据库厂商使用相同的接口&#xff0c;Java…

动物分类识别教程+分类释义+界面展示

1.项目简介 动物分类教程分类释义界面展示 动物分类是生物学中的一个基础知识&#xff0c;它是对动物进行分类、命名和描述的科学方法。本教程将向您介绍动物分类的基本原则和方法&#xff0c;并提供一些常见的动物分类释义。 动物分类的基本原则 动物分类根据动物的形态、…

分布式事务是什么

分布式事务是企业集成中的一个技术难点&#xff0c;也是每一个分布式系统架构中都会涉及到的一个东西&#xff0c;特别是在微服务架构中&#xff0c;几乎可以说是无法避免&#xff0c;本文就分布式事务来简单聊一下。 数据库事务 我们先从数据库事务说起。数据库事务可能大家…

(001)Unit 编译 UTF8JSON

文章目录 编译 Dll编译报错附录 编译 Dll 新建工程&#xff1a; 注意 UnityEngineDll 的选择&#xff01;2022 版本的太高了&#xff01;&#xff01;&#xff01; 下载包&#xff0c;导入unity : 3. 将 unf8json 的源码拷贝到新建的工程。 4. 编译发布版本&#xff1a; 编译…

ggkegg玩转KEGG数据 | 富集 | 可视化

本期内容 写在前面 今天分享一个关于KEGG通路图绘制的R包&#xff0c;也许在你后面的分析中可以使用得到。 在KEGG富集分析中&#xff0c;若我们要绘制某一个富集通路&#xff0c;一般回到KEGG官网中寻找该通路的富集图。然后&#xff0c;通过AI&#xff0c;PPT等等一系列手段…

【MySQL】数据库之小题练习(完全备份和增量备份的数据恢复,以及断点恢复)

目录 先创建库&#xff0c;创建表&#xff0c;完成三次数据的录入以及第一次的完全备份&#xff0c;第二次和第三次的增量备份&#xff1b; 第一次完全备份操作 第二次插入后做增量备份操作 第三次 插入后做增量备份操作 1、完全备份恢复&#xff0c;获取一班的人的成绩 …

内网穿透中微子代理搭建使用

内网穿透中微子代理搭建使用 环境准备服务器端部署客户端安装服务器开启端口 环境准备 中微子代理项目地址 最新版是2.0.1版本,需要java21的环境,考虑到现在项目大多数环境都是java8,稳重使用1.9版本搭建,jar下载地址: 准备一台有公网ip的服务器(或云服务器),本文中使用阿里…

【教程】从gitee或者github,下载单个文件或文件夹命令

1.打开git 2.初始化 git init 3.设置允许下载子目录 &#xff08;不需要修改任何&#xff0c;只要原样复制&#xff0c;需要按照个人状况修改的话我会标注&#xff09; git config core.sparseCheckout true 4. 选择要下载的单个文件夹的路径 这里单引号内部需要修改&…