打印

请大家帮我看看我的直流电机PID调速程序哪里有问题,谢谢

[复制链接]
1790|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
TT的爱|  楼主 | 2013-3-6 14:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 TT的爱 于 2013-3-6 14:58 编辑

无论怎么调,速度总是差那么一点。请各位高手帮我看一下。
我使用的芯片是PIC16F876A,编译环境是CCS。目标是通过PID实现直流有刷电机调速。
RA0接口为模拟电压输入,AD转换完的数据由单片机处理后,可以设定目标转速。光电编码器通过RC0接口输入计数脉冲,计算出实际转速,通过PID计算不断调整,最终接近目标转速。
定时器0每过10ms取计数器1的脉冲计数值,光电编码器分辨率为360。现在有两个情况比较奇怪,
1:我把P参数从1到10,I参数从0到5都试了一遍。当I参数过大时会出现实际速度变低的情况,当I参数为2时电机不能转动,其他情况下实际速度基本区别不大。
2:我将目标速度调高时(例如超过5000r/min),则实际速度比目标速度高出的部分会越来越大。而当目标速度调低时(如低于3000r/min),实际速度比目标速度低出的部分会越来越大。无论P,I如何设定,都是这样的情况。

程序如下。
#include  <16f876a.h>
#include  <lcd.h>
#fuses HS,PUT,NOPROTECT,NOWDT
#use delay(CLOCK = 20000000,RESTART_WDT)    //时钟频率20MHz

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)

unsigned long int overflow=0,get_count,new_count=0,get_speed=0,new_error=0,new_speed=0,old_count=0,old_speed=0,old_error=0;//所需变量
unsigned int ch0_channel,new_duty;
float new_pwm,set_speed;

#int_TIMER1
TIMER1_isr()      //定时器1中断函数
{
    overflow++;   
}

#int_TIMER0
TIMER0_isr()     //定时器0中断函数
{
    set_timer0(0x3C);                     //定时器0每过10ms发生一次中断
  
    new_count = get_timer1();          //读取计数器1的脉冲计数值
    get_count = new_count-old_count;                            //PID部分
    get_speed = get_count*50/3;
    new_error = set_speed-get_speed;
    new_speed = (new_error-old_error)*1+1*new_error+get_speed;
        

    new_pwm = (float)new_speed/31;    //感觉是这边出问题了
    new_duty = (int)new_pwm;
    set_pwm1_duty(new_duty);
   
   
    old_error = new_error;
    old_count = new_count;
    old_speed = new_speed;
    overflow = 0;
}

void main(void)
{
        set_tris_a(0b11111111);                            //0:输出、1:输入
        set_tris_b(0b11000000);                            //0:输出、1:输入
        set_tris_c(0b00000001);                            //0:输出、1:输入
   
        SETUP_ADC_PORTS(RA0_ANALOG);                      //RA0 为模拟输入
        SETUP_ADC(ADC_CLOCK_DIV_32);                       //Fosc/32  
        set_adc_channel(0);                                             //选择channel0

        enable_interrupts(GLOBAL);                         //允许全局中断
        enable_interrupts(INT_TIMER0);                   //允许定时器0中断
        enable_interrupts(INT_TIMER1);                   //允许定时器1中断

        setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);       //定时器0分频
        setup_timer_1(T1_EXTERNAL_SYNC|T1_DIV_BY_1);     //定时器1用作计数器

        set_timer0(0x3C);
               setup_timer_2(T2_DIV_BY_16,255,1);                 //定时器2设置
        setup_ccp1(ccp_pwm);                                       // 选择PWM模式

        read_adc(ADC_START_ONLY);                          //读取八位AD值
   
        lcd_initial();                                     //lcd初始化
        while(1)                                          
        {                                 
            ch0_channel = read_adc(ADC_READ_ONLY);               //读取AD转换值
            read_adc(ADC_START_ONLY);                            //AD转换开始
            set_speed = (float)31*ch0_channel;                   //经过测试测试,速度大概是channel0数值的31倍

            printf(lcd_data,"sv:%3.3f",set_speed);              //第一行显示目标速度
            lcd_cmd(0b11000000);                           
            printf(lcd_data,"pv:%lu",get_speed);                 //第2行显示实际速度
         
            delay_ms(200);                                 //lcd延迟200ms
            lcd_clear();                                       //lcd清除

     }   
}

相关帖子

沙发
airwill| | 2013-3-6 19:19 | 只看该作者
控制结果不收敛, 加大 P 参数.

使用特权

评论回复
板凳
TT的爱|  楼主 | 2013-3-6 20:40 | 只看该作者
airwill 发表于 2013-3-6 19:19
控制结果不收敛, 加大 P 参数.

谢谢啊,明天去加大P参数试试。刚刚又看了一下,发现数据类型搞错了,目标速度与实际速度之差、新的偏差与旧的偏差之差,我都是取得unsigned类型,计算中是得不到负数的。

使用特权

评论回复
地板
suse-lj| | 2013-4-18 18:04 | 只看该作者
偏差 是 有正负 之分的

使用特权

评论回复
5
leijie2001| | 2013-6-10 08:33 | 只看该作者
pid三个参数p, ti,td最好独立出来写,以方便调试。td一般可以去掉。给定速度,开始可以直接写一个常数,不使用电位器的值,等待速度环调试稳定后再使用。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

36

帖子

0

粉丝