赞
踩
效果演示
二维云台颜色追踪
import cv2 import time import numpy as np from threading import Thread from servo import Servo from pid import PID # 初始化伺服电机 pan = Servo(pin=19) tilt = Servo(pin=16) panAngle = 0 tiltAngle = 0 pan.set_angle(panAngle) tilt.set_angle(tiltAngle) # 定义视频流类 class VideoStream: def __init__(self, src=0): self.stream = cv2.VideoCapture(src) self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, 320) self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) self.stream.set(cv2.CAP_PROP_FPS, 30) self.grabbed, self.frame = self.stream.read() self.stopped = False def start(self): Thread(target=self.update, args=()).start() return self def update(self): while not self.stopped: self.grabbed, self.frame = self.stream.read() def read(self): return self.frame def stop(self): self.stopped = True self.stream.release() # 启动视频流 vs = VideoStream(src=0).start() # 设置 PID 控制器参数 pan_pid = PID(0.05, 0.01, 0.001) tilt_pid = PID(0.05, 0.01, 0.001) pan_pid.initialize() tilt_pid.initialize() # 计算帧率 fps = 0 pos = (10, 20) font = cv2.FONT_HERSHEY_SIMPLEX height = 0.5 weight = 1 myColor = (0, 0, 255) def nothing(x): pass cv2.namedWindow('PID Tuner') cv2.createTrackbar('Pan Kp', 'PID Tuner', int(pan_pid.kP * 100), 100, nothing) cv2.createTrackbar('Pan Ki', 'PID Tuner', int(pan_pid.kI * 100), 100, nothing) cv2.createTrackbar('Pan Kd', 'PID Tuner', int(pan_pid.kD * 100), 100, nothing) cv2.createTrackbar('Tilt Kp', 'PID Tuner', int(tilt_pid.kP * 100), 100, nothing) cv2.createTrackbar('Tilt Ki', 'PID Tuner', int(tilt_pid.kI * 100), 100, nothing) cv2.createTrackbar('Tilt Kd', 'PID Tuner', int(tilt_pid.kD * 100), 100, nothing) last_update = time.time() update_interval = 0.1 # 控制更新频率 try: while True: tStart = time.time() frame = vs.read() if frame is None: print("Failed to grab frame") break frame = cv2.flip(frame, 1) frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) cv2.putText(frame, str(int(fps)) + ' FPS', pos, font, height, myColor, weight) lowerBound = np.array([0, 147, 114]) upperBound = np.array([88, 255, 255]) myMask = cv2.inRange(frameHSV, lowerBound, upperBound) contours, _ = cv2.findContours(myMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: contours = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True) contour = contours[0] x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 3) # 计算误差 errorX = (x + w / 2) - (320 / 2) errorY = (240 / 2) - (y + h / 2) # 反转误差方向 if time.time() - last_update > update_interval: # 获取PID参数并更新 pan_pid.kP = cv2.getTrackbarPos('Pan Kp', 'PID Tuner') / 100 pan_pid.kI = cv2.getTrackbarPos('Pan Ki', 'PID Tuner') / 100 pan_pid.kD = cv2.getTrackbarPos('Pan Kd', 'PID Tuner') / 100 tilt_pid.kP = cv2.getTrackbarPos('Tilt Kp', 'PID Tuner') / 100 tilt_pid.kI = cv2.getTrackbarPos('Tilt Ki', 'PID Tuner') / 100 tilt_pid.kD = cv2.getTrackbarPos('Tilt Kd', 'PID Tuner') / 100 panAdjustment = pan_pid.update(errorX, sleep=0) tiltAdjustment = tilt_pid.update(errorY, sleep=0) panAngle += panAdjustment tiltAngle += tiltAdjustment # 限制角度范围 panAngle = max(-90, min(panAngle, 120)) tiltAngle = max(-90, min(tiltAngle, 90)) # 设置伺服电机角度 pan.set_angle(panAngle) tilt.set_angle(tiltAngle) last_update = time.time() # 仅在图形环境中显示图像窗口 try: cv2.imshow('Camera', frame) cv2.imshow('Mask', myMask) except cv2.error as e: print(f"OpenCV error: {e}") if cv2.waitKey(1) == ord('q'): break tEnd = time.time() loopTime = tEnd - tStart fps = .9 * fps + .1 * (1 / loopTime) finally: vs.stop() cv2.destroyAllWindows()
# 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]) def set_Kp(self, kP): self.kP = kP def set_Ki(self, kI): self.kI = kI def set_Kd(self, kD): self.kD = kD
import pigpio from time import sleep import subprocess # Start the pigpiod daemon 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) 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() 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__': 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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。