【STM32+HAL】杆球控制系统

news2024/11/13 9:46:55

一、前言

2017年电赛出了道板球控制系统题目,现写一个简化版本——杆球控制系统,以此记录电赛集训生活。

二、题目分析

最终采取的方案是:OpenMV读取小球的当前位置,并将坐标值传给STM32端,再由32通过电机改变杆的位置来改变乒乓球位置,由此实现闭环控制。模式间切换通过按键实现。

三、 所用工具

1、电机:DengFOC的2208无刷云台电机(无刷电机控制精度更高,算法较舵机难)

2、芯片:STM32F407ZGT6

3、机械结构:建议3D打印,提高精度,减轻算法压力。

四、CubeMX配置

1、控制+计时定时器

频率 frequency = 84MHz / 84 / 1000 = 1000Hz,即周期为1ms

2、三路PWM输出定时器

FOC控制需三路PWM波,频率设为25kHz

有关DengFOC的更多内容,请移步B站搜索DengFOC。

3、LCD屏显示配置

详见【STM32+HAL】LCD显示及触摸初始化配置,这里不再赘述。

4、读取AS5600编码器

配置IIC模式读取编码器值,获取当前电机转动角度。

5、串口配置

串口一通过DMA与OpenMV通信,有关串口DMA传输的内容,详见【STM32+HAL】DMA应用

至此,CuebMX配置完毕。

五、OpenMV识别

代码不难,大家自行理解吧。想了解更多OpenMV技术细节详见【OPENMV】学习记录 (持续更新)

import sensor, image, time, ustruct
from pyb import UART

uart = UART(3, 115200, timeout_char=200)
uart.init(115200, bits=8, parity=None, stop=1)  # init with given parameters
roi = (30, 50, 126, 22)

# 初始化摄像头
sensor.reset()                        # 初始化感应器
sensor.set_pixformat(sensor.RGB565)   # 设置像素格式为RGB565
sensor.set_framesize(sensor.QQVGA)     # 设置帧大小为320x240
sensor.set_brightness(-3)
sensor.set_contrast(-3)
sensor.set_saturation(-3)
sensor.set_windowing(roi)
sensor.skip_frames(time = 2000)       # 跳过前2秒帧,用于摄像头设置稳定
sensor.set_auto_gain(False)           # 必须关闭才能进行颜色跟踪
sensor.set_auto_whitebal(False)       # 必须关闭才能进行颜色跟踪
clock = time.clock()                  # 初始化时钟对象

def send_data(x):
    global uart;
    uart.write(str(x))
    uart.write(bytearray([0x20]))


# 设置颜色阈值,识别黄色
yellow_threshold = (80, 100, -35, 29, 32, 100)
white_threshole = (90,100,-2,2,-2,2)

while(True):
    clock.tick()                      # 开始新帧计时
    sensor.set_hmirror(True)
    img = sensor.snapshot().lens_corr(1.9).mean(1)           # 捕获图像
    blobs = img.find_blobs([yellow_threshold])
    if blobs:
        largest_blob = max(blobs, key=lambda b: b.pixels())
        # 绘制找到的物体边界框
        img.draw_rectangle(largest_blob.rect(), color=(255, 0, 0))
        img.draw_cross(largest_blob.cx(), largest_blob.cy(), color=(255, 0, 0))

        send_data(largest_blob.cx())

六、Keil填写代码

1、电机控制

此文件包含两个函数:

PID_Control:控制乒乓球运动到指定位置

PID_Sin:控制乒乓球以杆中心做正弦运动

有关无刷电机驱动的内容,详见[STM32+HAL]DengFOC移植之闭环速度控制

#include "Control.h"
#include "tim.h"
#include "FOC2.h"

#define KP 	0.21			// 比例系数
#define KI 	0.000022		// 积分系数
#define KD  13       		// 微分系数

double Current_position;			//小球当前位置
double Target_position;			//小球目标位置


/* 定点PID控制函数 */
float PID_Control(float Current,float Target)
{
	double Error, Integral, Derivative, LastError;
	/*PID算法*/
	Error = Target - Current;								// 当前误差
	Integral += Error;
	Derivative = Error - LastError;							// E[k]-E[k-1]项

	/*更新输出值*/
	float temp = KP * Error + (KI) * Integral + KD * Derivative;
	temp = (temp > 20 ) ? 20 : (temp < -20) ? -20 :temp;	//限幅
	
	LastError = Error;										// 存储误差,用于下次计算
	return temp;
}


