基于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语句的时候我们都手…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
