电赛练习之旋转倒立摆

电赛练习之旋转倒立摆2019年电赛已经结束,虽然结果不能令人满意,但闲下来,还是总结一下电赛学到的东西与失败的地方。这一次先来谈一下一阶旋转倒立摆。一、题目分析:拿到一道题目,其实最应该做的事情是分析题目,因为我们往往可以发现某些发挥题是在基础题的基础上进行的,但是,可能某些发挥题需要在基础题的基础上修改结构,我们也可以发现,题目中的某些问题具有相似性,当我们合并同类项的时候,可以把题目的要求变得简单。一下,我粘…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

2019年电赛已经结束,虽然结果不能令人满意,但闲下来,还是总结一下电赛学到的东西与失败的地方。这一次先来谈一下一阶旋转倒立摆。

一、题目分析:

拿到一道题目,其实最应该做的事情是分析题目,因为我们往往可以发现某些发挥题是在基础题的基础上进行的,但是,可能某些发挥题需要在基础题的基础上修改结构,我们也可以发现,题目中的某些问题具有相似性,当我们合并同类项的时候,可以把题目的要求变得简单。一下,我粘贴过来2013年一阶旋转倒立摆的题目以及要求:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
我们很容易知道本题的核心是使得摆杆保持稳定,之后再其基础上进行一些功能的延伸。为了更好地理解题意,我做了如下的分析:

一阶旋转到倒立摆

手动起摆

自动起摆

摆杆的运动

电动机的控制

可以看到,题目中所要求完成的就是通过对一个电机的控制来实现的,控制题的核心还是要落在控制上。历届电子设计大赛经常被人戏称为电子机械设计大赛,虽然控制题的核心是落在控制上,但系统的机械结构也至关重要。此次练习采用的是平衡小车之家的套件,所以省去了搭硬件的很多麻烦。单单就完成题目要求的功能来说,数值分析并不是很必要(强迫症或者冲击国奖者除外)。因此,本篇内容仅仅从代码的角度来进行分析。
俗话说的好,不写注释的程序猿是流氓。我个人很喜欢在主函数的前面写上所用引脚的定义,这样一来方便接线,二来可以避免引脚重复使用,造成不必要的麻烦。

/************************************************ 圆周倒立摆实验 ADC:PA1 Motor:PC0 PC1 Pwm:PB4 PB5 编码器A相:PB6 B相:PB7 按键:PA0 2 3 4 @JackFu ************************************************/

先来谈一谈我程序的风格,
一、
在主函数里我将初始化的函数写在了Board_Init();这个函数里,这样看起来比较方便后期的更改与检查,减少了主函数的代码量,使得主函数看起来简洁、调理。

void Board_Init(void)
{ 
   
	delay_init();
	Adc_Init();
	KEY_Init();
	Ecoder_TIM4_Init(); 
	Motor_Init();
	Timer_pwm_Init(999,8);
// Timer_Init_TIM5(4999,71);
// TIM_SetCompare2(TIM3,800);
	uart_init(115200);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}

二、我一般习惯于建立两个文件夹:HARDWARE、CONTROL。其中前者用来存放于硬件相关的一些配置函数,后者用来放与软件相关的一些配置函数。这样方便后期的维护与移植。

对于其中用到的基本配置,如ADC,ENCODER,MOTOR等等,此处不再赘述。网上有很多的例程与讲解。
该题的核心控制为电机,关键函数如下:

//电机调节函数 (控制电机倒立,很重要)
void Motor_Config(short pwm)
{ 
   
	int speed;
	if(start == 1)                         //开启倒立,调节倒立摆平衡
	{ 
   
		if(pwm<0 && pwm > -PWM_MAX)
		{ 
   
			speed = PWM_MAX + pwm;
			if(speed < 0)
			{ 
   
				speed = 0;
			}
			TIM_SetCompare2(TIM3,speed);
			Motor_reverse();
		}
		else if(pwm > 0 && pwm < PWM_MAX)
		{ 
   
			speed = PWM_MAX - pwm;
			if(speed > 400)
			{ 
   
				speed = 400;
			}
			TIM_SetCompare2(TIM3,speed);
			Motor_Forward();
		}
	}
	else                                   //电位器偏角过大,无法调节,为了安全,关闭电机
	{ 
   
		Motor_stop();
	}
}

传入的参数为电机ENCODER,电位器ADC采集值后经过PID处理的数据,具体的处理函数如下:

short PID_Pos_PosCalc(short NextPoint)
{ 
   
  register float  iError,dError;
	iError = pos_Set_Pos - NextPoint;        				// 偏差
	pos_SumError += iError*0.2;				    				// 积分
	if(pos_SumError > 1000.0)								//积分限幅1000
		pos_SumError = 1000.0;
	if(pos_SumError < -1000.0)
		pos_SumError = -1000.0;	
	dError = iError - pos_LastError; 						// 当前微分
	pos_LastError = iError;
	
	return(short)(pos_p * iError*0.8 + pos_i * pos_SumError + pos_d * dError);	//返回计算值
}


