树莓派,opencv,Picamera2利用舵机云台追踪特定颜色对象(PID控制)

本文主要是介绍树莓派,opencv,Picamera2利用舵机云台追踪特定颜色对象(PID控制),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、需要准备的硬件

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

二、项目目标

追踪特定颜色的物体:
当物体移动时,摄像头通过控制两个伺服电机(分别是偏航和俯仰)把该物体放到视界的中心位置,我在这里追踪的是一支黄色的铅笔。

三、具体步骤

3.1 获得被追踪对象的颜色参数

  1. 提前准备一张图片(如下图),可以直接用树莓派的CSI摄像头拍摄并保存,具体方法可以在我之前的文章里找到

示例图片
2. 利用下面的代码并通过调整滑块(Trackbar)获得红色铅笔的HSV颜色参数,为接下来的颜色追踪做准备

import cv2
import json
path='crop_img.jpg'
cv2.namedWindow("TrackBar")def nothing(x):pass
#创建滑块控件
cv2.createTrackbar("Hue Min","TrackBar",0,179,nothing)
cv2.createTrackbar("Hue Max","TrackBar",179,179,nothing)
cv2.createTrackbar("Sat Min","TrackBar",0,255,nothing)
cv2.createTrackbar("Sat Max","TrackBar",255,255,nothing)
cv2.createTrackbar("Val Min","TrackBar",0,255,nothing)
cv2.createTrackbar("Val Max","TrackBar",255,255,nothing)while True:#读取目标图片image=cv2.imread(path)image=cv2.resize(image,(640,480))imgHSV=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)hueLow=cv2.getTrackbarPos("Hue Min","TrackBar")hueHigh=cv2.getTrackbarPos("Hue Max","TrackBar")satLow=cv2.getTrackbarPos("Sat Min","TrackBar")satHigh=cv2.getTrackbarPos("Sat Max","TrackBar")valLow=cv2.getTrackbarPos("Val Min","TrackBar")valHigh=cv2.getTrackbarPos("Val Max","TrackBar")print(hueLow,hueHigh,satLow,satHigh,valLow,valHigh)#创建掩膜mask=cv2.inRange(imgHSV,(hueLow,satLow,valLow),(hueHigh,satHigh,valHigh))image=cv2.bitwise_and(image,image,mask=mask)#显示图像cv2.imshow('Origial',image)data={"hueLow":hueLow,"hueHigh":hueHigh,"satLow":satLow,"satHigh":satHigh,"valLow":valLow,"valHigh":valHigh,}mask_json=json.dumps(data)#按q键保存并退出if cv2.waitKey(1)==ord('q'):#将设置的参数保存到mask.json文件中with open('mask.json','w') as f:f.write(mask_json)break
cv2.destroyAllWindows() 
  1. 运行color_detection.py,并调整滑块(TrackBar)如下图,当然你的被追踪物体的颜色不同,参数也必然不同。
    在这里插入图片描述

  2. 这时你会发现,红色铅笔被显示出来,其它部分被掩膜遮挡,当你在frame窗口按下"q"键后,会自动生成mask.json文件保存相应参数设置
    被掩膜遮挡后的图片

3.2 目标追踪代码

  1. 新建color_tracking_pid.py文件,一级(pan)舵机的信号脚接在GPIO的19脚,二级(tilt)舵机的信号脚接在GPIO的16脚,在运行时可以通过调整main函数里的PID参数,代码如下:
# -*- coding: UTF-8 -*-
# 调用必需库
# color_tracking_pid.py
from multiprocessing import Manager, Process
from pid import PID
from colorcenter import Colorcenter
from servo import Servo
import time
import signal
import sys
import cv2
from picamera2 import Picamera2
import json# 定义舵机
pan = Servo(pin=19)
tilt = Servo(pin=16)# 定义图像尺寸
dispW = 1280
dispH = 720
# 读取掩模配置文件
with open('mask.json') as f:mask = json.load(f)def nothing(x):pass# 键盘终止函数def signal_handler(sig, frame):# 输出状态信息print("[INFO] You pressed `ctrl + c`! Exiting...")# 关闭舵机pan.stop()tilt.stop()# 退出sys.exit()def color_center(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 = 10picam2.preview_configuration.align()picam2.configure("preview")picam2.start()fps = 0time.sleep(2.0)# 初始化色块探测器obj = Colorcenter(mask['hueLow'], mask['satLow'], mask['valLow'],mask['hueHigh'], mask['satHigh'], mask['valHigh'])# 进入循环while True:tStart = time.time()# 从视频流抓取图像并旋转frame = picam2.capture_array()frame = cv2.flip(frame, 1)# #在图像上显示帧率fps = 0cv2.putText(frame, "FPS: {:.2f}".format(fps), (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 225), 3)# 找到图像中心(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 = obj.update(frame, (centerX.value, centerY.value))((objX.value, objY.value), rect) = objectLoc# 绘制色块外界矩形if rect is not None:(x, y, w, h) = rectcv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 3)fX = int(x + (w / 2.0))fY = int(y + (h / 2.0))cv2.circle(frame, (fX, fY), 5, (0, 0, 255), -1)cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 3)# 在色块中心和视窗中心画的条连线cv2.line(frame, (centerX.value, centerY.value),(fX, fY), (0, 255, 0), 2)# 显示图像tEnd = time.time()loopTime = tEnd-tStartfps = .9*fps + .1*(1/loopTime)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 = 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=color_center, 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. 上述代码中的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. 运行color_tracking_pid.py,移动黄色铅笔,摄像头就会自动追踪该对象

这篇关于树莓派,opencv,Picamera2利用舵机云台追踪特定颜色对象(PID控制)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/522433

相关文章

苹果macOS 26 Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色

《苹果macOS26Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色》在整体系统设计方面,macOS26采用了全新的玻璃质感视觉风格,应用于Dock栏、应用图标以及桌面小部件等多个界面... 科技媒体 MACRumors 昨日(6 月 13 日)发布博文,报道称在 macOS 26 Tahoe 中

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Python远程控制MySQL的完整指南

《Python远程控制MySQL的完整指南》MySQL是最流行的关系型数据库之一,Python通过多种方式可以与MySQL进行交互,下面小编就为大家详细介绍一下Python操作MySQL的常用方法和最... 目录1. 准备工作2. 连接mysql数据库使用mysql-connector使用PyMySQL3.

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

如何搭建并配置HTTPD文件服务及访问权限控制

《如何搭建并配置HTTPD文件服务及访问权限控制》:本文主要介绍如何搭建并配置HTTPD文件服务及访问权限控制的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、安装HTTPD服务二、HTTPD服务目录结构三、配置修改四、服务启动五、基于用户访问权限控制六、

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一