【电赛学习笔记】MaxiCAM 项目实践——二维云台追踪指定目标

前言

本文是对视觉模块MaixCam实现二维云台人脸跟踪_哔哩哔哩_bilibili大佬的项目实践整理与拓展,侵权即删。

单路舵机基本控制

#导入必要模块
from maix import pwm, time , pinmap#定义全局变量,设初值
SERVO_FREQ = 50         #主频
SERVO_MIN_DUTY = 2.5    #最小角度占空比
SERVO_MAX_DUTY = 12.5   #最大角度占空比
#选择pwm通道
pwm_id = 7
#引脚功能映射
pinmap.set_pin_function("A19", "PWM7")#定义角度设置函数
def angle_to_duty(angle):return (SERVO_MAX_DUTY - SERVO_MIN_DUTY) / 180 * angle + SERVO_MIN_DUTY     #固定公式无需记忆#创建PWM对象
out = pwm.PWM(pwm_id, freq = SERVO_FREQ, duty = angle_to_duty(0), enable = True)for i in range(180):out.duty(angle_to_duty(i))time.sleep_ms(10)

上述代码实现了舵机从0°到180°的运动

舵机类的定义

class Servo:#设置属性SERVO_FREQ = 50         #主频SERVO_MIN_DUTY = 2.5    #最小角度占空比SERVO_MAX_DUTY = 12.5   #最大角度占空比SERVO_MAX_ANGLE = 180   #最大旋转角#初始化函数def __init__(self, pwm_id:int, angle:int) -> None:angle = Servo.SERVO_MAX_ANGLE if angle > Servo.SERVO_MAX_ANGLE else angleangle = 0 if angle < 0 else angleif pwm_id == 7:pinmap.set_pin_function("A19", "PWM7")self.pwm = pwm.PWM(pwm_id, freq = Servo.SERVO_FREQ, duty = self._angle_to_duty_(angle), enable = True)elif pwm_id == 6:pinmap.set_pin_function("A18", "PWM6")self.pwm = pwm.PWM(pwm_id, freq = Servo.SERVO_FREQ, duty = self._angle_to_duty_(angle), enable = True)elif pwm_id == 5:pinmap.set_pin_function("A17", "PWM5")self.pwm = pwm.PWM(pwm_id, freq = Servo.SERVO_FREQ, duty = self._angle_to_duty_(angle), enable = True)elif pwm_id == 4:pinmap.set_pin_function("A16", "PWM4")self.pwm = pwm.PWM(pwm_id, freq = Servo.SERVO_FREQ, duty = self._angle_to_duty_(angle), enable = True)def __del__(self) -> None :self.pwm.disable()def _angle_to_duty_(self,angle:int) -> float :return (Servo.SERVO_MAX_DUTY - Servo.SERVO_MIN_DUTY) / 180 * angle + Servo.SERVO_MIN_DUTY def angle(self, angle:int) -> None:angle = Servo.SERVO_MAX_ANGLE if angle > Servo.SERVO_MAX_ANGLE else angleangle = 0 if angle < 0 else angleself.pwm.duty(self._angle_to_duty_(angle))

关于Python中“类”的简介

下面以这一段 Servo 舵机控制类 为例子,把 Python 中“类的定义规则、各参数/变量的作用域与访问规则” 逐条拆开讲清。只要记住 3 句话就能不迷路:

  1. 类里定义的变量分 类变量实例变量

  2. 函数参数和返回值可以写“类型注解”,但运行时不强制检查。

  3. self. 的是实例自己的;不带的是类或局部临时的。


一、类的“壳子”怎么写

class Servo:...
  • class 关键字 + 类名(首字母大写,PEP8 规范)。

  • 冒号后缩进 4 空格,内部放 类变量、方法


二、类变量(Class Variables)

SERVO_FREQ      = 50
SERVO_MIN_DUTY  = 2.5
SERVO_MAX_DUTY  = 12.5
SERVO_MAX_ANGLE = 180
  • 写在类体里、任何方法外

  • 所有实例共享同一份;通过 类名.变量实例.变量 都能读

    Servo.SERVO_MAX_ANGLE   # 推荐
    my_servo.SERVO_MAX_ANGLE
  • 如果某个实例想“私自”改值,会变成该实例自己的同名属性,不会动到类变量。