short PID_Ang_PosCalc(short NextPoint)
{ 
   
  register float  iError,dError;
	iError = ang_Set_Pos - NextPoint;        				// 偏差
	ang_SumError += iError;				    				// 积分
	if(ang_SumError > 1000.0)								//积分限幅1000
		ang_SumError = 1000.0;
	if(ang_SumError < -1000.0)
		ang_SumError = -1000.0;	
	dError = iError - ang_LastError; 						// 当前微分
	ang_LastError = iError;
	
	return(short)(ang_p * iError + ang_i * ang_SumError + ang_d * dError);	//返回计算值
}
void Task3(void)
{ 
   
	short temp,temp1,temp2;
	temp = 0;
	temp1 = ANG_MIN_VAL;										//角度最大值
	temp2 = ANG_MAX_VAL;										//角度最小值
	
	ang_Cur_Pos = Get_Adc_Average(ADC_Channel_1,15);			//获取当前角度
	
	
	ang_Set_Pos = ANG_MID_VAL;									//将平衡时位置角度应当采样的值赋给设定值
	
	if((ang_Cur_Pos > temp1)&&(ang_Cur_Pos<temp2))
	{ 
   
		temp += PID_Ang_PosCalc(ang_Cur_Pos);					//角度还在可以调整的范围内 
		start = 1;		
	}
	
	else
	{ 
   															//角度过大,调整不了,为了安全,关机
		start = 0;
	}
	
// if(++Position_Target>5)
		pos_Cur_Pos = PID_Pos_PosCalc(Encoder),Position_Target=0;
	temp -= pos_Cur_Pos;
// if(pos_Cur_Pos > 0 && flag == -1)
// { 
   
// ANG_MID_VAL = ANG_MID_VAL-5;
// flag = flag*(-1);
// }
// else if(pos_Cur_Pos < 0 && flag == 1)
// { 
   
// ANG_MID_VAL = ANG_MID_VAL+5;
// flag = flag*(-1);
// }

	Motor_Config(temp);
	printf("%d ",temp);
	printf("%d\r\n",Encoder);
}

该题的控制采用了双环的PID来进行处理,在进行学习的过程中发现,仅仅采用电位器环的PID可以使得摆杆倒立起来,但整个摆杆会整体顺时针或者逆时针的旋转,并不能满足题目要求。正如前面所述,在考虑机械结构时,要整体考虑,否则当做到这里时,发现需要用双环来进行控制,再改机械结构,会浪费很多的时间。

这里附上源码 (链接: Mycode(双环控制)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171008.html原文链接:https://javaforall.net

(0)
上一篇 2022年8月18日 上午7:36
下一篇 2022年8月18日 上午7:36


相关推荐

  • PAT备考经验&相关信息[通俗易懂]

    在9月8号下午的PAT考试中,我幸运的拿到了满分,用时1小时45分钟,排名第五,算是成功迈出了转专业的第一步。按照惯例应该嘚瑟一波,然而身边并没有人考这个,转念一想,不如把考试日志和备考经验教训记录下来,以期看见此文的后来者能少走一些弯路,更加高效的刷题学习(虽然可能并没有人能看到 _(:△」∠)_)。当然,在科班大佬面前我只是个尚未入门的弱鸡,因此这篇经验主要针对有意转行/业余爱好编程/基…

    2022年4月7日
    109
  • Sqlserver DateTime转换成SMALLDATETIME时“产生一个超出范围的值”「建议收藏」

    Sqlserver DateTime转换成SMALLDATETIME时“产生一个超出范围的值”「建议收藏」工作中遇到一个问题,A表中字段(DateTime1)的数据类型为DateTime,新建了一张表B的SMALLDATETIME1字段的数据来自A表的DateTime1但在将A表字段DateTime1导出到B表的SMALLDATETIME1字段时出现了以下错误后经过排查发现在原来是A表DateTime1字段的值有许多是"1753-01-0100:00:00.000",从而导致转换失败…

    2022年7月27日
    12
  • 220421-一些电平转换电路

    220421-一些电平转换电路摘要我在电路设计时 有时会遇到电压域不匹配的问题 如芯片为 1 8V 供电 而 MCU 为 3 3V 供电 这时候就需要进行电平转换才能通讯 总结几种常用的电平转换电路 文章目录前言一 二 三极管单向电平转换二 MOS 管双向电平转换电路集成芯片电平转换总结前言记录一些自己总结的知识 有部分内容也是参考借鉴网络资料 一 二 三极管单向电平转换我在做一些通讯模块 4G 模块 时发现串口是 1 8V 电平 与常用 MCU 的 3 3V 这时就需要电平转换 一般都会加个 MOS 做电平转换 因为 UART 的收发引脚是独立的

    2026年3月26日
    1
  • linux 查看PHP版本

    linux 查看PHP版本php v

    2026年3月16日
    2
  • docker查看redis版本[通俗易懂]

    docker查看redis版本[通俗易懂]dockerexec-itredisredis-server-v

    2022年6月2日
    60
  • 随机森林算法原理简要总结怎么写_旋转森林算法

    随机森林算法原理简要总结怎么写_旋转森林算法①RandomForest随机森林算法原理:即bagging法+CART算法生成决策树的结合。RF=bagging+fully-grownCARTdecisiontree②bagging法的核心:bootstrap在原始数据集D中选择若干个子数据集Dt,将子数据集单个单个进行决策树生成。③随机森林的优点:可并行化计算(子集的训练相互独立),效率高继承了CART算法的优点(使用Gini系数选择最优特征及切分点)减小了完全生成树的弊端(因为完全生成树过于复杂,Ein小但E

    2025年7月14日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号