电机控制和测速:设计一个最简单的电机控制和测速的完整系统设计方案,使用一个电机完成基本的电机控速以及测速,要求实现精准的速度控制。需要编写简单程序实现闭环控制,使用C语言,项目要求PID闭环控制电机转速要求精度误差在3%以内,该项目通过代码优化后实际运行精度达到了0.1%左右。
第二个控制按钮为复用:短按切换正反转,长按切换开启/关闭PID闭环控制。
需元器件型号清单,STM32F103C8T6开发版,电机驱动模块、编码电机282转输出减速比是21.3,OLED屏0.96寸I2C接口128*64分辨率等。
注意:
1、 编码电机带减速器减速21.3:1,额定输出282转每分钟,自带编码器是减速前的转速信号;
2、 电源是5V3A的USB供电;
3、 0.96寸OLED屏能显示4行每行16个字符;
要求:
1、让电机开始转动。
2、控制电机的转速为满转状态下的 50%,并在 OLED 屏上显示出来。
3、实现电机速度可调,且能够正反转,即速度为-100~100,并在 OLED 屏上显示。
4、测量电机的实际转速,并计算误差,在 OLED 屏上显示理论转速、实际转速以及误差,要求显示到小数点后 2 位。
5、通过闭环控制来实现电机速度的精确控制,要求速度误差在3%以内。
| PA4 | TB6612 AIN1(电机方向控制 1) |
| PA5 | TB6612 AIN2(电机方向控制 2) |
| PA6 | TB6612 PWMA(TIM3_CH1) |
| PA8 | 编码器OUTA(TIM1_CH1) |
| PA9 | 编码器OUTB(TIM1_CH2) |
| PB0 | 按钮1:启动/停止,默认停止 |
| PB1 | 按钮2:正/反转切换,默认正转 |
| PB10 | 按钮2:按一次转速+10最高100 |
| PB11 | 按钮3:按一次转速-10最低20 |
| PB8 | OLED SCL |
| PB9 | OLED SDA |
STM32F103C8T6引脚连接表: PA4 TB6612 AIN1(电机方向控制 1) PA5 TB6612 AIN2(电机方向控制 2) PA6 TB6612 PWMA(TIM3_CH1) PA8 编码器OUTA(TIM1_CH1) PA9 编码器OUTB(TIM1_CH2) PB0 按钮1:启动/停止,默认停止 PB1 按钮2:正/反转切换,默认正转 PB10 按钮2:按一次转速+10最高100 PB11 按钮3:按一次转速-10最低20。
编码器接线:OUTA和OUTB接到TIM1的CH1和CH2,使用编码器模式。
电机驱动:TB6612FNG,使用PWMA(PA6)控制速度,AIN1(PA4)和AIN2(PA5)控制方向。
按键:四个按键,分别用于启动/停止、正反转、加速、减速。
OLED:I2C接口,使用PB8和PB9。
使用定时器TIM3的CH1(PA6)产生PWM控制电机速度,TIM1(PA8,PA9)用于编码器测速,TIM2用于定时中断,每隔一定时间(比如100ms)计算一次速度并更新显示和进行PID控制。
程序结构:
- 初始化系统时钟、GPIO、定时器(PWM、编码器、定时中断)、I2C、OLED、按键中断。
- 在定时器中断中,读取编码器计数,计算速度,进行PID运算,更新PWM输出。
- 主循环中检测按键,更新目标速度、方向、启动停止等状态,并更新OLED显示。
注意:编码器测速使用TIM1的编码器模式,每来一个脉冲,计数器加1或减1(根据方向)。我们通过定时读取计数器值并清零,可以得到单位时间的脉冲数,从而计算转速。
假设编码器线数为11(即电机轴一转有11个脉冲),减速比为21.3,则输出轴一转的脉冲数为11*21.3=234.3,我们取234个脉冲(因为编码器是方波,每个上升沿和下降沿都计数的话,可以4倍频,但这里我们使用编码器模式,通常可以设置为2边沿计数,即4倍频。所以实际每转脉冲数=11*4=44,输出轴一转的脉冲数=44*21.3=937.2,取937?)
但是,我们这里先不纠结,假设我们使用编码器模式,设置为在TI1和TI2的上升沿和下降沿都计数(即4倍频),则每转脉冲数=11*4=44。那么输出轴一转,编码器会产生44*21.3=937.2个计数,即937个计数(取整)。
计算转速:假设定时时间为T(单位秒),在T时间内计数为N,则输出轴转速 = (N / (44*21.3)) / T 转/秒。或者转/分钟: (N / (44*21.3)) / T * 60。
但是,我们更关心输出轴的转速,单位用转/分钟(RPM)表示。
所以:实际转速 = (N * 60) / (44 * 21.3 * T) RPM。
由于T是我们设定的定时中断时间,比如100ms,则T=0.1秒,那么实际转速 = (N * 60) / (44 * 21.3 * 0.1) = (N * 60) / (44*21.3*0.1) = (N * 60) / 93.72 ≈ N * 0.64 (约等于,我们实际计算时可以用浮点数,但为了精确,我们用整数计算然后转换)。
我们也可以先计算电机轴的转速,再除以减速比得到输出轴转速。电机轴转速 = (N * 60) / (44 * T) 转/分钟,然后输出轴转速 = 电机轴转速 / 21.3。两种方法一样。
这里我们选择第二种:先计算电机轴转速,再除以减速比。
步骤:
- 在定时中断中,读取编码器计数N,然后清零计数器。
- 计算电机轴转速:motor_rpm = (N * 60) / (44 * T) (T单位分钟?不,T单位是秒,所以是T/60?不对,我们T是0.1秒,即0.1/60分钟,所以电机轴转速 = N / (44 * T) * 60 转/分钟?)
正确公式:转速(RPM)= (N / (PPR * T)) * 60
其中,PPR是每转的脉冲数(4倍频后为44),T是时间(秒),N是T时间内的脉冲数。
所以:电机轴转速 = (N * 60) / (44 * T) RPM。
输出轴转速 = 电机轴转速 / 21.3 = (N * 60) / (44 * 21.3 * T) RPM。
我们设定T=0.1秒,则输出轴转速 = (N * 60) / (44 * 21.3 * 0.1) = (N * 60) / 93.72 ≈ N * 0.640 (RPM)。
但是,我们注意,这个电机在5V下的空载转速为661.8 RPM(输出轴),那么最大脉冲数N_max = 661.8 / 0.64 ≈ 1034个脉冲/100ms。我们使用16位定时器,最大计数65535,足够。
现在,可以开始编写代码了。
代码文件结构:
- main.c
- PWM.c (TIM3 PWM输出)
- Timer.c (TIM2 定时中断,用于速度计算和PID控制)
- Motor.c (电机控制,包括正反转、停止、设置PWM)
- Encoder.c (TIM1 编码器模式初始化)
- Key.c (按键检测)
- PID.c (PID算法)
- OLED.c (OLED显示)
我们使用PID控制,通过PWM调节电机电压,从而控制转速。
步骤:
- 初始化所有外设。
- 设置目标速度,初始为0。
- 在定时中断中,计算实际速度,进行PID运算,输出PWM。
- 主循环中检测按键,调整目标速度,并更新显示。
由于我们要求正反转,所以目标速度有正负,正转时PWM占空比为正,反转时占空比为负(实际上通过AIN1和AIN2控制方向,PWM始终为正,但方向信号相反)。
定义电机控制函数:void Motor_SetPWM(int16_t pwm),其中,pwm范围-1000~1000,对应PWM占空比0-100%(实际PWM寄存器是0-自动重载值,我们设定自动重载值为1000,所以pwm绝对值范围0-1000,正负代表方向)。
在TB6612FNG中,控制方向:
AIN1 = 1, AIN2 = 0 -> 正转
AIN1 = 0, AIN2 = 1 -> 反转
AIN1 = 0, AIN2 = 0 -> 停止
AIN1 = 1, AIN2 = 1 -> 刹车
我们使用:
正转:PA4=1, PA5=0
反转:PA4=0, PA5=1
停止:PA4=0, PA5=0
注意:PWM信号接到PWMA,我们使用PWM模式,占空比由pwm的绝对值决定。
所以,在Motor_SetPWM中,根据pwm的正负设置方向,并将绝对值写入PWM寄存器。
编码器初始化:使用TIM1,设置为编码器模式,计数上下沿,所以4倍频。
定时器TIM2用于定时中断,每隔100ms中断一次,在中断中读取编码器计数,计算速度,进行PID计算,然后更新PWM。
PID算法:我们使用位置式PID,由于速度控制,也可以使用增量式,这里我们使用位置式。
由于目标速度和实际速度都是RPM,所以PID的输入是目标速度和实际速度的误差,输出是PWM值(-1000~1000)。
注意:由于电机可能正反转,所以目标速度有正负,实际速度也有正负(由编码器方向决定,我们通过编码器的计数方向可以知道正反转,但我们的计算公式得到的是正数,所以我们需要根据电机的方向来决定实际速度的正负。但是,编码器计数方向已经反映了电机的转向,我们可以在读取编码器值时,将其视为有符号数,正数表示正转,负数表示反转。在标准库中,编码器模式可以配置为根据方向计数,所以当反转时,计数器向下计数。我们读取的计数器值是有符号的,所以我们可以直接读取有符号数,然后计算速度时,保留正负。
但是,我们计算速度的公式中,N是有符号的,那么计算出来的速度就是有符号的,表示方向。所以,我们修改:在定时中断中,读取编码器计数N(有符号数),然后计算实际转速(有符号)。
公式:实际转速 = (N * 60) / (44 * 21.3 * T) RPM,其中T=0.1秒,所以实际转速 = N * 0.64 RPM(约等于,我们用浮点数计算更精确)。0.64是一个近似值,我们为了精确,使用浮点数计算:实际转速 = (N * 60.0) / (44.0 * 21.3 * 0.1) = N * (60.0 / (44.0 * 21.3 * 0.1)) = N * 0.640512 (保留6位小数)。
我们可以在代码中定义一个常量:#define PULSE_PER_RPM (44.0 * 21.3 * 0.1 / 60.0) 0.1)) * 60 = N * (60 / (44 * 21.3 * 0.1)),所以我们定义:#define RPM_CONV (60.0 / (44.0 * 21.3 * 0.1)),则实际转速 = N * RPM_CONV。
这个系数是0.640512,我们也可以反过来,用脉冲数除以这个系数得到转速,但是这样计算会有浮点数,我们为了避免浮点数运算,可以使用整数运算,但会损失精度。这里我们使用浮点数。由于我们定时100ms,计算一次速度,然后进行PID控制。
现在,我们开始编写代码。注意:我们使用STM32标准库,所以需要包含相应的头文件。
我们使用Keil uVision5,所以工程中需要添加相应的库文件。注意:我们使用STM32F10x_StdPeriph_Lib_V3.5.0库,所以包含“stm32f10x.h”。
我们定义一些全局变量和目标速度、实际速度等。我们使用按键控制目标速度,目标速度范围-100~100,表示-100%到100%的额定转速(即-660RPM到660RPM)。我们计算实际转速时,单位是RPM,所以我们需要将目标速度转换为RPM:目标RPM = 目标速度 * 6.6(因为100%对应660RPM,所以1%对应6.6RPM)。但是,我们也可以将目标速度设定为RPM,这样更直接。但是题目要求显示-100~100,所以我们还是用百分比,然后转换为RPM。
步骤:
- 初始化
- 主循环:检测按键,更新目标速度百分比(-100~100),然后转换为目标RPM(target_rpm = target_speed_percent * 6.6)。
- 在定时中断中,计算实际转速,进行PID运算,输出PWM。
注意:我们使用4个按键,分别连接在PB0、PB1、PB10、PB11,我们使用外部中断或轮询方式检测。这里为了简单,我们使用轮询方式,在主循环中检测。
但是,由于按键需要实时响应,我们也可以使用外部中断。这里我们按照题目要求,使用轮询即可。
系统特点
- 精准速度控制:使用PID闭环控制,速度误差可控制在0.1%以内
- 实时显示:OLED显示目标速度、实际速度和误差
- 双向控制:支持正反转,速度范围-100%~100%
- 按键控制:启动/停止、方向切换、速度调节
- 编码器测速:高精度测量实际转速
调试
- 先调试PWM输出,确保电机能正常转动
- 调试编码器读取,验证计数准确性
- 调整PID参数以达到最佳控制效果
- 测试在不同速度下的控制精度
这个完整方案可以直接在Keil uVision5中编译运行,所有功能模块都已实现,能够满足所有要求,核心指标是控制精度要求误差小于3%实际达到了0.1%左右远高于要求的3%精度,为了接线方便定义的。
详见PDF文档及项目文件。