三、实例变量(Instance Variables)

实例变量在 __init__ 里用 self.名字 = ... 绑定:

self.pwm = pwm.PWM(...)
  • 每个对象各有一份,生命周期随对象。

  • 访问必须通过实例:my_servo.pwm


四、构造函数 __init__

def __init__(self, pwm_id: int, angle: int) -> None:
位置含义
self固定第 1 参数,指向当前正在创建的对象本身
pwm_id: int形参 + 类型注解(告诉人/IDE 该传 int)。
angle: int同上。
-> None返回值注解:构造函数固定返回 None

五、形参、局部变量、类变量的区分示例

angle = Servo.SERVO_MAX_ANGLE if angle > Servo.SERVO_MAX_ANGLE else angle
  • 左边 angle局部变量(形参名被重新绑定)。

  • Servo.SERVO_MAX_ANGLE类变量

  • 没有 self. 前缀,所以不会存成实例属性。


六、私有“工具函数”的命名惯例

def _angle_to_duty_(self, angle: int) -> float:
  • 单下划线开头 _name 表示“内部使用”,Python 不会强制隐藏,仅提示程序员。

  • self → 实例方法,能访问实例变量 self.pwm

  • angle: int -> float 再次使用类型注解。


七、析构函数 __del__

def __del__(self) -> None:self.pwm.disable()
  • 对象被垃圾回收前自动调用;常用于释放硬件资源。

  • 同样带 self,但不建议依赖它做关键清理,CPython 不保证时机。


八、实例方法 angle

def angle(self, angle: int) -> None:
  • 调用方式:servo.angle(90)

  • 内部通过 self.pwm.duty(...) 修改实例自己的 PWM。


九、变量/属性的完整访问路径总结

写法指向
Servo.SERVO_FREQ类变量(所有实例共享)
self.pwm实例变量(当前对象私有)
angle(无前缀)局部变量(函数内临时)

十、快速记忆表

概念定义位置访问方式生命周期
类变量类体,方法外类.变量 / 实例.变量随类
实例变量__init__ 里用 self.实例.变量随实例
形参/局部变量函数参数或内部直接变量名函数调用期间

照以上规则,你就能看懂并写出任何类似的 Python 类。

项目实战——二位云台色块追踪

from maix import camera, display, image, app
import servo### 初始化 ###
# 舵机初始角度
INIT_POS_X = 90
INIT_POS_Y = 100
# 滤波系数(越小越平滑,响应越慢)
FILTER_FACTOR = 0.15
# PID 系数(已调好,可微调)
KP = 0.018
KD = 0.20# 摄像头与显示
cam = camera.Camera(320, 240)        # 分辨率可改,但需与后续一致
dis = display.Display()# 舵机(PWM6→水平,PWM7→垂直)
servo_x = servo.Servo(6, INIT_POS_X)
servo_y = servo.Servo(7, INIT_POS_Y)# 目标角度初值
target_x_pos = INIT_POS_X
target_y_pos = INIT_POS_Y
last_err_x_pos = 0
last_err_y_pos = 0# 图像中心
IMAGE_WIDTH  = 320
IMAGE_HEIGHT = 240# 红色色块的 LAB 阈值(需根据实际环境调整)
# 格式:(L_min, L_max, A_min, A_max, B_min, B_max)
color_threshold = [(0, 80, 30, 70, 10, 60)]while not app.need_exit():img = cam.read()# 查找色块:merge=True 合并相邻块,pixels_threshold 过滤小面积blobs = img.find_blobs(color_threshold, merge=True, pixels_threshold=300)if not blobs:          # 没检测到dis.show(img)continue# 取最大色块作为目标blob = max(blobs, key=lambda b: b.pixels())# 画框和中心十字img.draw_rect(blob.x(), blob.y(), blob.w(), blob.h(), color=image.COLOR_GREEN)img.draw_cross(blob.cx(), blob.cy(), color=image.COLOR_RED, size=5)# ---------- 横向 PID ----------err_x_pos = IMAGE_WIDTH / 2 - blob.cx()err_x_pos = FILTER_FACTOR * err_x_pos + (1 - FILTER_FACTOR) * last_err_x_posdelta_x_pos = KD * (err_x_pos - last_err_x_pos) + KP * err_x_poslast_err_x_pos = err_x_postarget_x_pos += delta_x_pos# ---------- 纵向 PID ----------err_y_pos = IMAGE_HEIGHT / 2 - blob.cy()err_y_pos = FILTER_FACTOR * err_y_pos + (1 - FILTER_FACTOR) * last_err_y_posdelta_y_pos = KD * (err_y_pos - last_err_y_pos) + KP * err_y_poslast_err_y_pos = err_y_postarget_y_pos += delta_y_pos# 舵机角度限幅(0°~180°)target_x_pos = max(0, min(180, target_x_pos))target_y_pos = max(0, min(180, target_y_pos))# 驱动舵机servo_x.angle(int(target_x_pos))servo_y.angle(int(target_y_pos))dis.show(img)

