基于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语句的时候我们都手…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候,显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...
自定义线程池1.2
自定义线程池 1.2 1. 简介 上次我们实现了 1.1 版本,将线程池中的线程数量交给使用者决定,并且将线程的创建延迟到任务提交的时候,在本文中我们将对这个版本进行如下的优化: 在新建线程时交给线程一个任务。让线程在某种情况下…...