/* Sin运动PID控制函数 */
float PID_Sin(float Current,float Target)
{
	double Error1, Integral1, Derivative1, LastError1;	
	/*Sin波的PID算法*/
	float P = 0.5;
	float D = 3;
	Error1 = Target - Current;								// 当前误差
	Derivative1 = Error1 - LastError1;			

	/*更新输出值*/
	float temp = P * Error1 + D * Derivative1;
	temp = (temp > 20 ) ? 20 : (temp < -20) ? -20 :temp;	//限幅

	LastError1 = Error1;
	return temp;
}

2、LCD显示
/* 界面一: 显示小球目标与当前位置曲线 */
void Show_1(uint8_t flag)
{
	LCD_ShowString(20,160,100,16,16,"Distance:");
	LCD_ShowString(160,160,100,16,16,"target_cx:");
	LCD_ShowString(20,210,100,16,16,"Overshoot:");
	LCD_ShowString(160,210,100,16,16,"Time:");

	if(fabs(target_cx - cx) < 2 && flag_Overshoot == 0)		//第一次经过目标值时,重置超调量,改变标志位
	{
		flag_Overshoot = 1;
		Max_Overshoot = 0;
		Overshoot = 0;
	}

	if(flag_Overshoot == 1)									//判断已经过了一次目标值后的最大偏移
	{
		Overshoot = fabs(target_cx - cx);
		Max_Overshoot = (Max_Overshoot <= Overshoot) ? Overshoot :Max_Overshoot;	//取最大值
	}

	LCD_ShowNum(95,160,distance,3,16);
	LCD_ShowNum(240,160,distance_target,3,16);
	LCD_ShowNum(103,210,(Max_Overshoot - 8.057) / 3.4615,3,16);	//距离值与像素转换
	LCD_ShowNum(240,210,time,5,16);
	draw_distance_wave1(distance,distance_target);			//绘制小球位置曲线
}


/* 界面二:两种模式之间切换界面 */
void Show_2(uint8_t flag)
{
	LCD_ShowString(20,160,100,16,16,"Left point:");
	LCD_ShowNum(105,160,2,3,16);
	LCD_ShowString(175,160,100,16,16,"Right point:");
	LCD_ShowNum(270,160,30,3,16);
	LCD_ShowString(20,30,100,16,16,"Set Cycle:");
	LCD_ShowNum(115,30,3,2,16);
}


/* 界面三:Sin运动曲线 0 - 30cm */
void Show_3(uint8_t flag)
{
	LCD_ShowString(20,210,100,16,16,"Left point:");
	LCD_ShowString(160,210,100,16,16,"Right point:");
	LCD_ShowString(160,30,100,16,16,"Fact Cycle:");
	LCD_ShowNum(115,210,2,3,16);
	LCD_ShowNum(255,210,30,3,16);
	LCD_ShowNum(255,30,3,3,16);
	draw_distance_wave(distance);			//绘制小球位置曲线
}


/* 显示的总菜单 */
void Show(uint8_t flag)
{
	if(flag != last_flag) LCD_Clear(WHITE);
	switch (flag)
    {
		case 1:Show_1(flag),HAL_TIM_Base_Start_IT(&htim2);	//界面一,定点控制
    		break;
		case 2:Show_2(flag),HAL_TIM_Base_Stop_IT(&htim2);	//界面二,两种模式停顿切换界面
			break;
		case 3:Show_3(flag),HAL_TIM_Base_Start_IT(&htim2);	//界面三,Sin运动曲线控制
    		break;
		default:
    		break;
    }
	last_flag = flag;
}

3、mian.c

为缩减篇幅,下附代码删减了部分冗余的代码,相信以大家的聪明才智一定看得懂!

思路:初始化后,当按键按下切换模式后,显示对应的界面,并在定时器回调函数中进行一系列的控制与计时。

int main(void)
{
  /* USER CODE BEGIN 2 */
	/* OpenMV接收数据初始化 */
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart1,rx_buffer,RXBUFFERSIZE);

	/* FOC初始化 */
	DFOC_Vbus(VMax);
	DFOC_alignSensor(PP,DIR);
	HAL_Delay(200);

	/* LCD初始化 */
	Show_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	Show(flag);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)		    //1ms定时器
{	
	/* 电机定时控制 */
	if( htim -> Instance == TIM2){
		/* flag == 1时进入闭环位置控制 */
		if(flag == 1)
		{
			if(flag_time_loop == 0) time_loop++;					//若不为目标位置,时间周期++
			time = 1.5 * time_loop;									//时间转换
			target_cx = distance_target * 3.4615 + 8.057;
			DFOC_M0_set_Force_Angle(PID_Control(cx,target_cx));		//设置位置
		}
		/* 当flag == 3时进入曲线控制 */
		else if(flag == 3)
		{
			DFOC_M0_set_Force_Angle(PID_Sin(cx,60));				//设置位置
		}
		else
		{
			time_loop = 0;
			flag_time_loop = 0;
		}
	}
}


