基于stm32的下推式磁悬浮制作 附源程序与仿真原理图,很早就想做磁悬浮只是觉得有点难就一直没有做,自学51单片机,又学了stm32后也一直没有做过单片机项目,直到做设计的时候才想起用单片机做个磁悬浮玩玩。选用stm32也只是手头上刚好有一块,通过对网上的资料学习后以为对磁悬浮有点了解,做起来应该不是很难,没想到上手操作的时候还是有很多的坑。
其大概原理就是用四个线圈对浮子在前后左右的位置进行调整,浮子的位置用线性霍尔元件进行检测,转换为电压后由单片机进行ADC采集
单片机将采集的位置数据分别进行x,y轴的pid算法处理,输出pwm控制电磁线圈的磁力从而控制浮子的位置。
本项目的重点就是霍尔传感器的信号放大以及ad采集,pid算法,pwm输出还有就是磁铁的放置位置。
线性霍尔元件用的a3144,输出只有几百mv需要用运放将其放大,运放接5v话其最大输出有4v左右,需要用两个电阻将其限制在3.3v以下保护单片机,也可以用两个二极管组成钳位电路进行限制。(一定要用线性霍尔元件,并且要接运放将霍尔元件的输出放大,对运放的输出限制以免烧坏stm32单片机。
将霍尔元件,运放,以及钳位电阻连接后,需要调节电位器使输出电压稳定在1.7v附近
pid算法网上有很多这里就不解释了,贴几个连接https://blog.csdn.net/qq_25352981/article/details/81007075 https://blog.csdn.net/sdkdlwk/article/details/107759435
以下运用的是位置pid算法,pid参数调整时可以x,y轴一起调整,先调整kp也就是比例系数,再调整kd微分系数。
- #define Max_Cycle 290//限制幅度
- #define Min_Cycle -290
- typedef struct//结构体
- {
- int targetValue;//设定值
- int Error;//误差
- int prevError;//上次误差
- int Integral;//误差积分
- float Kp;//比例系数
- float Ki;//没用
- float Kd;//微分系数
- }PID;
- int Calc_PID(PID *pid,int Pos)//位置PID
- {
- int output;
- int Differential;//微分变量
- pid->Error = (pid->targetValue – Pos); // 误差有正负
- pid->Integral += pid->Error; // 误差累计
- Differential = pid->Error – pid->prevError; // 微分部分本次误差减去上次误差
- output = (pid->Kp * pid->Error + pid->Integral * pid->Ki + pid->Kd * Differential);//通过比例 积分 微分参数算出输出控制量 可省略积分部分
- pid->prevError = pid->Error; // 本次误差赋值为上次
- if(output>Max_Cycle) // 超过上限则输出恒定值
- output = Max_Cycle;
- if(output<Min_Cycle)
- output = Min_Cycle;
- return output;
- }
- void Init_PID_Parameter(void)
- {
- xPID.targetValue = 2300; //
- yPID.targetValue = 2300;
- xPID.Error = 0;
- yPID.Error = 0;
- xPID.prevError=0;
- yPID.prevError=0;
- xPID.Ki = 0.0f;
- yPID.Ki = 0.0f;
- xPID.Kp = 0.3f;
- yPID.Kp = 0.3f;
- xPID.Kd = 0.60f;
- yPID.Kd = 0.60f;
- }
复制代码
pwm主要就是控制电磁线圈的的磁力大小,从而控制浮子位置。将pid算法的输出作为pwm的输入,控制4路pwm的通断
- void TIM3_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- /* 设置TIM3CLK 为 72MHZ */
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
- /* GPIOA and GPIOB clock enable */
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE );
- /*GPIOA Configuration: TIM3 channel 1 and 2 as alternate function push-pull */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- /*GPIOB Configuration: TIM3 channel 3 and 4 as alternate function push-pull */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- }
- void TIM3_Mode_Config(void)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- /* PWM信号电平跳变值 */
- /* u16 CCR1_Val ;
- u16 CCR2_Val ;
- CCR1_Val=CCR1_value;
- CCR2_Val=CCR2_value;
- */
- //周期为12K 72M/ARR+1/PSC+1
- /* Time base configuration */
- TIM_TimeBaseStructure.TIM_Period =299; //当定时器从0计数到299,即为300次,为一个定时周期
- TIM_TimeBaseStructure.TIM_Prescaler = 11; //设置预分频:不预分频,即为72MHz
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; //设置时钟分频系数:不分频(这里用不到)
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
- /* PWM1 Mode configuration: Channel1 */
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //当定时器计数值小于CCR1_Val时为高电平
- TIM_OCInitStructure.TIM_Pulse = 10; //设置跳变值,当计数器计数到这个值时,电平发生跳变 可不设置
- TIM_OC1Init(TIM3, &TIM_OCInitStructure); //使能通道1
- TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
- /* PWM1 Mode configuration: Channel2 */
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 10; //设置通道2的电平跳变值,输出另外一个占空比的PWM
- TIM_OC2Init(TIM3, &TIM_OCInitStructure); //使能通道2
- TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
- /*TIM3的通道3*/
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 10; //设置通道2的电平跳变值,输出另外一个占空比的PWM
- TIM_OC3Init(TIM3, &TIM_OCInitStructure); //使能通道2
- TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
- /*TIM 通道 4*/
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 10; //设置通道2的电平跳变值,输出另外一个占空比的PWM
- TIM_OC4Init(TIM3, &TIM_OCInitStructure); //使能通道2
- TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
- /* TIM3 enable counter */
- TIM_Cmd(TIM3, ENABLE); //使能定时器3
- }
- /**
- * 初始化
- */
- void TIM3_PWM_Init(void)
- {
- TIM3_GPIO_Config();
- TIM3_Mode_Config();
- }</div><div>//pwm输出处理函数</div><div>
- void TIM3_OutSequence1zhuoyou(int Shuzhi){
- if(Shuzhi>0){
- TIM_SetCompare1(TIM3,0);
- TIM_SetCompare2(TIM3,Shuzhi);
- }
- else {
- TIM_SetCompare1(TIM3,Shuzhi*(-1));
- TIM_SetCompare2(TIM3,0);
- }
- }
- void TIM3_OutSequence1shangxia(int Shuzhi){
- if(Shuzhi>0){
- TIM_SetCompare3(TIM3,0);
- TIM_SetCompare4(TIM3,Shuzhi);
- }
- else {
- TIM_SetCompare3(TIM3,Shuzhi*(-1));
- TIM_SetCompare4(TIM3,0);
- }
- }
复制代码
l298n接受单片机的pwm输出,再输出电压到电磁线圈。
当所有电路连接好后,将强力磁铁放在四个线圈的中间,再左右前后移动如感觉有斥力将浮子限制在中间则线路连接正确,否则要调整电路连接。
大磁铁(为浮子提供主要支持力)的位置放置很重要,将浮子放在大磁铁中间上方要感觉是有排斥力才行。大磁铁最好放在板子也就是电磁线圈的下方,否者电磁线圈的控制力不足以控制浮子的姿态。或者加大输入电磁线圈的电压也行。
正面
背面
最终效果
材料
stm32c8t6
lm358n运放
a3144线性霍尔元件
l298n
2个电位器10k
两个100k电阻
四个4.7k电阻
两个15k电阻
四个19*12电磁线圈
100*60*15环形磁铁
20*3铷磁铁
4个m3的螺丝
Proteus仿真与程序资料51hei下载地址:
磁悬浮stm32.7z (231.26 KB, 下载次数: 0)