基于Openmv的追小球的云台
介绍
在这篇文章,我会先介绍需要用到且需要注意的函数,之后再给出整体代码
在追小球的云台中,比较重要的部分就是云台(实质上就是舵机)的控制以及对识别的色块位置进行处理得到相应信息后控制云台进行运动
1、舵机模块和PID模块的导入
需要注意的是,PID还需要官方提供的PID文件(当然你要是把整体代码全部移植过来也是可以的)
from pyb import PID
from pyb import Servo
2、初始化舵机端口
这里初始化舵机端口,我的理解是就相当于一个宏定义
sp_servo=Servo(1)
cz_servo=Servo(2)
3、校准舵机,设置PWM信号的范围
sp_servo.calibration(500, 2500, 1500)
cz_servo.calibration(500, 2500, 1500)
这里的代码根据之前的初始化舵机端口名字来写,不同名不一样
这里用到的函数是
Servo.calibration([pulse_min,pulse_max,pulse_centre[,pulse_angle_90,pulse_speed_100]])
它后面的两个参数不需要用到,我们只对前三个参数进行说明
pulse_min:设置的最小脉冲宽度
pulse_max:设置的最大脉冲宽度
pulse_centre:中心(90度)\0度对应的位置
4、初始化PID控制器
PID控制器的初始化分为两种情况
情况1是在上机调试时的情况,情况2时在脱机运行时的情况
按道理来说,上机调试时由于数据的传输是需要时间的,也就是实时数据的获取存在延迟;而脱机运行时数据的传输更快,延迟更小
根据实时数据获取的延迟来看,在脱机运行时,由于数据传输的实时性很好,所以PID参数可以采用大一些的值;同理,在上机调试时,由于数据的实时性较差,所以采用更小的PID参数来进行调节
根据我上面的阐述,在设置PID参数时,脱机设置较大值,上机设置较小值;不知道是不是官方给出的代码出现了错误,它给出来的PID参数设置与我上述设置相反(我也不确定到底是谁错了)
sp_pid = PID(p=0.1, i=0, imax=90)cz_pid = PID(p=0.1, i=0, imax=90)
5、判断最大色块
由于在进行小球的追踪时,肯定会有其他小球之外的色块存在,所以在这个时候,我们只对最大的色块进行处理(默认最大色块为小球)
def find_max(blobs):max_size = 0max_blob = Nonefor blob in blobs:if blob[2] * blob[3] > max_size:max_blob = blobmax_size = blob[2] * blob[3]return max_blob
对于判断最大色块,我们定义了一个函数find_max(blobs)来寻找最大色块
在这判断色块大小是根据色块查找函数find_blobs(thresholds)返回的blob对象列表的值来确定的
在Openmv官方例程和函数库中并没有给出blob对象的值,所以我们我们自己写一个色块寻找程序,并打印出blob对象的值
可以看到blob对象的值如下图所示
所以blob[2]、[3]分别是blob对象的第3、4个参数,也就是色块的宽度和色块的高度,两个参数相乘即为色块的面积
6、误差的获取
通过用最大色块返回的中心坐标值减去图像中心坐标值,获得误差
sp_error = max_blob.cx() - img.width() / 2
cz_error = max_blob.cy() - img.height() / 2
7、误差的处理
sp_output = sp_pid.get_pid(sp_error, 1) / 2cz_output = cz_pid.get_pid(cz_error, 1)sp_servo.angle(sp_servo.angle() + sp_output)cz_servo.angle(cz_servo.angle() - cz_output)
对误差的控制引入了PID的控制,在这里我就不对涉及pid.py文件的部分作说明,这两天会再写一篇文章对该文件及PID算法进行说明
8、main.py代码
import sensor,image,time
from pyb import PID
from pyb import Servo#初始化用于平移和倾斜的舵机(我的理解是这里就是做了一个宏定义)
sp_servo=Servo(1)
cz_servo=Servo(2)# 校准舵机,设置PWM信号占空比的范围
sp_servo.calibration(500, 2500, 1500)
cz_servo.calibration(500, 2500, 1500)# 初始化平移和倾斜的PID控制器
# 脱机运行或者禁用图像传输时使用这些PID值
sp_pid = PID(p=0.07, i=0, imax=90)
cz_pid = PID(p=0.05, i=0, imax=90)# 如果在线调试,使用这些PID值,之所以在线调试和脱机调试用不一样的PID参数,是因为在线调试数据有延迟
# pan_pid = PID(p=0.1, i=0, imax=90)
# tilt_pid = PID(p=0.1, i=0, imax=90)sensor.reset() # 初始化摄像头传感器
sensor.set_pixformat(sensor.RGB565) # 设置像素格式为RGB565
sensor.set_framesize(sensor.QQVGA) # 设置分辨率为QQVGA以提高速度
sensor.skip_frames(1000) # 让新设置生效
sensor.set_auto_whitebal(False) # 关闭自动白平衡
sensor.set_auto_gain(False) #关闭自动增益
clock = time.clock() # 跟踪每秒帧数(FPS)#根据find_blobs()函数返回的blob列表的blob对象的第3、4个参数分别是像素的宽和高
def find_max(blobs):#每次调用该函数都会对参数作初始化max_size = 0max_blob = Nonefor blob in blobs:if blob[2] * blob[3] > max_size:max_blob = blobmax_size = blob[2] * blob[3]return max_blobwhile(True):clock.tick() # 跟踪每次快照之间经过的毫秒数img = sensor.snapshot() # 拍照并返回图像blobs = img.find_blobs([red_threshold])#如果识别到了色块if blobs:max_blob = find_max(blobs)#计算水平方向和垂直方向和图像中心的距离作为误差值sp_error = max_blob.cx() - img.width() / 2cz_error = max_blob.cy() - img.height() / 2print("sp_error:", sp_error)print("cz_error:", cz_error)img.draw_rectangle(max_blob.rect()) # 画出矩形img.draw_cross(max_blob.cx(), max_blob.cy()) # 画出十字sp_output = sp_pid.get_pid(sp_error, 1) / 2cz_output = cz_pid.get_pid(cz_error, 1)print("sp_output:", sp_output)print("cz_output:", cz_output)sp_servo.angle(sp_servo.angle() + sp_output)cz_servo.angle(cz_servo.angle() - cz_output)
9、pid.py代码
from pyb import millis # 导入 pyboard 的 millis 函数,用于获取当前时间(毫秒)
from math import pi, isnan # 导入 pi 和 isnan 函数class PID:# 定义 PID 控制器的参数和状态变量_kp = _ki = _kd = _integrator = _imax = 0_last_error = _last_derivative = _last_t = 0_RC = 1/(2 * pi * 20) # RC 低通滤波器的时间常数def __init__(self, p=0, i=0, d=0, imax=0):# 初始化 PID 控制器的参数self._kp = float(p) # 比例系数self._ki = float(i) # 积分系数self._kd = float(d) # 微分系数self._imax = abs(imax) # 积分限制,防止积分饱和self._last_derivative = float('nan') # 最后的导数值初始化为 NaNdef get_pid(self, error, scaler):tnow = millis() # 获取当前时间dt = tnow - self._last_t # 计算时间差output = 0 # 初始化输出值if self._last_t == 0 or dt > 1000: # 如果是第一次运行或者时间差大于 1 秒dt = 0 # 重置时间差self.reset_I() # 重置积分器self._last_t = tnow # 更新最后时间戳delta_time = float(dt) / float(1000) # 将时间差转换为秒output += error * self._kp # 计算比例项if abs(self._kd) > 0 and dt > 0: # 如果微分系数大于 0 且时间差大于 0if isnan(self._last_derivative): # 如果最后的导数值为 NaNderivative = 0 # 设置导数为 0self._last_derivative = 0 # 重置最后的导数值else:derivative = (error - self._last_error) / delta_time # 计算误差的导数# 使用低通滤波器平滑导数值derivative = self._last_derivative + ((delta_time / (self._RC + delta_time)) * (derivative - self._last_derivative))self._last_error = error # 更新最后的误差值self._last_derivative = derivative # 更新最后的导数值output += self._kd * derivative # 计算微分项并加到输出中output *= scaler # 按比例缩放输出值if abs(self._ki) > 0 and dt > 0: # 如果积分系数大于 0 且时间差大于 0self._integrator += (error * self._ki) * scaler * delta_time # 计算积分项并加到积分器中# 限制积分器的值在 -imax 和 imax 之间,防止积分饱和if self._integrator < -self._imax:self._integrator = -self._imaxelif self._integrator > self._imax:self._integrator = self._imaxoutput += self._integrator # 将积分项加到输出中return output # 返回计算的 PID 控制器输出值def reset_I(self):self._integrator = 0 # 重置积分器self._last_derivative = float('nan') # 重置最后的导数值为 NaN
结语
如果大家发现有什么不对的地方,请斧正!
相关文章:

基于Openmv的追小球的云台
介绍 在这篇文章,我会先介绍需要用到且需要注意的函数,之后再给出整体代码 在追小球的云台中,比较重要的部分就是云台(实质上就是舵机)的控制以及对识别的色块位置进行处理得到相应信息后控制云台进行运动 1、舵机模…...
关于scrapy模块中setting.py文件的介绍
作用 在Scrapy框架中,settings.py 文件起着非常重要的作用,它用于配置和控制整个Scrapy爬虫项目的行为、性能和功能。 setting.py文件的介绍 # Scrapy settings for haodaifu project # # For simplicity, this file contains only settings consider…...
laravel Blade 指令的趣味性
首先,我们通过几个要点来解释 Blade 引擎的工作原理。 您选择一个 Blade 模板进行渲染。引擎使用一系列正则表达式来解析和编译模板。该引擎生成一个普通的 PHP 文件并将其写入磁盘(以便将其缓存以供将来渲染)。包含 PHP 文件并使用输出缓冲…...

【面试题】等保(等级保护)的工作流程
等保(等级保护)的工作流程主要包括以下几个步骤,以下将详细分点介绍: 系统定级: 确定定级对象:根据《信息系统等级保护管理办法》和《信息系统等级保护定级指南》的要求,确定需要进行等级保护的…...
python调用麦克风和扬声器,并调用阿里云实时语音转文字
import time import queue import sounddevice as sd import numpy as np import nls import sys# 阿里云配置信息 URL "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1" TOKEN "XXXX" # 参考https://help.aliyun.com/document_detail/450255.html获…...
描述在React中集成第三方库(如Redux或React Router)的常见模式。
在React中集成第三方库,如状态管理库Redux或路由库React Router,通常遵循一些常见的模式和最佳实践。下面是一些集成这些库的步骤和模式: 集成Redux 安装Redux及相关包: 安装Redux及其中间件(如redux-thunk或redux-saga…...
JavaScript语法特性篇-空值合并运算符(??)
1、基本使用 空值合并运算符(??)英文名称为 Nullish coalescing operator,是一个逻辑运算符。 特性:当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。 const foo nul…...

rancher快照备份至S3
巧用rancher的S3快照备份功能,快速实现集群复制、集群转移、完全崩溃后的极限修复 1.进入集群管理,在对应的集群菜单后,点击编辑配置 2.选择ETCD,启用,Backup Snapshots to S3选项 并填入你的minio 3 配置成功后 手…...

ChatGPT API教程在线对接OpenAI APIKey技术教程
一、OpenAI基本库介绍 您可以通过 HTTP 请求与 API 进行交互,这可以通过任何编程语言实现。我们提供官方的 Python 绑定、官方的 Node.js 库,以及由社区维护的库。 要安装官方的 Python 绑定,请运行以下命令: pip install open…...
随心而遇,跟着感觉走
分数限制下,选好专业还是选好学校? 24年高考结束,很多学生犹豫选择专业还是好学校,我的建议是,选择好学校。 本人体验来说,电子,工地,计科,数学,工科相关的…...
LeetCode题练习与总结:只出现一次的数字--136
一、题目描述 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 示例 1 : …...
常见的中间件都在解决什么问题?
常见的中间件都在解决什么问题 RocketMQ RocketMQ 是一款功能强大的分布式消息系统。 RocketMQ 源码地址:https://github.com/apache/rocketmq(opens new window) RocketMQ 官方网站:https://rocketmq.apache.org 什么场景下用 RocketMQ?…...
微信小程序-scroll-view实现上拉加载和下拉刷新
一.scroll-view实现上拉加载 scroll-view组件通过自身一些属性实现上拉加载的功能。 lower-threshold“100"属性表示距离底部多少px就会实现触发下拉加载的事件。 类似于在.json文件里面配置"onReachBottomDistance”: 100 bindscrolltolower"getMore"属…...
TS中interface和type的区别
在 TypeScript 中,interface 和 type 都可以用来定义对象的类型,但它们之间存在一些差异。 以下是 interface 和 type 的主要区别: 扩展(Extending): interface 可以通过 extends 关键字来扩展其他 interface。interfa…...

Hightec编译器系列之高级调试技巧精华总结
Hightec编译器系列之高级调试技巧精华总结 小T为了便于大家理解,本文的思维导图大纲如下: 之前可能很多小伙伴没有使用过Hightec编译器,大家可以参考小T之前的文章《Hightec编译器系列之白嫖就是爽》可以下载一年试用版本。 小T使用过适配英…...

【论文笔记】LoRA LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS
题目:LoRA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS 来源: ICLR 2022 模型名称: LoRA 论文链接: https://arxiv.org/abs/2106.09685 项目链接: https://github.com/microsoft/LoRA 文章目录 摘要引言问题定义现有方法的问题方法将 LORA 应用于 Transformer 实…...

【Sa-Token|4】Sa-Token微服务项目应用
若微服务数量多,如果每个服务都改动,工作量大,则可以只在网关和用户中心进行改动,也是可以实现服务之间的跳转。 这种方式可以通过在网关服务中生成和验证 Sa-Token,并将其与现有的 Token关联存储在 Redis 中。用户中心…...
鸿蒙开发系统基础能力:【@ohos.hilog (日志打印)】
日志打印 hilog日志系统,使应用/服务可以按照指定级别、标识和格式字符串输出日志内容,帮助开发者了解应用/服务的运行状态,更好地调试程序。 说明: 本模块首批接口从API version 7开始支持。后续版本的新增接口,采用…...

SpringMVC系列十: 中文乱码处理与JSON处理
文章目录 中文乱码处理自定义中文乱码过滤器Spring提供的过滤器处理中文 处理json和HttpMessageConverter<T>处理JSON-ResponseBody处理JSON-RequestBody处理JSON-注意事项和细节HttpMessageConverter<T\>文件下载-ResponseEntity<T\>作业布置 上一讲, 我们学…...

使用MyBatisPlus进行字段的自动填充
使用MyBatisPlus进行字段的自动填充 需求场景 当我们往数据库里面插入一条数据,或者是更新一条数据时,一般都需要标记创建时间create_time和更新时间update_time的值,但是如果我们每张表的每个请求,在执行sql语句的时候我们都手…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...