PID部分解释

零基础也能听懂的 PID 小车比喻
(把“色块追踪”想成“让小汽车自动开到路中间”)

────────────────────

  1. 先认识三个字母
    P —— Proportional 比例
    I —— Integral 积分
    D —— Derivative 微分

(先不用背英文,记住它们各自干的事就行)

────────────────────
2. 把问题换成生活例子

• 你坐在一辆玩具小汽车里,车要停在一条长路的正中间。
• 你每隔 1 秒钟往窗外看一眼,测一下“车身离中线的距离”(这个距离就是误差 err)。
• 每一次看完,你就给方向盘一个“修正量”(delta),让车往中线靠。

PID 就是决定“修正量”的三兄弟。
────────────────────
3. 三兄弟分别做什么?

① 大哥 P(比例):
“离得越远,打得越猛!”
公式:P 部分 = KP × err
• KP 是“比例系数”,像方向盘灵敏度。
• 如果 KP 太小,车慢吞吞;KP 太大,车猛冲过头。

② 二哥 D(微分):
“快撞线了,赶紧松手!”
公式:D 部分 = KD × (err − last_err)
• 只看“误差变化的速度”。
• 当车快速接近中线时,D 会反向拉一把,避免冲过头。
• 相当于“阻尼”,让车不晃。

③ 小弟 I(积分):
“怎么老差一点?慢慢加把劲!”
• 把历史上的误差都加起来,再乘一个系数 KI。
• 对小误差做长期“补偿”。
• 本例为了简单,把 I 关掉(KI=0),所以代码里只有 P 和 D。

────────────────────
4. 代码逐句翻译

以横向为例:

err_x_pos = IMAGE_WIDTH/2 - blob.cx()

→ 看一眼:色块中心离画面中心有多少像素。

err_x_pos = FILTER_FACTOR*err_x_pos + (1-FILTER_FACTOR)*last_err_x_pos

→ 先做个“小滤波”,让测量值别太跳(和 PID 无关,只是让数据平滑)。

delta_x_pos = KD*(err_x_pos - last_err_x_pos) + KP*err_x_pos

→ 把 P 和 D 两个修正量合在一起:
• KPerr_x_pos → 大哥 P:离得多就转得多。
• KD
(err-last) → 二哥 D:如果误差变化很快,就减速。

last_err_x_pos = err_x_pos

→ 把这次误差存起来,下次算 D 时用。

target_x_pos += delta_x_pos

→ 方向盘最终转角 = 上次转角 + 本次修正量。

纵向同理,只是换了一个方向。

────────────────────
5. 调参口诀(小白速成)

  1. 先把 KD 设为 0,只调 KP:

    • 车慢 → 增大 KP

    • 车抖动 → 减小 KP

  2. 再加 KD:

    • 车冲到中线停不下来 → 增大 KD

    • 车变得迟钝 → 减小 KD

  3. 如果静止时总有固定误差,再加一点 KI(本例不需要)。

