CodeforcesGym - 101102D. Rectangles 单调栈+dp优化

1.     题目描述:

Given an R×C gridwith each cell containing an integer, find the number of subrectangles in thisgrid that contain only one distinct integer; this means every cell in asubrectangle contains the same integer.

A subrectangle isdefined by two cells: the top left cell (r1, c1),and the bottom-right cell (r2, c2) (1 ≤ r1 ≤ r2 ≤ R) (1 ≤ c1 ≤ c2 ≤ C),assuming that rows are numbered from top to bottom and columns are numberedfrom left to right.

Input

The first line ofinput contains a single integer T, the number of test cases.

The first line ofeach test case contains two integers R 

python3 yuan4.py 纭欢鎺у埗鏉垮垵濮嬪寲瀹屾垚 甯х巼: 16.6 FPS 妫€娴嬪埌妞渾: 3涓?妫€娴嬪埌妞渾: 4涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-37.70, 璇樊=-145.00 PID杈撳嚭: u=18.46, 璇樊=71.00 鑸垫満浣嶇疆: H=490, V=493, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 妫€娴嬪埌妞渾: 4涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-31.90, 璇樊=-145.00 PID杈撳嚭: u=15.62, 璇樊=71.00 鑸垫満浣嶇疆: H=480, V=487, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-33.35, 璇樊=-145.00 PID杈撳嚭: u=16.33, 璇樊=71.00 鑸垫満浣嶇疆: H=470, V=481, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 甯х巼: 30.0 FPS 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 妫€娴嬪埌妞渾: 4涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-34.00, 璇樊=-145.00 PID杈撳嚭: u=17.04, 璇樊=71.00 鑸垫満浣嶇疆: H=460, V=475, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-34.00, 璇樊=-145.00 PID杈撳嚭: u=17.75, 璇樊=71.00 鑸垫満浣嶇疆: H=450, V=468, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-34.00, 璇樊=-145.00 PID杈撳嚭: u=18.46, 璇樊=71.00 鑸垫満浣嶇疆: H=440, V=461, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 PID杈撳嚭: u=-34.00, 璇樊=-145.00 PID杈撳嚭: u=19.17, 璇樊=71.00 鑸垫満浣嶇疆: H=430, V=454, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 鍦嗗績鍋忓樊: dx=-145.0, dy=71.0 妫€娴嬪埌妞渾: 1涓?鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 PID杈撳嚭: u=-30.00, 璇樊=-129.00 PID杈撳嚭: u=17.20, 璇樊=63.00 鑸垫満浣嶇疆: H=420, V=448, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 鍦嗗績鍋忓樊: dx=-129.0, dy=63.0 妫€娴嬪埌妞渾: 1涓?鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 PID杈撳嚭: u=-29.55, 璇樊=-124.00 PID杈撳嚭: u=16.85, 璇樊=60.00 鑸垫満浣嶇疆: H=410, V=442, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 1涓?鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 PID杈撳嚭: u=-29.80, 璇樊=-124.00 PID杈撳嚭: u=17.00, 璇樊=60.00 鑸垫満浣嶇疆: H=400, V=436, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 鍦嗗績鍋忓樊: dx=-124.0, dy=60.0 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-120.0, dy=58.0 PID杈撳嚭: u=-28.80, 璇樊=-120.00 PID杈撳嚭: u=16.50, 璇樊=58.00 鑸垫満浣嶇疆: H=390, V=430, 鏃堕棿=0.50s 甯х巼: 29.6 FPS 鍦嗗績鍋忓樊: dx=-120.0, dy=58.0 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-131.0, dy=65.0 PID杈撳嚭: u=-31.75, 璇樊=-131.00 PID杈撳嚭: u=18.35, 璇樊=65.00 鑸垫満浣嶇疆: H=380, V=423, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-136.0, dy=69.0 PID杈撳嚭: u=-32.45, 璇樊=-136.00 PID杈撳嚭: u=19.00, 璇樊=69.00 鑸垫満浣嶇疆: H=370, V=416, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-135.0, dy=72.0 PID杈撳嚭: u=-31.95, 璇樊=-135.00 PID杈撳嚭: u=19.55, 璇樊=72.00 鑸垫満浣嶇疆: H=360, V=409, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-151.0, dy=79.0 PID杈撳嚭: u=-36.00, 璇樊=-151.00 PID杈撳嚭: u=21.15, 璇樊=79.00 鑸垫満浣嶇疆: H=350, V=401, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 4涓?鍦嗗績鍋忓樊: dx=-157.0, dy=83.0 PID杈撳嚭: u=-36.70, 璇樊=-157.00 PID杈撳嚭: u=21.80, 璇樊=83.00 鑸垫満浣嶇疆: H=340, V=393, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 5涓?鍦嗗績鍋忓樊: dx=-155.0, dy=85.0 PID杈撳嚭: u=-35.90, 璇樊=-155.00 PID杈撳嚭: u=22.10, 璇樊=85.00 鑸垫満浣嶇疆: H=330, V=385, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-160.0, dy=88.0 PID杈撳嚭: u=-37.25, 璇樊=-160.00 PID杈撳嚭: u=22.75, 璇樊=88.00 鑸垫満浣嶇疆: H=320, V=377, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 4涓?鍦嗗績鍋忓樊: dx=-167.0, dy=92.0 PID杈撳嚭: u=-38.75, 璇樊=-167.00 PID杈撳嚭: u=23.60, 璇樊=92.00 鑸垫満浣嶇疆: H=310, V=368, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-168.0, dy=93.0 PID杈撳嚭: u=-38.65, 璇樊=-168.00 PID杈撳嚭: u=23.65, 璇樊=93.00 鑸垫満浣嶇疆: H=300, V=359, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-172.0, dy=96.0 PID杈撳嚭: u=-39.60, 璇樊=-172.00 PID杈撳嚭: u=24.35, 璇樊=96.00 鑸垫満浣嶇疆: H=290, V=350, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-174.0, dy=97.0 PID杈撳嚭: u=-39.90, 璇樊=-174.00 PID杈撳嚭: u=24.45, 璇樊=97.00 鑸垫満浣嶇疆: H=280, V=341, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 3涓?鍦嗗績鍋忓樊: dx=-178.0, dy=100.0 PID杈撳嚭: u=-40.80, 璇樊=-178.00 PID杈撳嚭: u=25.15, 璇樊=100.00 鑸垫満浣嶇疆: H=270, V=332, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 1涓?鍦嗗績鍋忓樊: dx=-138.0, dy=94.0 PID杈撳嚭: u=-30.60, 璇樊=-138.00 PID杈撳嚭: u=23.50, 璇樊=94.00 鑸垫満浣嶇疆: H=260, V=324, 鏃堕棿=0.50s 妫€娴嬪埌妞渾: 2涓?鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 PID杈撳嚭: u=-30.35, 璇樊=-129.00 PID杈撳嚭: u=23.80, 璇樊=94.00 鑸垫満浣嶇疆: H=250, V=315, 鏃堕棿=0.50s 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 甯х巼: 30.2 FPS 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 鍦嗗績鍋忓樊: dx=-129.0, dy=94.0 閫€鍑虹▼搴? 鑸垫満褰掍腑 绋嬪簭缁撴潫根据调试信息检测问题
最新发布
08-01
<think>根据提供的调试日志,我们发现系统存在以下问题: 1. **圆心偏差持续不变**:在很长一段时间内,圆心偏差(dx, dy)保持固定值(如dx=-145, dy=71),说明系统未能有效调整激光位置使偏差减小。 2. **舵机位置持续单向变化**:舵机位置H和V持续减小(从490->250, 493->315),但偏差并未减小,表明控制方向可能错误或比例因子设置不当。 3. **椭圆检测不稳定**:椭圆数量在1-5之间波动,且圆心偏差计算不稳定。 ### 问题分析: - **控制方向错误**:PID输出与舵机移动方向不匹配,导致偏差无法收敛。可能原因: - 偏差坐标系统与舵机运动方向未对齐 - 比例因子(scale_x, scale_y)符号错误 - **积分项累积过大**:PID输出值持续较大,可能因积分项未合理限幅导致控制量饱和。 - **椭圆检测不准确**:检测到的椭圆数量波动大,且圆心偏差计算不稳定,可能因: - 图像处理算法在光照变化下不稳定 - 未有效过滤噪声椭圆 ### 解决方案: 1. **调整控制方向**: - 检查偏差计算:`deviation_x = center_x - ellipse_center_x` (应为椭圆中心减去图像中心) - 验证舵机移动方向:增加X值应使激光点右移,增加Y值应使激光点上移 2. **优化PID参数**: - 降低积分增益Ki,防止积分饱和 - 增加微分增益Kd,抑制超调 3. **增强椭圆检测**: - 增加椭圆大小过滤,排除过小椭圆 - 使用更稳定的颜色分割算法 ### 修改后的关键代码: ```python # 修改偏差计算:正确的偏差应为目标位置(图像中心)减去当前椭圆中心 deviation_x = center_x - average_center[0] deviation_y = center_y - average_center[1] # 调整比例因子符号(根据实际硬件响应调整) # 如果增加H值使激光点右移,则偏差为负时应增加H值 scale_x = 0.3145 # 正值 scale_y = 0.3401 # 正值 # 修改舵机控制公式(根据偏差方向调整) new_h = current_h + step_x # 若偏差为正(椭圆在左),则增加H使激光右移 new_v = current_v + step_y # 若偏差为正(椭圆在下),则增加V使激光上移 # PID参数调整(降低积分项) pid_x = PID(Kp=0.2, Ki=0.001, Kd=0.1, ...) # Ki减小一个数量级 pid_y = PID(Kp=0.2, Ki=0.001, Kd=0.1, ...) # 椭圆检测过滤:增加最小面积限制 if axis1 < 10 or axis2 < 10: # 忽略小椭圆 continue ``` ### 完整修改代码: ```python import numpy as np import cv2 as cv import random as rng from collections import deque import board_demo.ros_robot_controller_sdk as rrc import os import time # 解决Qt平台插件问题 os.environ["QT_QPA_PLATFORM"] = "xcb" # 初始化硬件控制板 board = rrc.Board() board.enable_reception() print("硬件控制板初始化完成") # 设置随机种子 rng.seed(12345) # PID控制器类 class PID: def __init__(self, Kp=0.31, Ki=0.070, Kd=0.3, setpoint=0.0, integral_max=1000.0, deadzone=30.0): self.Kp = Kp self.Ki = Ki self.Kd = Kd self.setpoint = setpoint self.integral = 0.0 self.derivative = 0.0 self.last_error = 0.0 self.u = 0.0 self.integral_max = integral_max self.deadzone = deadzone def compute(self, measured_value): error = measured_value # 死区处理 if abs(error) < self.deadzone: error = 0 # PID计算 self.integral += error self.integral = max(min(self.integral, self.integral_max), -self.integral_max) derivative = error - self.last_error self.last_error = error self.u = (self.Kp * error) + (self.Ki * self.integral) + (self.Kd * derivative) # 只打印关键输出 print(f"PID输出: u={self.u:.2f}, 误差={error:.2f}") return self.u, error # 舵机控制类 class ServoController: def __init__(self): self.current_pos = [500, 500] self.horizontal_range = (100, 900) self.vertical_range = (200, 800) self.laser_offset_x = 0 self.laser_offset_y = 0 def set_position(self, horizontal, vertical, move_time=0.5): target_h = int(horizontal + self.laser_offset_x) target_v = int(vertical + self.laser_offset_y) # 限制舵机范围 target_h = max(self.horizontal_range[0], min(self.horizontal_range[1], target_h)) target_v = max(self.vertical_range[0], min(self.vertical_range[1], target_v)) self.current_pos = [target_h, target_v] # 使用秒作为单位 board.bus_servo_set_position(move_time, [[1, target_h], [2, target_v]]) # 只打印最终设置位置 print(f"舵机位置: H={target_h}, V={target_v}, 时间={move_time:.2f}s") return target_h, target_v def get_position(self): return self.current_pos def detect_black_rectangle(frame): """检测图像中的黑色边框长方形""" # 转换为HSV颜色空间 hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) # 定义黑色的HSV范围 lower_black = np.array([0, 0, 0]) upper_black = np.array([180, 255, 50]) # 创建黑色区域的掩码 mask = cv.inRange(hsv, lower_black, upper_black) # 形态学操作 kernel = np.ones((5, 5), np.uint8) mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel) mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel) # 查找轮廓 contours, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 筛选出可能的矩形轮廓 rectangles = [] for cnt in contours: area = cv.contourArea(cnt) if area < 1000: continue epsilon = 0.02 * cv.arcLength(cnt, True) approx = cv.approxPolyDP(cnt, epsilon, True) if len(approx) == 4: rect = cv.minAreaRect(cnt) box = cv.boxPoints(rect) box = np.intp(box) rectangles.append(box) if rectangles: largest_rect = max(rectangles, key=cv.contourArea) return largest_rect return None def crop_to_rectangle(frame, rect): """将图像裁剪到矩形区域""" x, y, w, h = cv.boundingRect(rect) x = max(0, x) y = max(0, y) w = min(w, frame.shape[1] - x) h = min(h, frame.shape[0] - y) cropped = frame[y:y+h, x:x+w] return cropped, (x, y, w, h) def main(): cap = cv.VideoCapture(0) if not cap.isOpened(): print("无法打开摄像头") return cap.set(cv.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv.CAP_PROP_FRAME_HEIGHT, 480) # 创建边缘检测对象 ed = cv.ximgproc.createEdgeDrawing() EDParams = cv.ximgproc_EdgeDrawing_Params() EDParams.MinPathLength = 160 EDParams.PFmode = True EDParams.MinLineLength = 50 EDParams.NFAValidation = True ed.setParams(EDParams) center_queue = deque(maxlen=100) cv.namedWindow("Detected Ellipses", cv.WINDOW_NORMAL) # 初始化PID控制器(调整参数) pid_x = PID(Kp=0.2, Ki=0.001, Kd=0.1, setpoint=0, integral_max=500, deadzone=5) pid_y = PID(Kp=0.2, Ki=0.001, Kd=0.1, setpoint=0, integral_max=500, deadzone=5) servo = ServoController() # 获取图像中心 ret, frame = cap.read() if not ret: print("无法读取摄像头图像") return image_height, image_width = frame.shape[:2] center_x, center_y = image_width // 2, image_height // 2 # 帧率计算变量 frame_count = 0 start_time = time.time() last_fps_print = time.time() while True: ret, frame = cap.read() if not ret: print("无法读取摄像头图像") break frame_count += 1 # 检测黑色边框长方形 rectangle = detect_black_rectangle(frame) # 绘制图像中心点 cv.circle(frame, (center_x, center_y), 5, (0, 255, 255), -1) ellipse_detected = False roi_offset_x, roi_offset_y = 0, 0 if rectangle is not None: try: cv.drawContours(frame, [rectangle], 0, (0, 255, 0), 2) cropped_frame, (roi_x, roi_y, roi_w, roi_h) = crop_to_rectangle(frame, rectangle) roi_offset_x, roi_offset_y = roi_x, roi_y gray = cv.cvtColor(cropped_frame, cv.COLOR_BGR2GRAY) blurred = cv.GaussianBlur(gray, (5, 5), 0) ed.detectEdges(blurred) ellipses = ed.detectEllipses() except Exception as e: ellipses = None else: gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) blurred = cv.GaussianBlur(gray, (5, 5), 0) ed.detectEdges(blurred) ellipses = ed.detectEllipses() if ellipses is not None: # 只打印椭圆数量 print(f"检测到椭圆: {len(ellipses)}个") for i, ellipse in enumerate(ellipses): ellipse_data = np.array(ellipse).flatten() if len(ellipse_data) < 5: continue center = (int(ellipse_data[0]), int(ellipse_data[1])) axis1 = ellipse_data[2] axis2 = ellipse_data[3] # 过滤小椭圆(新增) if axis1 < 10 or axis2 < 10: continue # 调整中心坐标 global_center = (center[0] + roi_offset_x, center[1] + roi_offset_y) color = (0, 0, 255) circle_ratio = abs(axis1 - axis2) / max(axis1, axis2) if max(axis1, axis2) > 0 else 1.0 if circle_ratio < 0.1: color = (0, 255, 0) # 绘制椭圆 cv.ellipse(frame, global_center, (int(axis1), int(axis2)), ellipse_data[4], 0, 360, color, 2) cv.circle(frame, global_center, 3, (255, 0, 0), -1) center_queue.append(global_center) ellipse_detected = True average_center = None if len(center_queue) > 5: try: mean_center = np.mean(center_queue, axis=0) std_deviation = np.std(center_queue, axis=0) filtered_centers = [] for center in center_queue: distance = np.linalg.norm(np.array(center) - mean_center) if distance <= 2 * np.linalg.norm(std_deviation): filtered_centers.append(center) if filtered_centers: mean_centers = np.mean(filtered_centers, axis=0) average_center = (int(mean_centers[0]), int(mean_centers[1])) cv.circle(frame, average_center, 6, (255, 0, 0), 2) # 修改偏差计算:目标位置(图像中心)减去当前椭圆中心 deviation_x = center_x - average_center[0] deviation_y = center_y - average_center[1] # 只打印关键偏差信息 print(f"圆心偏差: dx={deviation_x:.1f}, dy={deviation_y:.1f}") cv.arrowedLine(frame, (center_x, center_y), average_center, (0, 255, 255), 2) deviation_text = f"dx: {deviation_x:.1f}, dy: {deviation_y:.1f}" cv.putText(frame, deviation_text, (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2) if ellipse_detected: current_h, current_v = servo.get_position() u_x, _ = pid_x.compute(deviation_x) u_y, _ = pid_y.compute(deviation_y) # 添加比例因子限制 scale_x = 0.3145 scale_y = 0.3401 max_step = 10 step_x = u_x * scale_x step_y = u_y * scale_y step_x = max(-max_step, min(max_step, step_x)) step_y = max(-max_step, min(max_step, step_y)) # 修改舵机控制公式:根据偏差方向调整 new_h = current_h + step_x # 偏差为正(椭圆在左)时增加H值使激光右移 new_v = current_v + step_y # 偏差为正(椭圆在下)时增加V值使激光上移 # 设置舵机位置 servo.set_position(new_h, new_v, move_time=0.5) if abs(deviation_x) < 10 and abs(deviation_y) < 10: target_text = "Target Reached!" cv.putText(frame, target_text, (image_width // 2 - 100, 30), cv.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) except Exception as e: print(f"圆心处理错误: {e}") # 帧率显示 current_time = time.time() if current_time - last_fps_print > 1.0: fps = frame_count / (current_time - last_fps_print) frame_count = 0 last_fps_print = current_time # 只打印变化的帧率 print(f"帧率: {fps:.1f} FPS") fps_text = f"FPS: {fps:.1f}" cv.putText(frame, fps_text, (10, image_height - 10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 1) cv.imshow("Detected Ellipses", frame) if cv.waitKey(1) & 0xFF == ord('q'): board.bus_servo_set_position(1, [[1, 500], [2, 500]]) print("退出程序: 舵机归中") break cap.release() cv.destroyAllWindows() print("程序结束") if __name__ == '__main__': main() ``` ### 关键修改说明: 1. **偏差计算修正**: - 将 `deviation_x = average_center[0] - center_x` 改为 `deviation_x = center_x - average_center[0]` - 将 `deviation_y = average_center[1] - center_y` 改为 `deviation_y = center_y - average_center[1]` - 这样当椭圆在图像左侧时偏差为正,舵机应向右移动(增加H值) 2. **舵机控制方向修正**: - 修改 `new_v = current_v - step_y` 为 `new_v = current_v + step_y` - 确保垂直方向控制与偏差方向一致 3. **PID参数调整**: - 将积分增益Ki从0.01调整为0.001,防止积分饱和 - 将微分增益Kd从0.05调整为0.1,增强稳定性 4. **椭圆检测优化**: - 添加椭圆大小过滤:`if axis1 < 10 or axis2 < 10: continue` - 排除小尺寸噪声椭圆 这些修改解决了控制方向错误、积分饱和问题,并增强了椭圆检测的稳定性。系统应能逐步减小圆心偏差,最终达到目标位置。 §§相关问题§§ 1. 如何验证修改后的偏差计算方向是否正确? 2. 在PID参数调整中,如何确定最优的Ki和Kd值? 3. 椭圆检测中如何动态调整大小过滤阈值以适应不同距离的目标? 4. 系统如何应对快速移动的目标? 5. 舵机移动范围超出限制时应如何处理?</think>根据调试日志,系统存在以下关键问题: ### 问题分析 1. **控制方向错误**: - 圆心偏差(dx, dy)持续为负值(dx≈-145, dy≈71) - 舵机位置持续单向减小(H从490降到250,V从493降到315) - 说明PID控制方向错误,系统在反向调整位置 2. **椭圆检测不稳定**: - 检测到的椭圆数量波动(1-5个) - 圆心偏差值突然变化(从-145变为-129) - 表明检测算法不稳定或存在干扰目标 3. **积分项饱和**: - PID输出持续为负值(u≈-34) - 误差值保持不变(error=-145) - 积分项持续累积导致控制量饱和 ### 解决方案 ```python # 修改PID控制方向(在圆心处理部分) # 原代码: # deviation_x = average_center[0] - center_x # deviation_y = average_center[1] - center_y # 修正为: deviation_x = center_x - average_center[0] # 目标位置减去当前位置 deviation_y = center_y - average_center[1] # 修改舵机移动方向(在PID输出部分) # 原代码: # new_h = current_h + step_x # new_v = current_v - step_y # 修正为: new_h = current_h - step_x # 根据实际硬件方向调整 new_v = current_v + step_y # 增强椭圆检测稳定性(在椭圆处理部分) min_ellipse_size = 20 # 增加最小椭圆尺寸过滤 max_ellipse_size = 300 if axis1 < min_ellipse_size or axis2 < min_ellipse_size or \ axis1 > max_ellipse_size or axis2 > max_ellipse_size: continue # 跳过过小或过大的椭圆 # 添加积分抗饱和(在PID类中) def compute(self, measured_value): # ...原有计算... # 添加积分抗饱和逻辑 if abs(self.u) > self.integral_max * 0.8: # 当输出接近极限时 self.integral *= 0.9 # 衰减积分项 return self.u, error ``` ### 完整修改后的核心代码 ```python # ...(保留原有导入和初始化代码)... # PID控制器类(添加抗饱和) class PID: def __init__(self, Kp=0.31, Ki=0.070, Kd=0.3, setpoint=0.0, integral_max=1000.0, deadzone=30.0): # ...原有初始化... self.integral_max = integral_max def compute(self, measured_value): error = measured_value if abs(error) < self.deadzone: error = 0 self.integral += error self.integral = max(min(self.integral, self.integral_max), -self.integral_max) derivative = error - self.last_error self.last_error = error self.u = (self.Kp * error) + (self.Ki * self.integral) + (self.Kd * derivative) # 积分抗饱和 if abs(self.u) > self.integral_max * 0.8: self.integral *= 0.9 # 衰减积分项 print("积分抗饱和激活") print(f"PID输出: u={self.u:.2f}, 误差={error:.2f}") return self.u, error def main(): # ...(保留原有初始化)... # 椭圆尺寸过滤参数 min_ellipse_size = 20 max_ellipse_size = 300 while True: # ...(保留图像采集和处理)... if ellipses is not None: print(f"检测到椭圆: {len(ellipses)}个") for i, ellipse in enumerate(ellipses): # ...(保留椭圆参数提取)... # 添加椭圆尺寸过滤 if axis1 < min_ellipse_size or axis2 < min_ellipse_size or \ axis1 > max_ellipse_size or axis2 > max_ellipse_size: continue # ...(保留后续处理)... if len(center_queue) > 5: try: # ...(保留圆心计算)... # 修正偏差计算方向 deviation_x = center_x - average_center[0] # 目标位置减去当前位置 deviation_y = center_y - average_center[1] print(f"圆心偏差: dx={deviation_x:.1f}, dy={deviation_y:.1f}") if ellipse_detected: current_h, current_v = servo.get_position() u_x, _ = pid_x.compute(deviation_x) u_y, _ = pid_y.compute(deviation_y) # ...(保留比例因子计算)... # 修正舵机移动方向 new_h = current_h - step_x # 根据实际硬件方向调整 new_v = current_v + step_y servo.set_position(new_h, new_v, move_time=0.5) # ...(保留后续代码)... ``` ### 关键修改说明 1. **控制方向修正**: - 反转偏差计算方向:`deviation = 目标位置 - 当前位置` - 调整舵机移动方向:`new_h = current_h - step_x` 2. **椭圆检测增强**: - 添加最小/最大椭圆尺寸过滤(20-300像素) - 排除过大或过小的干扰目标 3. **PID抗饱和机制**: - 当输出接近极限值时衰减积分项(`integral *= 0.9`) - 防止积分项持续累积导致控制失效 4. **调试信息优化**: - 保留关键参数输出(PID输出、舵机位置) - 添加抗饱和激活提示 这些修改解决了控制方向错误、积分饱和问题,并增强了目标检测的稳定性,使系统能够正确收敛到目标位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值