/* 按键消抖 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET){
		HAL_Delay(20); //延时消抖
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET){
			/* flag切换模式 */
			flag = (flag + 1) % 4;
		}
	}
	else if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET){
		HAL_Delay(20); //延时消抖
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET){
			/* distance = 30 切换距离 */
			distance_target = (distance_target + 1) % 31;
		}
	}
}
/* USER CODE END 4 */

七、源码提供

夸克:我用夸克网盘分享了「Ball_Control」,点击链接即可保存。

百度:通过百度网盘分享的文件 提取码:6666

Gitee:Ball_Control

CSDN:Ball_Control

八、成果欣赏

Ball_Control

九、结语

本人能力有限,代码未必是最优解,若有可改进之处望在评论区留言。

如有小伙伴想交流学习心得,欢迎加入群聊751950234,群内不定期更新代码,以及提供本人博客所有源码

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

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

相关文章

Proxmox 8.1.3 系统安装堡塔云 waf 防火墙

堡塔云 waf 工作原理&#xff1a; 堡塔云 waf 是一款Web应用防火墙&#xff0c;区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击。 堡塔云 waf 采用半容器化部署&#xff0…

消息中间件相关笔记整理

目录 1. 简述消息队列 2. 常见的的消息队列中间件 2.1 Kafka概念及结构 基本概念名词解释 为什么要设置多个副本&#xff1f; 2.2 Kafka的工作流程 消息生产流程 消息存储流程 消息消费流程 2.3 面试必问题 如何保证消息不丢失&#xff1f; 消费者如何保证消息不重…

JVM垃圾回收算法有哪些

JVM垃圾回收算法有哪些 标记清除算法(mark and sweep) 将垃圾回收分为两个阶段:标记和清除 根据可达性分析算法得出的垃圾进行标记 对标记的内容进行垃圾回收 优点: 标记和清除速度较快 缺点: 碎片化较为严重,内存不连贯 标记整理算法 记录存活的对象,清除需要回收的对…

oracle创建账户

1、查看表空间 SELECT tablespace_name FROM user_tablespaces;2、创建用户 CREATE USER FLINKCDC2 IDENTIFIED BY "123456";也可以使用指定表空间的方式 CREATE USER FLINKCDC2 IDENTIFIED BY "123456" DEFAULT TABLESPACE LOGMINER_TBS QUOTA UNLIMIT…

企业为什么要管理好员工的工作微信?

为什么要管理员工工作微信&#xff0c;可以从以下几个方面进行优化和丰富&#xff1a; 一、优化人力资源配置&#xff1a; 提高工作效率&#xff1a;通过监控和管理员工的工作微信&#xff0c;企业可以确保每位员工的时间被合理分配&#xff0c;专注于与工作相关的沟通和任务…

【区块链+食品安全】基于 FISCO BCOS 联盟链的供应链溯源管理系统 | FISCO BCOS应用案例

冷冻食品企业通常会面临以下痛点&#xff1a; 1. 食品安全问题&#xff1a;无法迅速确定受污染或有质量问题的产品来源&#xff0c;导致召回时效延迟&#xff0c;增加企业的风险和损失。 2. 信息不透明&#xff1a;传统的供应链系统存在记账信息孤岛&#xff0c;数据无法溯源…

opencascade Bnd_Range源码学习区间表示

opencascade Bnd_Range 前言 这个类描述了由两个实数值限定的 1D 空间中的区间。 一个区间可以是无效的&#xff0c;这表示区间中不包含任何点。 方法 1 //! 默认构造函数。创建一个无效区间。 Bnd_Range() : myFirst(0.0), myLast(-1.0) {} 2 //! 构造函数。不会创建无效…

Java后端面试题(redis相关1)(day7)

目录 为什么要用Redis&#xff1f;Redis到底是多线程还是单线程&#xff1f;Redis数据持久化机制RDB方式AOF方式 Redis是单线程&#xff0c;但为什么快&#xff1f;Redis 过期删除策略Redis 内存淘汰策略 为什么要用Redis&#xff1f; 基于内存操作&#xff0c;内存读写速度快k…

XSS漏洞分析

原理&#xff1a;利用网页开发时web应用程序对用户输入过滤不足导致将恶意代码注入到网页中&#xff0c;使用户浏览器加载并执行恶意代码&#xff0c;通常是JavaScript类型&#xff0c;也包括java、vbs、flash、html等。 解码的顺序是HTML&#xff0c;URL和JavaScript。 常用…