一句话总结
P 管“现在有多偏”,D 管“偏得有多快”,I 管“长期小偏差”,三兄弟一起用力,就能把色块牢牢地锁在画面正中央!

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

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

相关文章

深入解析 ArkUI 触摸事件机制:从点击到滑动的开发全流程

摘要 随着 HarmonyOS NEXT 的不断发展&#xff0c;ArkUI 逐渐成为主流的 UI 构建方式。而用户交互在任何应用中都是基础而又关键的一环&#xff0c;如何利用 ArkUI 提供的触摸事件机制&#xff0c;如 onTouch、onClick、onSwipe 等&#xff0c;来实现自然、顺滑、用户友好的交互…

Tailwind CSS 自定义工具类与主题配置指南

一、自定义工具类配置在 src/tailwind.css 文件中&#xff0c;我们可以通过 layer utilities 指令添加自定义工具类&#xff1a;tailwind base; tailwind components; tailwind utilities;layer utilities {/* 自定义工具 上下浮动效果 */.animate-floatY {animation: floatY 3…

【代码随想录二刷|704.二分查找、27.移除元素、977.有序数组的平方】

704.二分查找 题目链接&#xff1a;704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int search(vector<int>& nums, int target) {//不用二分查找&#xff0c;直接求// for(int i0;i<nums.size();i){// if(nums[i]target)…

基于Vue的工业设备大屏可视化模板(含设备地图分布+宣传模块+报表展示+三维模型加载预览)

场景 为实现工业设备可视化大屏需求&#xff0c;可实现基于地图的设备数据管理&#xff0c;点击具体设备可进行详细介绍和三维模型展示。 可播放宣传视频&#xff0c;可展示PM数据报表等数据&#xff0c;可接受报警数据提醒、统计等数据。 基于现有开源平台框架进行二次改造…

堆(Heap)优先级队列(Priority Queue)

一、堆的概念堆&#xff08;Heap&#xff09;是一种特殊的基于树的数据结构&#xff0c;通常分为最大堆和最小堆两种类型。它满足堆属性&#xff1a;对于最大堆&#xff0c;父节点的值总是大于或等于其子节点的值&#xff1b;而对于最小堆&#xff0c;父节点的值总是小于或等于…

踩坑记录:因版本不匹配导致 Boost 1.85 编译失败的完整解决过程

踩坑记录&#xff1a;因版本不匹配导致 Boost 1.85 编译失败的完整解决过程 转载请注明出处&#xff0c;欢迎评论区交流。 背景 最近在 Windows 11 VS2022 环境下尝试用 b2 编译 Boost 1.85.0&#xff0c;结果一路踩坑&#xff0c;最后发现罪魁祸首是 Boost.Build 自带的 msv…

InfluxDB Line Protocol 协议深度剖析(二)

四、Line Protocol 写入操作与实践&#xff08;一&#xff09;HTTP API 写入使用 HTTP API 是通过 Line Protocol 写入数据到 InfluxDB 的常用方式。InfluxDB 1.x&#xff1a;请求方式为 POST&#xff0c;URL 格式为 “http://host:port/write?dbdatabase_name”。其中&#x…

负载均衡:提升业务性能的关键技术

一.负载均衡&#xff08;3.6 &#xff09;1.1.什么是负载均衡&#xff1a;负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均 衡将特定的业务(web服务、网络流量等)分担给指定的一个或多个…

【STM32项目】智能家居(版本1)

✌️✌️大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是基于《基于STM32的智能家居设计》。 目录 1、系统功能 2.1、硬件清单 2.2、功能介绍 2.3、控制模式 2、演示视频和实物 3、系统设计框图 4、软件设计流程图 5、原理图 6、主…

OpenSCA开源社区每日安全漏洞及投毒情报资讯—2025年7月24日

2025年7月24日安全风险情报资讯在野漏洞风险&#xff08;CVE未收录&#xff09;&#xff1a;1公开漏洞精选&#xff1a;2组件投毒情报&#xff1a;2在野漏洞风险&#xff08;CVE未收录&#xff09;1.1 gemini-cli项目潜在命令注入漏洞项目详情项目描述&#xff1a;gemini-cli是…

