import sensor, time, image,lcd from machine import Timer,UART,PWM from Maix import GPIO from fpioa_manager import fm Angle = 0 Angle2 = 0 Servo1_Pin = 1 Servo2_Pin = 2 #sensor.reset() #sensor.reset(freq=24000000, dual_buff=True) #sensor.set_pixformat(sensor.RGB565) #sensor.set_framesize(sensor.QVGA) #sensor.set_auto_exposure(1) #sensor.set_auto_gain(0, gain_db=12) #sensor.set_auto_whitebal(0, rgb_gain_db=(0, 0, 0)) #sensor.set_vflip(1) #sensor.set_hmirror(1) #sensor.skip_frames(time=2000) #摄像头初始化 sensor.reset() sensor.reset(freq=24000000, dual_buff=True) sensor.set_auto_gain(1, gain_db=12) sensor.set_auto_whitebal(0, rgb_gain_db=(0, 0, 0)) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.set_vflip(1) #后置模式,所见即所得 sensor.run(1) sensor.skip_frames(30) #映射串口引脚 fm.register(15 , fm.fpioa.UART1_RX, force=True) fm.register(16, fm.fpioa.UART1_TX, force=True) #初始化串口 uart = UART(UART.UART1, 9600, read_buf_len=4096) #lcd初始化 lcd.init() clock=time.clock() class PID_property(): Kp = 0 Ki = 0 Kd = 0 P = 0 I = 0 D = 0 now_error = 0 last_error = 0 now_error_LeiJi = 0 last_last_error = 0 Out_posPID = 0 OUT_PWM = 0 S1_PD = PID_property() ###############上面 舵机 S1_PD.Kp = 0.555 S1_PD.Ki = 0.00005 S1_PD.Kd = 0.0005 S2_PD = PID_property() ###############下面 舵机 S2_PD.Kp = 0.739 S2_PD.Ki = 0.00009 S2_PD.Kd = 0.0008 def PID_Pos(Sx_PD,erro): Sx_PD.now_error = erro #误差 Sx_PD.now_error_LeiJi = Sx_PD.now_error + Sx_PD.now_error_LeiJi#累计误差_求和 Sx_PD.P = Sx_PD.Kp * Sx_PD.now_error Sx_PD.I = Sx_PD.Ki * Sx_PD.now_error_LeiJi Sx_PD.D = Sx_PD.Kd * (Sx_PD.now_error - Sx_PD.last_error) Sx_PD.Out_posPID = Sx_PD.P + Sx_PD.I + Sx_PD.D Sx_PD.last_error = Sx_PD.now_error Sx_PD.OUT_PWM = Sx_PD.Out_posPID return Sx_PD.OUT_PWM class color_property(): cx = 0 cy = 0 flag = 0 color = 0 density = 0 pixels_max = 0 led_flag = 0 color_threshold = (0, 0, 0, 0, 0, 0) color_roi = (0, 0, 320, 240) color_x_stride = 1 color_y_stride = 1 color_pixels_threshold = 10 color_area_threshold = 10 color_merge = True color_margin = 1 black = color_property() black.color_threshold = (31, 91, 6, 33, 19, 6) black.color_roi = (0, 0, 320, 240) black.color_x_stride = 1 black.color_y_stride = 1 black.color_pixels_threshold = 10 black.color_area_threshold = 10 black.color_merge = True black.color_margin = 1 tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM) S1 = PWM(tim, freq=50, duty=0, pin=Servo1_Pin) tim2 = Timer(Timer.TIMER2, Timer.CHANNEL2, mode=Timer.MODE_PWM) S2 = PWM(tim2, freq=50, duty=0, pin=Servo2_Pin) def Servo(S_num): global Angle if Angle > 90: Angle = 90 elif Angle < -90: Angle = -90 S_num.duty((Angle+90)/180*10+2.5) # 占空比范围从0到1 def Servo2(S_num): global Angle2 if Angle2 > 90: Angle2 = 90 elif Angle2 < -90: Angle2 = -90 S_num.duty((Angle2+90)/180*10+2.5) # 占空比范围从0到1 def find_max(blobs): max_size = 0 max_blob = None for blob in blobs: if blob[2] * blob[3] > max_size: max_blob = blob max_size = blob[2] * blob[3] return max_blob while True: err = 0 img = sensor.snapshot() blobs = img.find_blobs([black.color_threshold], area_threshold=50, pixels_threshold=50) if blobs: b = find_max(blobs) img.draw_rectangle(b[0:4]) img.draw_cross(b[5], b[6], color=(0, 255, 0), size=15) x_pos = b[5] # 中心位置 y_pos = b[6] # 中心位置 width = b[2] # 宽度 height = b[3] # 高度 area = width * height # 面积 X = '%03d' % x_pos Y = '%05d' % y_pos DATA = '@' + X + Y + 'A' + 'F' uart.write(DATA) print(DATA) Angle = PID_Pos(S1_PD, x_pos - 160) Angle2 = PID_Pos(S2_PD, y_pos - 120) Servo2(S2) Servo(S1) print("矩形面积:", area) print("矩形长度:", width) print("矩形宽度:", height) print("中心坐标X:", x_pos) print("中心坐标Y:", y_pos) # 打印占空比 servo1_duty = ((Angle+90)/180*10+2.5) servo2_duty = ((Angle2+90)/180*10+2.5) print("舵机1占空比:", servo1_duty) print("舵机2占空比:", servo2_duty) print(Angle) print(Angle2) # 在LCD上显示数据 # lcd.clear() img.draw_string(2, 5, "Servo1 Duty:{:.2f}".format(servo1_duty), scale=2, color=(255, 0, 0)) img.draw_string(2, 30, "Servo2 Duty:{:.2f}".format(servo2_duty), scale=2, color=(255, 0, 0)) img.draw_string(2, 55, "x_pos: {}".format(x_pos), scale=2, color=(255, 0, 0)) img.draw_string(2, 80, "y_pos: {}".format(y_pos), scale=2, color=(255, 0, 0)) else: # 如果没有检测到色块,则输出默认值 #print("矩形面积:", 0) #print("矩形长度:", 0) #print("矩形宽度:", 0) print("中心坐标X:", 160) print("中心坐标Y:", 120) x_pos=160 y_pos=120 X = '%03d' % x_pos Y = '%05d' % y_pos DATA = '@' + X + Y + 'A' + 'F' print(DATA) uart.write(DATA) # 在LCD上显示默认值 #lcd.clear() img.draw_string(2, 5, "NO", scale=3, color=(255, 0, 0)) img.draw_string(2, 35, "NO", scale=3, color=(255, 0, 0)) img.draw_string(2, 65, "x_pos:160", scale=3, color=(255, 0, 0)) img.draw_string(2, 95, "y_pos:120", scale=3, color=(255, 0, 0)) lcd.display(img) #LCD显示图片(逐行分析代码)
时间: 2025-07-20 16:19:20 浏览: 8
<think>我们被要求逐行解释一段使用MaixPy在K210芯片上控制云台追踪色块的Python代码。代码已经给出,我们将逐行解释其功能,并适当结合引用内容进行说明。
### 代码逐行解释
```python
import sensor, time, image, lcd
from machine import Timer, UART, PWM
from Maix import GPIO
from fpioa_manager import fm
```
- **功能**:导入必要的库模块。
- `sensor`:用于摄像头配置和图像捕获。
- `time`:用于时间相关操作(如延时)。
- `image`:提供图像处理功能。
- `lcd`:用于在液晶显示屏上显示图像。
- `Timer`, `UART`, `PWM`:从`machine`模块导入,分别用于定时器、串口通信和脉宽调制(控制舵机)。
- `GPIO`:用于通用输入输出控制(虽然代码中未直接使用)。
- `fm`:引脚映射管理,用于配置引脚功能(如将物理引脚映射为UART功能)。
---
```python
Angle = 0
Angle2 = 0
Servo1_Pin = 1
Servo2_Pin = 2
```
- **功能**:初始化全局变量。
- `Angle`和`Angle2`:分别存储两个舵机的目标角度(初始为0度)。
- `Servo1_Pin`和`Servo2_Pin`:定义舵机信号线连接的物理引脚编号(此处为1和2,实际硬件连接需对应)。
---
```python
# 摄像头初始化
sensor.reset()
sensor.reset(freq=24000000, dual_buff=True)
sensor.set_auto_gain(1, gain_db=12)
sensor.set_auto_whitebal(0, rgb_gain_db=(0, 0, 0))
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) # 后置模式,所见即所得
sensor.run(1)
sensor.skip_frames(30)
```
- **功能**:配置摄像头参数[^2][^4]。
- `sensor.reset()`:复位摄像头。
- `sensor.reset(freq=24000000, dual_buff=True)`:设置摄像头时钟频率为24MHz,并启用双缓冲(提高图像采集效率)。
- `sensor.set_auto_gain(1, gain_db=12)`:启用自动增益控制,初始增益为12dB值12。
- `sensor.set_auto_whitebal(0, ...)`:禁用自动白平衡,并设置RGB增益为0(避免颜色自动调整干扰色块识别)。
- `sensor.set_pixformat(sensor.RGB565)`:设置图像格式为RGB565(16位色彩)。
- `sensor.set_framesize(sensor.QVGA)`:设置图像分辨率为QVGA(320×240像素)。
- `sensor.set_vflip(1)`:垂直翻转图像(适用于摄像头倒置安装的情况)。
- `sensor.run(1)`:启动摄像头。
- `sensor.skip_frames(30)`:跳过前30帧,等待摄像头稳定。
---
```python
# 映射串口引脚
fm.register(15, fm.fpioa.UART1_RX, force=True)
fm.register(16, fm.fpioa.UART1_TX, force=True)
# 初始化串口
uart = UART(UART.UART1, 9600, read_buf_len=4096)
```
- **功能**:配置串口通信[^2]。
- `fm.register(15, fm.fpioa.UART1_RX, force=True)`:将物理引脚15映射为UART1的接收端(RX)。
- `fm.register(16, fm.fpioa.UART1_TX, force=True)`:将物理引脚16映射为UART1的发送端(TX)。
- `uart = UART(UART.UART1, 9600, read_buf_len=4096)`:初始化UART1,波特率9600,接收缓冲区大小为4096字节。
---
```python
# lcd初始化
lcd.init()
clock = time.clock()
```
- **功能**:初始化LCD显示屏并创建时钟对象。
- `lcd.init()`:启动LCD显示。
- `clock = time.clock()`:创建时钟对象用于帧率计算。
---
```python
class PID_property():
Kp = 0
Ki = 0
Kd = 0
P = 0
I = 0
D = 0
now_error = 0
last_error = 0
now_error_LeiJi = 0
last_last_error = 0
Out_posPID = 0
OUT_PWM = 0
```
- **功能**:定义PID控制器的属性类[^1][^4]。
- 包含比例(`Kp`)、积分(`Ki`)、微分(`Kd`)系数。
- 存储当前误差(`now_error`)、上一次误差(`last_error`)、累计误差(`now_error_LeiJi`)等中间变量。
- `Out_posPID`和`OUT_PWM`存储PID计算输出的控制量(角度或PWM占空比)。
---
```python
S1_PD = PID_property() # 上面舵机
S1_PD.Kp = 0.555
S1_PD.Ki = 0.00005
S1_PD.Kd = 0.0005
S2_PD = PID_property() # 下面舵机
S2_PD.Kp = 0.739
S2_PD.Ki = 0.00009
S2_PD.Kd = 0.0008
```
- **功能**:初始化两个舵机的PID控制器实例,并设置PID参数。
- `S1_PD`控制上方舵机(水平旋转),`S2_PD`控制下方舵机(垂直旋转)。
- 参数值(如`Kp=0.555`)需根据实际云台机械结构和响应调试确定[^1]。
---
```python
def PID_Pos(Sx_PD, erro):
Sx_PD.now_error = erro # 误差
Sx_PD.now_error_LeiJi = Sx_PD.now_error + Sx_PD.now_error_LeiJi # 累计误差_求和
Sx_PD.P = Sx_PD.Kp * Sx_PD.now_error
Sx_PD.I = Sx_PD.Ki * Sx_PD.now_error_LeiJi
Sx_PD.D = Sx_PD.Kd * (Sx_PD.now_error - Sx_PD.last_error)
Sx_PD.Out_posPID = Sx_PD.P + Sx_PD.I + Sx_PD.D
Sx_PD.last_error = Sx_PD.now_error
Sx_PD.OUT_PWM = Sx_PD.Out_posPID
return Sx_PD.OUT_PWM
```
- **功能**:实现位置式PID控制算法[^1][^4]。
- 输入:PID控制器实例`Sx_PD`和当前误差`erro`。
- 计算步骤:
1. 更新当前误差(`now_error`)。
2. 累加误差(积分项计算基础)。
3. 计算比例项(`P`)、积分项(`I`)和微分项(`D`)。
4. 输出控制量(`Out_posPID`)为三项之和。
5. 更新上一次误差(`last_error`)并返回输出值。
---
```python
class color_property():
cx = 0
cy = 0
flag = 0
color = 0
density = 0
pixels_max = 0
led_flag = 0
color_threshold = (0, 0, 0, 0, 0, 0)
color_roi = (0, 0, 320, 240)
color_x_stride = 1
color_y_stride = 1
color_pixels_threshold = 10
color_area_threshold = 10
color_merge = True
color_margin = 1
```
- **功能**:定义色块检测的属性类。
- 包含色块中心坐标(`cx`, `cy`)、颜色阈值(`color_threshold`)、检测区域(`color_roi`)等参数。
- 其他参数如`color_x_stride`(水平扫描步长)、`color_merge`(是否合并相邻色块)等用于优化检测过程。
---
```python
black = color_property()
black.color_threshold = (31, 91, 6, 33, 19, 6) # 示例阈值,实际需调整
black.color_roi = (0, 0, 320, 240)
black.color_x_stride = 1
black.color_y_stride = 1
black.color_pixels_threshold = 10
black.color_area_threshold = 10
black.color_merge = True
black.color_margin = 1
```
- **功能**:创建黑色色块的检测配置实例。
- 设置颜色阈值(LAB色彩空间,格式为`(L_min, L_max, A_min, A_max, B_min, B_max)`)。
- 其他参数保持默认,如全图检测(ROI为整个图像)等。
---
```python
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)
S1 = PWM(tim, freq=50, duty=0, pin=Servo1_Pin)
tim2 = Timer(Timer.TIMER2, Timer.CHANNEL2, mode=Timer.MODE_PWM)
S2 = PWM(tim2, freq=50, duty=0, pin=Servo2_Pin)
```
- **功能**:初始化舵机控制的PWM信号[^1][^3]。
- 使用定时器(`Timer0`和`Timer2`)生成50Hz的PWM信号(舵机标准控制频率)。
- `S1`和`S2`为PWM对象,分别控制两个舵机,初始占空比为0。
---
```python
def Servo(S_num):
global Angle
if Angle > 90:
Angle = 90
elif Angle < -90:
Angle = -90
S_num.duty((Angle + 90) / 180 * 10 + 2.5) # 占空比范围从0到1
```
- **功能**:控制舵机1(水平方向)转动到目标角度。
- 限幅:角度`Angle`限制在$[-90^\circ, 90^\circ]$范围内。
- 占空比计算:将角度映射到舵机控制脉冲宽度(通常0°对应2.5%占空比,180°对应12.5%占空比)。公式解析:
- 角度范围$[-90, 90]$ → 映射到$[0, 180]$:`(Angle + 90)`
- 转换为占空比:`(角度值)/180 * (12.5-2.5) + 2.5` → 简化为`(Angle+90)/180*10+2.5`
---
```python
def Servo2(S_num):
global Angle2
if Angle2 > 90:
Angle2 = 90
elif Angle2 < -90:
Angle2 = -90
S_num.duty((Angle2 + 90) / 180 * 10 + 2.5)
```
- **功能**:控制舵机2(垂直方向),逻辑同`Servo`。
---
```python
def find_max(blobs):
max_size = 0
max_blob = None
for blob in blobs:
if blob[2] * blob[3] > max_size:
max_blob = blob
max_size = blob[2] * blob[3]
return max_blob
```
- **功能**:从检测到的色块列表中找出面积最大的色块[^2][^4]。
- 遍历所有色块,比较其面积(宽度`blob[2]` × 高度`blob[3]`)。
- 返回面积最大的色块对象。
---
```python
while True:
err = 0
img = sensor.snapshot()
blobs = img.find_blobs([black.color_threshold], area_threshold=50, pixels_threshold=50)
```
- **功能**:主循环,持续捕获图像并检测色块。
- `img = sensor.snapshot()`:捕获一帧图像。
- `blobs = img.find_blobs(...)`:根据设定的阈值检测黑色色块,面积阈值和像素数阈值均为50(过滤小噪点)。
---
```python
if blobs:
b = find_max(blobs)
img.draw_rectangle(b[0:4])
img.draw_cross(b[5], b[6], color=(0, 255, 0), size=15)
```
- **功能**:如果检测到色块,则处理最大色块。
- `b = find_max(blobs)`:获取最大色块。
- `img.draw_rectangle(b[0:4])`:绘制色块矩形框。
- `img.draw_cross(b[5], b[6], ...)`:在色块中心绘制绿色十字(`b[5]`和`b[6]`为中心坐标)。
---
```python
x_pos = b[5] # 中心位置
y_pos = b[6] # 中心位置
width = b[2] # 宽度
height = b[3] # 高度
area = width * height # 面积
X = '%03d' % x_pos
Y = '%05d' % y_pos
DATA = '@' + X + Y + 'A' + 'F'
uart.write(DATA)
print(DATA)
```
- **功能**:提取色块信息并通过串口发送。
- 中心坐标`(x_pos, y_pos)`、宽度、高度和面积。
- 格式化坐标:`X`为3位数字(不足补0),`Y`为5位数字。
- 构造数据包`@XXXYYYYAF`(`@`开头,`AF`结尾)并通过串口发送[^2]。
---
```python
Angle = PID_Pos(S1_PD, x_pos - 160)
Angle2 = PID_Pos(S2_PD, y_pos - 120)
Servo2(S2)
Servo(S1)
```
- **功能**:计算舵机角度并驱动云台[^1][^4]。
- 水平误差:`x_pos - 160`(图像中心X坐标为160)。
- 垂直误差:`y_pos - 120`(图像中心Y坐标为120)。
- 通过PID计算舵机角度(`Angle`和`Angle2`)。
- 调用`Servo`和`Servo2`函数更新舵机位置。
---
```python
# 打印占空比和坐标信息(略)
# 在图像上叠加显示数据(略)
else:
# 未检测到色块时发送默认坐标(160,120)
x_pos = 160
y_pos = 120
# 构造相同格式的数据包并发送
# 在图像上显示"NO"提示
lcd.display(img) # 刷新LCD显示
```
- **功能**:无论是否检测到色块,都更新LCD显示内容。
- 检测到色块:显示舵机占空比、坐标等信息。
- 未检测到:显示默认坐标和"NO"提示。
### 总结
该代码实现了以下核心功能:
1. **摄像头配置**:初始化并设置参数,确保稳定采集图像。
2. **色块检测**:在每帧图像中查找符合阈值条件的最大黑色色块。
3. **PID控制**:根据色块中心与图像中心的误差,计算舵机调整角度。
4. **云台驱动**:通过PWM信号控制舵机转动,使色块向图像中心移动。
5. **数据通信**:将色块坐标通过串口发送(格式为`@XXXYYYYAF`)。
6. **状态显示**:在LCD上实时显示图像和调试信息。
此系统可用于电赛云台追踪等场景[^4],通过调整PID参数和颜色阈值可优化追踪效果[^1]。
§§相关问题§§
1. 如何调试PID参数以提高K210云台追踪的稳定性?
2. 在K210上如何优化颜色阈值以适应不同光照条件?
3. K210的串口通信协议如何设计以实现与其他设备(如STM32)的数据交互?
4. 如何扩展此代码以实现多目标追踪?
[^1]: 第七章 K210 云台追踪物体教程(基于Maix bit)。
[^2]: 基于STM32与K210的云台追踪装置。
[^3]: 基于K210开源AI智能跟随云台OpenFollow(基础篇)--(三)K210的MaixPy外设基础。
[^4]: 2023电赛E题云台激光追踪视觉实现(K210)。</think>### 逐行解析 K210 云台追踪色块代码
#### 1. 硬件初始化与参数配置
```python
import sensor, time, image, lcd
from machine import Timer, UART, PWM
from Maix import GPIO
from fpioa_manager import fm
```
- **功能**:导入核心库
- `sensor`:摄像头控制
- `machine`:硬件底层控制(定时器/UART/PWM)
- `fpioa_manager`:引脚功能映射管理[^3]
```python
Angle = 0 # 水平舵机角度
Angle2 = 0 # 垂直舵机角度
Servo1_Pin = 1 # 水平舵机引脚
Servo2_Pin = 2 # 垂直舵机引脚
```
- **功能**:初始化全局变量,存储舵机角度和硬件映射
#### 2. 摄像头配置
```python
sensor.reset()
sensor.reset(freq=24000000, dual_buff=True)
sensor.set_auto_gain(1, gain_db=12)
sensor.set_auto_whitebal(0, rgb_gain_db=(0, 0, 0))
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240分辨率
sensor.set_vflip(1) # 垂直翻转(后置摄像头)
sensor.run(1)
sensor.skip_frames(30) # 跳过初始不稳定帧
```
- **关键参数**:
- `dual_buff=True`:双缓冲提升帧率[^3]
- `gain_db=12`:固定增益值防止自动调整干扰识别
- `RGB565`:16位色深平衡性能与质量
- `QVGA`:优化处理速度(全分辨率VGA需更高算力)[^4]
#### 3. 通信接口初始化
```python
fm.register(15, fm.fpioa.UART1_RX, force=True)
fm.register(16, fm.fpioa.UART1_TX, force=True)
uart = UART(UART.UART1, 9600, read_buf_len=4096) # 串口通信
lcd.init() # 显示屏初始化
clock = time.clock() # 帧率计时器
```
- **功能**:配置UART引脚映射和参数,用于调试数据输出[^2]
#### 4. PID控制器实现
```python
class PID_property():
# PID参数和状态变量定义
...
def PID_Pos(Sx_PD, erro):
# 位置式PID计算
Sx_PD.now_error = erro
Sx_PD.now_error_LeiJi += erro # 误差累积(积分项)
Sx_PD.P = Sx_PD.Kp * Sx_PD.now_error
Sx_PD.I = Sx_PD.Ki * Sx_PD.now_error_LeiJi
Sx_PD.D = Sx_PD.Kd * (Sx_PD.now_error - Sx_PD.last_error)
Sx_PD.Out_posPID = Sx_PD.P + Sx_PD.I + Sx_PD.D
Sx_PD.last_error = Sx_PD.now_error
return Sx_PD.Out_posPID
```
- **控制原理**:
$$u(t) = K_p e(t) + K_i \int e(t)dt + K_d \frac{de(t)}{dt}$$
- $K_p$:比例系数(快速响应)
- $K_i$:积分系数(消除稳态误差)
- $K_d$:微分系数(抑制振荡)[^1]
#### 5. 色块识别参数
```python
class color_property():
# 颜色识别参数容器
...
black = color_property()
black.color_threshold = (31, 91, 6, 33, 19, 6) # LAB色域阈值
black.color_roi = (0, 0, 320, 240) # 全图检测区域
```
- **阈值说明**:LAB格式 `(L_min, L_max, A_min, A_max, B_min, B_max)`
- 适用于黑色物体识别(需根据实际光照调整)[^2]
#### 6. 舵机驱动
```python
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)
S1 = PWM(tim, freq=50, duty=0, pin=Servo1_Pin) # 水平舵机
def Servo(S_num):
global Angle
Angle = max(-90, min(90, Angle)) # 角度限幅
duty = (Angle + 90)/180*10 + 2.5 # 角度转占空比
S_num.duty(duty)
```
- **舵机控制原理**:
- 50Hz PWM信号(周期20ms)
- 占空比范围:2.5%(-90°)~12.5%(+90°)
- 转换公式:$duty = \frac{\theta + 90}{180} \times 10 + 2.5$[^1]
#### 7. 主控制循环
```python
while True:
img = sensor.snapshot() # 捕获图像
blobs = img.find_blobs([black.color_threshold], area_threshold=50, ...)
if blobs:
b = find_max(blobs) # 找最大色块
x_pos, y_pos = b[5], b[6] # 中心坐标
# PID计算舵机角度
Angle = PID_Pos(S1_PD, x_pos - 160) # 水平误差
Angle2 = PID_Pos(S2_PD, y_pos - 120) # 垂直误差
# 串口输出坐标数据
DATA = f'@{x_pos:03d}{y_pos:05d}AF'
uart.write(DATA)
# LCD显示调试信息
img.draw_string(2, 5, f"Servo1:{duty:.2f}", ...)
else:
# 目标丢失处理
uart.write('@160120AF') # 发送中心坐标
```
- **工作流程**:
1. 图像采集 → 2. 色块识别 → 3. 中心坐标计算 → 4. PID误差控制 → 5. 舵机驱动 → 6. 数据显示[^4]
### 关键技术与优化点
1. **双缓冲机制**:`dual_buff=True` 提升图像采集效率
2. **动态PID调参**:水平/垂直舵机使用独立参数($K_p$不同)
3. **目标丢失处理**:无目标时输出图像中心坐标防止舵机失控
4. **数据协议优化**:`@XXXYYYYAF` 格式串口数据帧(含校验)[^2]
### 典型应用场景
- 电赛E题激光追踪系统[^4]
- 智能跟随云台
- 工业分拣机械臂视觉引导
阅读全文
相关推荐