【运维】macos使用微软官方远程桌面工具mstsc控制windows10教程(含失败的解决方法)

【环境】macos使用微软官方远程桌面工具mstsc控制windows10教程&#xff08;含失败的解决方法&#xff09; 文章目录 1、如何获取本地账号密码并连接2、失败&#xff1a;检查被控权限是否有打开3、失败&#xff1a;登录过微软账号的情况 1、如何获取本地账号密码并连接 打开cm…

mysql 物理备份 MySQL 全量备份 增量备份 差异备份 日志备份万字长文 1.3万字

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循版权协议&#xff0c;转载请附上原文出处链接和本声明 注意&#xff0c;通常 完备增备&#xff0c;日志&#xff08;binlog)备&#xff0c;结合使用 差异则根据具体情况选用。 此备份过程 属于公司 常用的单个数据…

OpenVLA: An Open-Source Vision-Language-Action Model

发表时间&#xff1a;13 Jun 2024 作者单位&#xff1a;Stanford University Motivation: the potential to change how we teach robots new skills。然而&#xff0c;VLA 对机器人技术的广泛采用具有挑战性&#xff0c;因为 1&#xff09;现有的 VLA 在很大程度上是封闭的并…

Linux: 进程概念详解

目录 1. 冯诺依曼体系结构 2. 操作系统(Operator System) 1. 概念 2. 设计OS的目的 3. 任务 &#xff08;定位&#xff09; 4. 理解管理 3. 进程 1. 基本概念 2. 描述进程-PCB 3. 进程状态 4. 创建进程 fork 5. (Zombie)-僵尸进程 6. 孤儿进程 7. 进程优先…

[卷积神经网络]YOLOv10论文解读

原文地址&#xff1a; YOLOv10: Real-Time End-to-End Object Detectionhttps://arxiv.org/pdf/2405.14458 项目地址&#xff1a; yolov10https://github.com/THU-MIG/yolov10 一、概述 YOLOv10的改进点主要由两点&#xff1a;①提出一种基于无NMS的一致的双重训练策略&…

构建高效外贸电商系统的技术探索与源码开发

在当今全球化的经济浪潮中&#xff0c;外贸电商作为连接国内外市场的桥梁&#xff0c;其重要性日益凸显。一个高效、稳定、功能全面的外贸电商系统&#xff0c;不仅能够助力企业突破地域限制&#xff0c;拓宽销售渠道&#xff0c;还能提升客户体验&#xff0c;增强品牌竞争力。…

Web网站的授权oAuth2.0 单点登录

1.Web网站的授权(oAuth2.0) Client 第三方应用&#xff08;需要做鉴权的业务网站、业务系统&#xff09;UserAgent 用户代理&#xff08;浏览器&#xff09;Resource Owner 用户授权&#xff08;用户手动点击【同意】按钮&#xff0c;授权使用第三方登录渠道&#xff09;&#…

CPU飙升 怎么定位问题

传统的方法 【top】 查看所有进程占系统CPU的排序&#xff0c;定位是哪个进程搞的鬼。PID那一列就是进程号。 【top -Hp pid】 定位进程中使用 CPU 最高的线程tid 【printf ‘0x%x’ tid】 线程 tid 转化 16 进制,例如printf ‘0x%x’ 11882 得到16进制的 0x2e6a 【jstack…

Spring——IOC/DI思想

1、IOC&#xff08;Inversion of Control&#xff09;控制反转 何为控制反转&#xff1f; 在业务层中我们如果要调用数据层的方法那么必然牵扯到对象的创建&#xff0c;如果我想要改变上述数据层的方法中的内容&#xff0c;那么我就要改变业务层的代码&#xff0c;重新创建对…

R语言的矩阵运算

下面内容摘录自《R 语言与数据科学的终极指南》专栏文章的部分内容&#xff0c;每篇文章都在 5000 字以上&#xff0c;质量平均分高达 94 分&#xff0c;看全文请点击下面链接&#xff1a; 3章4节&#xff1a;R的逻辑运算和矩阵运算-CSDN博客文章浏览阅读165次。在 R 语言的丰…

PHP概述、环境搭建与基本语法讲解

目录 【学习目标、重难点知识】 什么是网站&#xff1f; 1. PHP 介绍 1.1. PHP 概述 1.1.1. PHP 是什么&#xff1f; 1.1.2. PHP 都能做什么&#xff1f; 1.2. PHP 环境搭建 1.2.1. PhpStudy 2. PHP 基本语法 2.1. PHP 语法入门 2.1.1. 第一个 PHP 程序 2.1.2. PHP …