飞算 JavaAI 深度实战:从老项目重构到全栈开发的降本增效密码

飞算 JavaAI 深度实战&#xff1a;从老项目重构到全栈开发的降本增效密码引言正文一、智能引导模块&#xff1a;老项目重构的 “手术刀” 级解决方案1.1 本地化智能分析&#xff1a;IDEA 插件实操演示1.1.1 &#x1f4cc; IDEA 插件安装步骤1.1.1.1 首先打开idea工具&#xff0…

分布式推客系统开发全解:微服务拆分、佣金结算与风控设计

一、推客系统概述与市场背景推客系统&#xff08;也称为分销系统或社交电商系统&#xff09;已成为现代电商平台和内容平台的重要增长引擎。根据最新统计数据&#xff0c;2023年社交电商市场规模已突破3万亿元&#xff0c;占整体电商市场份额的25%以上。推客系统的核心价值在于…

Linux tcpdump 抓取udp 报文

一、tcpdump 支持命令选项tcpdump -i # 指定监听网络接口tcpdump -w # 将捕获到的信息保存到文件中&#xff0c;且不分析和打印在屏幕tcpdump -r # 从文件中读取数据tcpdump -n # 不把 ip 转化成域名tcpdump -t # 在每行的输出中不显示时间tcpdump -v # 产生详细的输出tc…

Oracle数据块8KB、OS默认认块管理4KB,是否需调整大小为一致?

上班路上&#xff0c;脑中忽然闪现一个问题&#xff1a;Oracle数据库块大小&#xff08;8KB&#xff09;、操作系统文件系统块大小&#xff08;4KB&#xff09;&#xff0c;为了减少IOPS&#xff0c;需不需要调整为一致&#xff1f;在数据块保持一致的情况下&#xff0c;针对频…

卡尔曼滤波器噪声方差设置对性能影响的仿真研究

卡尔曼滤波器噪声方差设置对性能影响的仿真研究 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家,觉得好请收藏。点击跳转到网站。 1. 引言 卡尔曼滤波器是一种广泛应用于信号处理、控制系统、导航系统等领域的递归估计算法。它通过对系…

“多线程修路:当count++变成灾难现场”

1.现象 当我们操作一个线程池的时候&#xff0c;可能需要去计数&#xff0c;也就是统计count&#xff0c;那我们这里有一个疑问&#xff0c;会不会产生线程安全问题&#xff1f; 毫无疑问绝对会有线程安全问题。在线程池环境中&#xff0c;多个线程并发访问和修改一个共享的 co…

GaussDB null的用法

1 null的定义null 空值代表丢失的未知数据。 默认情况下&#xff0c;表列可以保存 null 值。 本章解释 is null 和 is not null 操作符。2 null值的赘述如果表中的列是可选的&#xff0c;那么我们可以插入一个新记录或更新一个现有记录&#xff0c;而无 需向列添加一个值。这意…

智慧农业新图景:物联网如何精准守护作物生长​

在传统农业生产模式下&#xff0c;农民往往凭借经验判断作物生长需求&#xff0c;灌溉、施肥缺乏精准性&#xff0c;导致水资源浪费、土壤板结、作物产量与品质难以提升等问题。加之气候变化无常&#xff0c;极端天气频发&#xff0c;给农业生产带来诸多不确定性&#xff0c;传…

[ComfyUI] -入门2- 小白零基础搭建ComfyUI图像生成环境教程

AI图像生成已经成为AIGC(人工智能生成内容)领域的重要组成部分,而ComfyUI作为一款可视化的Stable Diffusion工作流工具,以其模块化、高度自由化的特点吸引了越来越多创作者的关注。本文将手把手教你如何在Windows系统下,从零搭建属于自己的ComfyUI图像生成环境。 一、Comf…

java设计模式 -【单例模式】

单例模式的定义 单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。常用于需要控制资源或共享状态的场景&#xff0c;例如数据库连接、日志记录器等 单例模式的实现方式 饿汉式&…