搜索
bottom↓
回复: 85

匿名上位机调节PID成功,有视频,有源码

  [复制链接]

出0入10汤圆

发表于 2015-7-16 12:45:13 | 显示全部楼层 |阅读模式
花了两天,搞定了PID,匿名上位机帮了我的大忙!上位机可以直接修改PID的值,还可以观察角度、滤波之后的角度、PWM控制量等等的波形。
先上视频,现在PID控制得算是比较好了,从视频中可以看到我动了帆板一下,很快就恢复回原来的状态,开始调不好,出现了震荡,很久才恢复到初始角度。
http://v.youku.com/v_show/id_XMTI4NTk5NjQ0MA==.html
很多人可能更感兴趣匿名上位机,匿名上位机真的很强大!可以直接修改PID,显示波形。



匿名上位机跟单片机的通讯程序在帮助里面有

上位机跟通讯协议


PID程序如下。
  1. /*************************************************

  2. 名称:void pid(float angle, float angle_dot)
  3. 功能:PID运算
  4. 输入参数:
  5.    float angle     倾斜角度
  6.          float angle_dot 倾斜角速度
  7. 输出参数:无
  8. 返回值:  无
  9. **************************************************/
  10. //增量式PID
  11. int pid(PID_Type *PID,float angle, float angle_dot)
  12. {
  13.         static float err=0,err1=0,err2=0;//本次、上次、上上次误差
  14.         float add;//增量
  15.         
  16.         //如果PID1改变了,将err都置0
  17.         if(PID->SetPointChange==1)
  18.         {
  19.                 PID->SetPointChange=0;
  20.                 err=err1=err2=0;
  21.         }
  22.         
  23.         //计算误差
  24.         err=PID->SetPoint-angle;
  25.         //计算增量
  26.         add=PID->kP*(err-err1)+PID->kI*err+PID->kD*(err-2*err1+err2);
  27.         //加上增量
  28.         PID->CtrlValue+=add;
  29.         //限幅
  30.         if(PID->CtrlValue>MAX_PWM)PID->CtrlValue=MAX_PWM;
  31.         if(PID->CtrlValue<0)PID->CtrlValue=0;
  32.         
  33.         //保存误差
  34.         err2=err1;
  35.         err1=err;
  36.         
  37.         return PID->CtrlValue;
  38. }


  39. //位置式
  40. int pid2(PID_Type *PID,float angle, float angle_dot)
  41. {
  42.         //err0 本次的误差,err1 累计的误差,err2 这次跟前次误差的差值 err3 上次的误差
  43.         static float err0=0,err1=0,err2=0,err3=0;
  44.         
  45.         //如果PID1改变了,将err都置0
  46.         if(PID->SetPointChange==1)
  47.         {
  48.                 PID->SetPointChange=0;
  49.                 err0=err1=err2=0;
  50.         }
  51.         
  52.         //计算误差
  53.         err0=PID->SetPoint-angle;
  54.         //误差累加
  55.         err1+=err0;
  56.         //计算误差差值
  57.         err2=err0-err3;
  58.         //保存这次的误差
  59.         err3=err0;
  60.         //计算控制量
  61.         PID->CtrlValue=PID->kP*err0+PID->kI*err1+PID->kD*err2;

  62.         //限幅
  63.         if(PID->CtrlValue>MAX_PWM)PID->CtrlValue=MAX_PWM;
  64.         if(PID->CtrlValue<0)PID->CtrlValue=0;
  65.         
  66.         return PID->CtrlValue;
  67. }
复制代码



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入10汤圆

 楼主| 发表于 2015-7-16 17:45:18 | 显示全部楼层
lyg407 发表于 2015-7-16 17:09
楼主解释下 这个是什么意思,  那个是有刷电机 ?  吹风 然后检测板子 角度      板子上面一个加速度 陀螺 ...

话说我以为你们懂的,直流电机吹板子会不稳定的,也不能到达一个角度。
所以我就将角度反馈回来,用PID计算出PWM控制电机的转速。

出0入10汤圆

 楼主| 发表于 2015-7-18 11:02:54 | 显示全部楼层


串口速率够就行,不过估计占用51资源会比较多

出0入10汤圆

 楼主| 发表于 2015-7-18 11:32:12 | 显示全部楼层
顺便记录下调试PID心得:
在调整小的变化的时候,上面的PID是没有问题的,但是当出现较大的变化,并且比较久的时候,我发现整个系统会出现较长时间的恢复,这是由于积分项在捣乱,把偏差一直积分,后面P在减小PID输出的时候要纠正积分作用就很困难了,这个需要将程序修改如下!
  1. #define MAX_ERR  20
  2. //积分分离法的位置式PID
  3. //当误差比较大的时候去掉积分环节
  4. int pid3(PID_Type *PID,float angle, float angle_dot)
  5. {
  6.         //err0 本次的误差,err1 累计的误差,err2 这次跟前次误差的差值 err3 上次的误差
  7.         static float err0=0,err1=0,err2=0,err3=0;
  8.        
  9.         //如果PID1改变了,将err都置0
  10.         if(PID->SetPointChange==1)
  11.         {
  12.                 PID->SetPointChange=0;
  13.                 err0=err1=err2=0;
  14.         }
  15.        
  16.         //计算误差
  17.         err0=PID->SetPoint-angle;
  18.         //误差累加
  19.         err1+=err0;
  20.         //计算误差差值
  21.         err2=err0-err3;
  22.         //保存这次的误差
  23.         err3=err0;
  24.         if(fabs(err0)<MAX_ERR)
  25.         {
  26.                 //计算控制量
  27.                 PID->CtrlValue=PID->kP*err0+PID->kI*err1+PID->kD*err2;
  28.         }
  29.         else
  30.         {
  31.                 //
  32.                 PID->CtrlValue=PID->kP*err0+PID->kD*err2;
  33.         }

  34.         //限幅
  35.         if(PID->CtrlValue>MAX_PWM)PID->CtrlValue=MAX_PWM;
  36.         if(PID->CtrlValue<0)PID->CtrlValue=0;
  37.        
  38.         return PID->CtrlValue;
  39. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入10汤圆

 楼主| 发表于 2015-7-18 11:44:45 | 显示全部楼层
看到同学有另外一个比较好的算法,在平常的PID后面加上一个随角度偏移的角度量——这是在PID中的控制常量,一般PID没有!这样子PID工作起来就很方便,只需要在一个角度附近进行调整,但是获得一个随角度偏移的角度量这个是比较繁琐的!但是想到可以利用一开始调整好的PID(这个时候没有控制常量),然后得出比较合适的PWM!最后将这组常量做成一个数组,作为一个随目标角度变化的控制常量,然后再调整另外的一组PID!

出0入10汤圆

 楼主| 发表于 2015-7-18 21:31:02 | 显示全部楼层
爱新觉罗_极刚霸 发表于 2015-7-18 21:18
多谢回复~下午试了下,可以绘制出传感器的曲线。不知道可不可以在上位机上直接修改PID参数进行调 ...

上位机发送数据下来,然后你进行解析,提取出功能字,具体看协议,然后将数据段保存到对应PID变量,最后要返回一个cheaksum

出0入0汤圆

发表于 2015-7-16 15:07:43 | 显示全部楼层
点赞下!

出0入0汤圆

发表于 2015-7-16 15:24:54 | 显示全部楼层
不错呀,

出0入0汤圆

发表于 2015-7-16 15:37:20 | 显示全部楼层
我就看看

出0入0汤圆

发表于 2015-7-16 15:49:05 | 显示全部楼层
不错,上位机发给我一下。、

出0入0汤圆

发表于 2015-7-16 16:35:56 | 显示全部楼层
围观学习中

出0入0汤圆

发表于 2015-7-16 17:09:28 | 显示全部楼层
楼主解释下 这个是什么意思,  那个是有刷电机 ?  吹风 然后检测板子 角度      板子上面一个加速度 陀螺仪模块么?

出0入0汤圆

发表于 2015-7-16 17:25:41 来自手机 | 显示全部楼层
这个看着还不错

出0入0汤圆

发表于 2015-7-16 18:43:58 | 显示全部楼层
好资料,谢谢分享。一直没时间玩,有机会要试试试。

出0入0汤圆

发表于 2015-7-16 19:00:16 | 显示全部楼层
貌似是某年的竞赛题

出0入10汤圆

 楼主| 发表于 2015-7-16 19:22:25 | 显示全部楼层
huang518489 发表于 2015-7-16 19:00
貌似是某年的竞赛题

11年国赛题目

出0入0汤圆

发表于 2015-7-16 22:12:27 | 显示全部楼层
MARK一下!PID两种计算方法!

出0入0汤圆

发表于 2015-7-16 22:47:38 来自手机 | 显示全部楼层
高大上吗?

出0入0汤圆

发表于 2015-7-17 11:09:06 | 显示全部楼层
请教一下,如果是双极PID,就是可以调节电机正反转的,使输出的PID有负数值,楼上可以指点一下不?

出50入0汤圆

发表于 2015-7-17 11:12:06 | 显示全部楼层
谢谢分享,果断收藏

出0入10汤圆

 楼主| 发表于 2015-7-17 11:14:58 | 显示全部楼层
阿豪博士 发表于 2015-7-17 11:09
请教一下,如果是双极PID,就是可以调节电机正反转的,使输出的PID有负数值,楼上可以指点一下不? ...

把我程序的限幅去掉,然后判断如果是小于0就翻转,大于0就正转

出0入0汤圆

发表于 2015-7-17 11:19:08 | 显示全部楼层
好的,似曾相识,原来国赛题目

出0入0汤圆

发表于 2015-7-17 11:21:45 | 显示全部楼层
好的!
领悟了。
另外请教一个问题,楼主十分可以将这个工程开源一下?
让我学习一下整体的控制思路?
另外这个有必要设定死区吗?

出0入0汤圆

发表于 2015-7-17 11:23:49 | 显示全部楼层
好的!
领悟了。
另外请教一个问题,楼主十分可以将这个工程开源一下?
让我学习一下整体的控制思路?
另外这个有必要设定死区吗?

出0入10汤圆

 楼主| 发表于 2015-7-17 11:38:26 | 显示全部楼层
阿豪博士 发表于 2015-7-17 11:23
好的!
领悟了。
另外请教一个问题,楼主十分可以将这个工程开源一下?

你要哪部分程序我直接发上来,工程不可以。
死区就看你调的效果,我不知道

出0入0汤圆

发表于 2015-7-17 11:46:41 | 显示全部楼层
好的!
领悟了。
另外请教一个问题,楼主十分可以将这个工程开源一下?
让我学习一下整体的控制思路?
另外这个有必要设定死区吗?

出0入0汤圆

发表于 2015-7-18 10:25:15 | 显示全部楼层
不知道51可以实现不?

出0入0汤圆

发表于 2015-7-18 21:18:29 | 显示全部楼层
10xjzheng 发表于 2015-7-18 11:02
串口速率够就行,不过估计占用51资源会比较多

多谢回复~下午试了下,可以绘制出传感器的曲线。不知道可不可以在上位机上直接修改PID参数进行调节,上位机上的帮助信息有一部分没有看明白~

出0入0汤圆

发表于 2015-7-18 21:20:39 | 显示全部楼层
10xjzheng 发表于 2015-7-18 11:02
串口速率够就行,不过估计占用51资源会比较多

多谢回复~下午试了下,可以绘制出传感器的曲线。不知道可不可以在上位机上直接修改PID参数进行调节,上位机上的帮助信息有一部分没有看明白~

出0入0汤圆

发表于 2015-7-18 21:43:51 | 显示全部楼层
最近也在做平衡的题目,帮顶。楼主感兴趣也可以看看http://www.amobbs.com/forum.php? ... =5627184&extra=

出0入0汤圆

发表于 2015-7-19 08:41:02 | 显示全部楼层
10xjzheng 发表于 2015-7-18 21:31
上位机发送数据下来,然后你进行解析,提取出功能字,具体看协议,然后将数据段保存到对应PID变量,最后 ...

发送PID数据帧1....
PID数据帧1发送成功!
发送PID数据帧2....
通信出错,请重试

再次请教一下,上位机向下发送时会发送几个数据帧???

void Data_Receive_Anl(u8 *data_buf,u8 num)
{
        vs16 rc_value_temp;
        u8 sum = 0;
       
        for(u8 i=0;i<(num-1);i++)
                sum += *(data_buf+i);
        if(!(sum==*(data_buf+num-1)))                return;                //判断sum
        if(!(*(data_buf)==0xAA && *(data_buf+1)==0xAF))                return;                //判断帧头
/////////////////////////////////////////////////////////////////////////////////////
        if(*(data_buf+2)==0X01)
        {
                if(*(data_buf+4)==0X01)
                        MPU6050_CalOff_Acc();
                if(*(data_buf+4)==0X02)
                        MPU6050_CalOff_Gyr();
                if(*(data_buf+4)==0X03)
                {MPU6050_CalOff_Acc();MPU6050_CalOff_Gyr();}
//                if(*(data_buf+4)==0X04)
//                        Cal_Compass();
//                if(*(data_buf+4)==0X05)
//                        MS5611_CalOffset();
        }
        if(*(data_buf+2)==0X02)
        {
                if(*(data_buf+4)==0X01)
                {
                        Send_PID1 = 1;
                        Send_PID2 = 1;
                        Send_PID3 = 1;
                        Send_PID4 = 1;
                        Send_PID5 = 1;
                        Send_PID6 = 1;
                }
                if(*(data_buf+4)==0X02)
                        Send_Offset = 1;
        }
#ifndef CONTROL_USE_RC
        if(*(data_buf+2)==0X03)
        {
                Rc_D.THROTTLE = (vs16)(*(data_buf+4)<<8)|*(data_buf+5);
                Rc_D.YAW = (vs16)(*(data_buf+6)<<8)|*(data_buf+7);
                Rc_D.ROLL = (vs16)(*(data_buf+8)<<8)|*(data_buf+9);
                Rc_D.PITCH = (vs16)(*(data_buf+10)<<8)|*(data_buf+11);
                Rc_D.AUX1 = (vs16)(*(data_buf+12)<<8)|*(data_buf+13);
                Rc_D.AUX2 = (vs16)(*(data_buf+14)<<8)|*(data_buf+15);
                Rc_D.AUX3 = (vs16)(*(data_buf+16)<<8)|*(data_buf+17);
                Rc_D.AUX4 = (vs16)(*(data_buf+18)<<8)|*(data_buf+19);
                Rc_D.AUX5 = (vs16)(*(data_buf+20)<<8)|*(data_buf+21);
                Rc_D.AUX6 = (vs16)(*(data_buf+21)<<8)|*(data_buf+22);
                Rc_Fun(&Rc_D,&Rc_C);
        }
#endif
        if(*(data_buf+2)==0X10)                                                                //PID1
        {
                        PID_ROL.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_ROL.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/1000;
                        PID_ROL.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_PIT.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_PIT.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/1000;
                        PID_PIT.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        PID_YAW.P = (float)((vs16)(*(data_buf+16)<<8)|*(data_buf+17))/100;
                        PID_YAW.I = (float)((vs16)(*(data_buf+18)<<8)|*(data_buf+19))/100;
                        PID_YAW.D = (float)((vs16)(*(data_buf+20)<<8)|*(data_buf+21))/100;
                        Data_Send_Check(sum);
        }
        if(*(data_buf+2)==0X11)                                                                //PID2
        {
                        PID_ALT.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_ALT.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/100;
                        PID_ALT.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_POS.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_POS.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/100;
                        PID_POS.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        PID_PID_1.P = (float)((vs16)(*(data_buf+16)<<8)|*(data_buf+17))/100;
                        PID_PID_1.I = (float)((vs16)(*(data_buf+18)<<8)|*(data_buf+19))/100;
                        PID_PID_1.D = (float)((vs16)(*(data_buf+20)<<8)|*(data_buf+21))/100;
                        Data_Send_Check(sum);
        }
        if(*(data_buf+2)==0X12)                                                                //PID3
        {
                        PID_PID_2.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_PID_2.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/100;
                        PID_PID_2.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_PID_3.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_PID_3.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/100;
                        PID_PID_3.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        PID_PID_4.P = (float)((vs16)(*(data_buf+16)<<8)|*(data_buf+17))/100;
                        PID_PID_4.I = (float)((vs16)(*(data_buf+18)<<8)|*(data_buf+19))/100;
                        PID_PID_4.D = (float)((vs16)(*(data_buf+20)<<8)|*(data_buf+21))/100;
                        Data_Send_Check(sum);
        }
        if(*(data_buf+2)==0X13)                                                                //PID4
        {
                        PID_PID_5.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_PID_5.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/100;
                        PID_PID_5.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_PID_6.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_PID_6.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/100;
                        PID_PID_6.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        PID_PID_7.P = (float)((vs16)(*(data_buf+16)<<8)|*(data_buf+17))/100;
                        PID_PID_7.I = (float)((vs16)(*(data_buf+18)<<8)|*(data_buf+19))/100;
                        PID_PID_7.D = (float)((vs16)(*(data_buf+20)<<8)|*(data_buf+21))/100;
                        Data_Send_Check(sum);
        }
        if(*(data_buf+2)==0X14)                                                                //PID5
        {
                        PID_PID_8.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_PID_8.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/100;
                        PID_PID_8.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_PID_9.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_PID_9.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/100;
                        PID_PID_9.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        PID_PID_10.P = (float)((vs16)(*(data_buf+16)<<8)|*(data_buf+17))/100;
                        PID_PID_10.I = (float)((vs16)(*(data_buf+18)<<8)|*(data_buf+19))/100;
                        PID_PID_10.D = (float)((vs16)(*(data_buf+20)<<8)|*(data_buf+21))/100;
                        Data_Send_Check(sum);
        }
        if(*(data_buf+2)==0X15)                                                                //PID6
        {
                        PID_PID_11.P = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/100;
                        PID_PID_11.I = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/100;
                        PID_PID_11.D = (float)((vs16)(*(data_buf+8)<<8)|*(data_buf+9))/100;
                        PID_PID_12.P = (float)((vs16)(*(data_buf+10)<<8)|*(data_buf+11))/100;
                        PID_PID_12.I = (float)((vs16)(*(data_buf+12)<<8)|*(data_buf+13))/100;
                        PID_PID_12.D = (float)((vs16)(*(data_buf+14)<<8)|*(data_buf+15))/100;
                        Data_Send_Check(sum);
                        EE_SAVE_PID();
        }
        if(*(data_buf+2)==0X16)                                                                //OFFSET
        {
                        AngleOffset_Rol = (float)((vs16)(*(data_buf+4)<<8)|*(data_buf+5))/1000;
                        AngleOffset_Pit = (float)((vs16)(*(data_buf+6)<<8)|*(data_buf+7))/1000;
        }
}

这几个数据帧都会发送下来吗?虽然没有用到?

出0入0汤圆

发表于 2015-7-19 08:49:55 | 显示全部楼层
10xjzheng 发表于 2015-7-18 21:31
上位机发送数据下来,然后你进行解析,提取出功能字,具体看协议,然后将数据段保存到对应PID变量,最后 ...

发送PID数据帧1....
PID数据帧1发送成功!
发送PID数据帧2....
通信出错,请重试

再次请教一下,上位机向下发送时会发送几个数据帧???

是不是如果后边几个数据帧没有接收就会出现上面的错误?

出0入10汤圆

 楼主| 发表于 2015-7-19 10:29:02 | 显示全部楼层
爱新觉罗_极刚霸 发表于 2015-7-19 08:41
发送PID数据帧1....
PID数据帧1发送成功!
发送PID数据帧2....

你在界面上看到的所有数据都会发送下来,你应该是少了检测了几个功能字,然后就少发送了cheaksum

出0入10汤圆

 楼主| 发表于 2015-7-19 10:29:22 | 显示全部楼层
具体看协议,excel文档即可知道!

出0入0汤圆

发表于 2015-7-19 10:37:01 | 显示全部楼层
10xjzheng 发表于 2015-7-19 10:29
具体看协议,excel文档即可知道!

多谢你啦 ,已经可以发送了~但因为收发都是在串口中断中处理的,所以有时上位机会接受失败或者上位机发送失败,不知道你是如何处理哒?
我用的是STC12C5A60S2

出0入10汤圆

 楼主| 发表于 2015-7-19 10:44:05 | 显示全部楼层
爱新觉罗_极刚霸 发表于 2015-7-19 10:37
多谢你啦 ,已经可以发送了~但因为收发都是在串口中断中处理的,所以有时上位机会接受失败或者上 ...

我用的是状态机分析协议,搞到一帧数据,然后将获得的数据放到1缓冲区,接着在main中中进行处理缓冲区1,在给出一个缓冲区B在中断中进行接收,中断里面只是接收数据+几个判断。
  1. void USART1_IRQHandler(void)
  2. {
  3.         char com_data;
  4.        
  5.        
  6.        
  7.         //如果是串口接收中断
  8.         if( USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
  9.         {
  10.                 USART_GetITStatus(USART1,USART_IT_RXNE);
  11.                 com_data = USART_ReceiveData(USART1);
  12.                 if(Rx_Adr==0)                //寻找帧头0XAAAF
  13.                 {
  14.                         if(com_data==0xAA)       
  15.                         {
  16.                                 Rx_Buf[Rx_Act][0] = com_data;
  17.                                 Rx_Adr = 1;
  18.                         }
  19.                 }
  20.                 else if(Rx_Adr==1)
  21.                 {
  22.                         if(com_data==0xAF)
  23.                         {
  24.                                 Rx_Buf[Rx_Act][1] = com_data;
  25.                                 Rx_Adr = 2;
  26.                         }
  27.                         else
  28.                                 Rx_Adr = 0;
  29.                 }
  30.                 else if(Rx_Adr==2)                //FUN
  31.                 {
  32.                         Rx_Buf[Rx_Act][2] = com_data;
  33.                         Rx_Adr = 3;
  34.                 }
  35.                 else if(Rx_Adr==3)                //LEN
  36.                 {
  37.                         Rx_Buf[Rx_Act][3] = com_data;
  38.                         Rx_Adr = 4;
  39.                 }
  40.                 else
  41.                 {
  42.                         Rx_Buf[Rx_Act][Rx_Adr] = com_data;
  43.                         Rx_Adr ++;
  44.                 }
  45.                
  46.                 if(Rx_Adr==Rx_Buf[Rx_Act][3]+5)                //数据接收完毕
  47.                 {
  48.                         Rx_Adr = 0;
  49.                         if(Rx_Act)       
  50.                         {
  51.                                 Rx_Act = 0;                         //切换缓存
  52.                                 Rx_Ok1 = 1;
  53.                         }
  54.                         else
  55.                         {
  56.                                 Rx_Act = 1;
  57.                                 Rx_Ok0 = 1;
  58.                         }
  59.                 }
  60.         }
  61. }
复制代码

你也可以用FIFO,在中断中放入缓冲区,然后while中取出来分析,应该可以防止通讯失败,但是效率应该没有上面的状态机高。

出0入0汤圆

发表于 2015-7-19 11:18:09 | 显示全部楼层
10xjzheng 发表于 2015-7-19 10:44
我用的是状态机分析协议,搞到一帧数据,然后将获得的数据放到1缓冲区,接着在main中中进行处理缓冲区1, ...

你们也是电赛培训吧,感觉你好厉害啊!

出0入0汤圆

发表于 2015-7-19 11:31:58 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2015-7-20 08:11:52 | 显示全部楼层
10xjzheng 发表于 2015-7-17 11:38
你要哪部分程序我直接发上来,工程不可以。
死区就看你调的效果,我不知道 ...

好的楼主!
谢谢哦!
那我看看楼主对于PID计算中与定时器相关联的部分吧!
以及PID部分的定义结构体。
另外,楼主请指点一下,如果做一个正反方向转动的风机,并且可以两面吹风,双向控制角度这样的PID可以做成不?

出0入10汤圆

 楼主| 发表于 2015-7-20 09:15:24 | 显示全部楼层
阿豪博士 发表于 2015-7-20 08:11
好的楼主!
谢谢哦!
那我看看楼主对于PID计算中与定时器相关联的部分吧!
  1. /* 定义PID类型 */
  2. typedef struct PID_Type PID_Type;

  3. struct PID_Type
  4. {
  5.         float SetPoint;
  6.         unsigned char SetPointChange;  //设置目标发生变化
  7.         int CtrlValue;//0~1000
  8.         float kP;
  9.         float kI;
  10.         float kD;
  11. };
复制代码

下面的计算工程的计算频率时100Hz
  1.                         MPU6050ReadAcc(Accel);
  2.                         MPU6050ReadGyro(Gyro);

  3.       //对加速度进行平滑滤波
  4.                         acc_filter(Accel,Mag);       
  5.                         //将获得的原始数据转化为度/s
  6.                         AnglePot=Gyro[0]*GYRO_SCALE;
  7.                         //根据滤波后的加速度计算角度
  8.                         Angle=atan2(Accel[1],Accel[2])*57.295780;
  9.                         //进行卡尔曼滤波
  10.                         kalman_filter(Angle,AnglePot,&f_Angle,&f_AnglePot);
  11.                         //对角度进行平滑滤波
  12.                         //angle_filter(&PID1,Angle,&AfterAccel);
  13.                         //返回PID的控制量,并设置占空比
  14.                         SET_DUTY(speed_filter(pid2(&PID1,f_Angle,f_AnglePot)));
  15.                        


  16. //显示波形
  17.                         Mag[0]=PID1.SetPoint;
  18.                         Mag[1]=Angle;
  19.                         Mag[2]=Duty;
  20.                         //角度、角速度放到PID2里面去
  21.                         PID2.kP=Angle;
  22.                         PID2.kI=AnglePot;
  23.                         PID2.kD=f_Angle;
  24.                         //显示相差的大小
  25.                         PID3.kD=fabs(PID1.SetPoint-Angle);
  26.                         //波形数据从串口发送出去->蓝牙->空气->蓝牙->串口->串口转USB->电脑接收并处理
  27.                         NingmingSend_Data(Gyro,Accel,Mag);
  28.                         Task_Delay[1]=10;
复制代码

计算相关的函数
  1. #include "include.h"
  2. //卡尔曼滤波
  3. static float angle, angle_dot;                
  4. const float Q_angle = 0.002, Q_gyro = 0.002, R_angle = 0.5, dt = 0.01;
  5. static float P[2][2]={
  6.                                        { 1, 0 },
  7.                                    { 0, 1 }
  8.                                  };       
  9. static float Pdot[4] = {0, 0, 0, 0};
  10. const u8 C_0 = 1;
  11. static float q_bias, angle_err, PCt_0, PCt_1, E, K_0, K_1, t_0, t_1;

  12. //平滑滤波
  13. short ax_buf[FILTER_COUNT]={0};
  14. short ay_buf[FILTER_COUNT]={0};
  15. short az_buf[FILTER_COUNT]={0};
  16. short Angle_buf[FILTER_COUNT]={0};


  17. s16 speed_buf[FILTER_COUNT]={0};
  18. /*************************************************

  19. 名称:void acc_filter(void)
  20. 功能:加速度计数据滤波
  21. 输入参数:据滤波后的数据
  22. 输出参数:无
  23. 返回值:  无
  24. **************************************************/
  25. void acc_filter(short *Accel,short *Mag)
  26. {
  27.   u8 i;
  28.   s32 ax_sum = 0, ay_sum = 0, az_sum = 0;

  29.   for(i = 1 ; i < FILTER_COUNT; i++)
  30.   {
  31.     ax_buf[i - 1] = ax_buf[i];
  32.           ay_buf[i - 1] = ay_buf[i];
  33.           az_buf[i - 1] = az_buf[i];
  34.   }

  35.   ax_buf[FILTER_COUNT - 1] = Accel[0];
  36.   ay_buf[FILTER_COUNT - 1] = Accel[1];
  37.   az_buf[FILTER_COUNT - 1] = Accel[2];

  38.   for(i = 0 ; i < FILTER_COUNT; i++)
  39.   {
  40.     ax_sum += ax_buf[i];
  41.           ay_sum += ay_buf[i];
  42.           az_sum += az_buf[i];
  43.   }

  44.   Mag[0] = (s16)(ax_sum / FILTER_COUNT);
  45.   Mag[1] = (s16)(ay_sum / FILTER_COUNT);
  46.   Mag[2] = (s16)(az_sum / FILTER_COUNT);
  47. }

  48. //角度的平滑滤波
  49. void angle_filter(short Angle,short *AfterAngle)
  50. {
  51.         u8 i;
  52.         s32 angle_sum = 0;
  53.        
  54.         for(i = 1 ; i < FILTER_COUNT; i++)
  55.   {
  56.     Angle_buf[i - 1] = Angle_buf[i];
  57.         }
  58.        
  59.         Angle_buf[FILTER_COUNT - 1] = Angle;
  60.        
  61.   for(i = 0 ; i < FILTER_COUNT; i++)
  62.   {
  63.     angle_sum += Angle_buf[i];
  64.         }
  65.        
  66.         (*AfterAngle)=(s16)(angle_sum / FILTER_COUNT);
  67. }

  68. /*************************************************

  69. 名称:void kalman_filter(float angle_m, float gyro_m, float *angle_f, float *angle_dot_f)
  70. 功能:陀螺仪数据与加速度计数据通过滤波算法融合
  71. 输入参数:
  72.           float angle_m   加速度计计算的角度
  73.           float gyro_m    陀螺仪角速度

  74.           float *angle_f  融合后的角度
  75.           float *angle_dot_f  融合后的角速度
  76. 输出参数:滤波后的角度及角速度
  77. 返回值:无
  78. **************************************************/
  79. void kalman_filter(float angle_m, float gyro_m, float *angle_f, float *angle_dot_f)                       
  80. {
  81.   angle += (gyro_m - q_bias) * dt;
  82.        
  83.   Pdot[0]  =Q_angle - P[0][1] - P[1][0];
  84.   Pdot[1] = -P[1][1];
  85.   Pdot[2] = -P[1][1];
  86.   Pdot[3] = Q_gyro;
  87.        
  88.   P[0][0] += Pdot[0] * dt;
  89.   P[0][1] += Pdot[1] * dt;
  90.   P[1][0] += Pdot[2] * dt;
  91.   P[1][1] += Pdot[3] * dt;
  92.        
  93.   angle_err = angle_m - angle;
  94.        
  95.   PCt_0=C_0 * P[0][0];
  96.   PCt_1=C_0 * P[1][0];
  97.        
  98.   E = R_angle + C_0 * PCt_0;
  99.        
  100.   K_0 = PCt_0 / E;
  101.   K_1 = PCt_1 / E;
  102.        
  103.   t_0 = PCt_0;
  104.   t_1 = C_0 * P[0][1];

  105.   P[0][0] -= K_0 * t_0;
  106.   P[0][1] -= K_0 * t_1;
  107.   P[1][0] -= K_1 * t_0;
  108.   P[1][1] -= K_1 * t_1;
  109.                
  110.   angle        += K_0 * angle_err;
  111.   q_bias += K_1 * angle_err;
  112.   angle_dot = gyro_m - q_bias;

  113.   *angle_f = angle;
  114.   *angle_dot_f = angle_dot;
  115. }



  116. /*************************************************

  117. 名称:void speed_filter(void)
  118. 功能:速度滤波
  119. 输入参数:无
  120. 输出参数:无
  121. 返回值:  无
  122. **************************************************/
  123. int speed_filter(int speed)
  124. {
  125.   u8 i;
  126.   s32 speed_sum = 0;

  127.   for(i = 1 ; i < FILTER_COUNT; i++)
  128.   {
  129.     speed_buf[i - 1] = speed_buf[i];
  130.   }

  131.   speed_buf[FILTER_COUNT - 1] = speed;

  132.   for(i = 0 ; i < FILTER_COUNT; i++)
  133.   {
  134.     speed_sum += speed_buf[i];
  135.   }
  136.    
  137.   return (s16)(speed_sum / FILTER_COUNT);
  138. }
复制代码


双向PID是很好的,我没有试过,但是曾经想试!你做个,到时发个视频上来!

出0入0汤圆

发表于 2015-7-20 10:55:05 | 显示全部楼层
10xjzheng 发表于 2015-7-20 09:15
下面的计算工程的计算频率时100Hz

计算相关的函数

好的 那我做一个风机吹乒乓球的。
我用激光探测乒乓球的高度 然后控制风机的旋转。
我正在做电路部分。

出0入0汤圆

发表于 2015-7-20 11:46:25 | 显示全部楼层
请教一下楼主,现在困惑的一个问题是:根据MPU6050这一类的MEMS,可以得到加速度与角速度。
然后根据加速度与角速度得到一个角度姿态数值。
我们为了得到预期的姿态控制角度,例如68°,50°等等,我们是根据角度变化来进行计算比较好呢还是直接根据角速度值来进行计算比较好呢?
或许我表达的有点啰嗦,是不是这一类的计算,最终都是根据姿态的变化以及姿态的变化速率来进行PID计算的?

出0入0汤圆

发表于 2015-7-20 12:00:57 | 显示全部楼层
学习,谢谢!!!

出0入10汤圆

 楼主| 发表于 2015-7-20 13:36:15 | 显示全部楼层
阿豪博士 发表于 2015-7-20 11:46
请教一下楼主,现在困惑的一个问题是:根据MPU6050这一类的MEMS,可以得到加速度与角速度。
然后根据加速度 ...

PID是差值进行计算

出0入0汤圆

发表于 2015-7-20 14:02:33 | 显示全部楼层
10xjzheng 发表于 2015-7-20 13:36
PID是差值进行计算

最理想的状态应该是PV与SV的差值为0吧。
在PID计算中,将误差设置为0比较好还是设置一个比较合适的范围比较好呢?
就是在这一部分:
//如果PID1改变了,将err都置0
if(PID->SetPointChange==1)
        {
               PID->SetPointChange=0;
              err0=err1=err2=0;
       }
//
请楼主麻烦指点一下。

出0入0汤圆

发表于 2015-7-20 16:51:29 | 显示全部楼层
MARK,收藏了先

出0入0汤圆

发表于 2015-7-21 09:23:31 | 显示全部楼层
请教一下楼主,
//返回PID的控制量,并设置占空比
SET_DUTY(speed_filter(pid2(&PID1,f_Angle,f_AnglePot)));
这里的:&PID1,PID1是另外定义了一个变量指针吗?

出0入0汤圆

发表于 2015-7-22 08:42:00 | 显示全部楼层
请教一下 楼主,在上位机发送数据的时候,这个SetPointChange 如何调呢?

出0入0汤圆

发表于 2015-7-22 16:09:22 | 显示全部楼层
真是个好帖子啊,先收藏,过两天调的时候在看

出0入0汤圆

发表于 2015-7-23 17:53:00 | 显示全部楼层
PID计算中,角速度没有用到吧。为什么还要写入到PID的参数中呢……

出0入0汤圆

发表于 2015-8-4 07:34:06 | 显示全部楼层
学习一下,顶

出0入0汤圆

发表于 2015-8-4 18:01:23 | 显示全部楼层
还是比较又难度的,学习中!

出0入0汤圆

发表于 2015-8-5 09:18:00 | 显示全部楼层
楼主可否知道下上位机发数据部分代码呢
我试了很多次没成功诶

出0入0汤圆

发表于 2015-9-10 16:08:14 | 显示全部楼层


你这个PID公式有何处出吗?  

我按照标准公式推了一遍,应该是如下: Kp * e - KI * ek-1 + Kd *ek-2;
PS: 我看着有一本书,请教书名和著作人啊;

出0入0汤圆

发表于 2015-9-10 17:48:49 | 显示全部楼层
不错。赞一个

出0入0汤圆

发表于 2015-9-11 08:48:09 | 显示全部楼层

楼主学以致用啊,赞一个

出0入0汤圆

发表于 2015-9-15 15:39:45 | 显示全部楼层
请教一下楼主,
SetPointChange=1;
这个变量该如何控制呢?
是需要计算一次或者接收到目标值修改一次,就用到这一条指令吗?

出0入0汤圆

发表于 2015-9-16 00:19:26 | 显示全部楼层

出0入0汤圆

发表于 2015-9-19 11:24:04 | 显示全部楼层
并没什么卵用,调试出来就显摆,

出0入10汤圆

 楼主| 发表于 2015-9-19 12:16:34 | 显示全部楼层
若非 发表于 2015-9-19 11:24
并没什么卵用,调试出来就显摆,

是啊,you can you up

出0入10汤圆

 楼主| 发表于 2015-9-19 12:19:05 | 显示全部楼层
若非 发表于 2015-9-19 11:24
并没什么卵用,调试出来就显摆,

是啊,我就是来显摆的,显摆给懂的人看啊

出0入0汤圆

发表于 2015-11-9 21:55:06 | 显示全部楼层
很实用 谢谢分享

出0入0汤圆

发表于 2016-1-4 21:43:24 | 显示全部楼层
不错,真的很好!!

出100入101汤圆

发表于 2016-1-5 09:16:33 | 显示全部楼层
这个是PID调什么?

出0入0汤圆

发表于 2016-1-5 09:17:42 | 显示全部楼层
不错的经验,谢谢分享

出0入0汤圆

发表于 2016-1-5 13:23:33 | 显示全部楼层
支持楼主 收藏中

出0入0汤圆

发表于 2016-1-13 09:58:55 | 显示全部楼层
代码能否开源啊

出0入0汤圆

发表于 2016-1-13 10:05:15 | 显示全部楼层
楼主,请问增量式PID算法如何处理积分分离啊?    谢谢
我的处理方法: 如果error_abs 小于10度,则 积分因子(=积分系数*error)加到控制增量Δu(k)中, 否则加0!    是否正确?

出0入0汤圆

发表于 2016-1-13 10:10:48 | 显示全部楼层
做的很好!!!!!

出0入10汤圆

 楼主| 发表于 2016-1-13 10:13:51 | 显示全部楼层
zcf287 发表于 2016-1-13 10:05
楼主,请问增量式PID算法如何处理积分分离啊?    谢谢
我的处理方法: 如果error_abs 小于10度,则 积分因 ...

还有一种:如果积分部分太大,删之。
源码部分我已经告诉你们去帮助那里获取啦,试试就知道了。

出0入0汤圆

发表于 2016-1-13 10:15:09 | 显示全部楼层
学习了。这个试验很好,配合上位机。

出0入0汤圆

发表于 2016-1-13 22:38:14 | 显示全部楼层
想问一下如果用cc3200做飞控可以用这个调吗?

出0入0汤圆

发表于 2016-1-14 00:47:15 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2016-1-14 09:52:10 | 显示全部楼层
10xjzheng 发表于 2016-1-13 10:13
还有一种:如果积分部分太大,删之。
源码部分我已经告诉你们去帮助那里获取啦,试试就知道了。 ...

谢谢了,如果(控制增量Δu(k)+上一次输出结果)超过结果限幅, 下一次积分因子赋值为0。    是这样吗?

出0入0汤圆

发表于 2016-1-14 14:53:22 | 显示全部楼层
这个不错,MARK

出0入0汤圆

发表于 2016-1-18 10:49:38 | 显示全部楼层
正在研究,MARK

出0入0汤圆

发表于 2016-1-19 11:10:01 | 显示全部楼层
学习,收藏了!

出0入0汤圆

发表于 2016-5-9 10:39:26 | 显示全部楼层
以前电赛的时候我弄得也是这个帆板控制

出0入0汤圆

发表于 2016-5-9 22:45:21 | 显示全部楼层
请教一下楼主:
//如果PID1改变了,将err都置0
        if(PID->SetPointChange==1)
        {
                PID->SetPointChange=0;
                err=err1=err2=0;
        }
“PID->SetPointChange==1”,这个参数是基于那里在改变的呢?

出0入0汤圆

发表于 2016-5-11 09:34:41 | 显示全部楼层
顶下这个帖子,教会很多人调试方法。

出0入0汤圆

发表于 2016-7-5 15:19:05 | 显示全部楼层
本帖最后由 小桀 于 2016-7-5 15:20 编辑

楼主, 请问这本书叫什么名字啊, 在网上没有搜到。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入10汤圆

 楼主| 发表于 2016-7-29 09:52:40 | 显示全部楼层
小桀 发表于 2016-7-5 15:19
楼主, 请问这本书叫什么名字啊, 在网上没有搜到。

https://book.douban.com/subject/6052207/
电动机的单片机控制

出0入0汤圆

发表于 2016-8-5 12:00:57 | 显示全部楼层
非常好用。谢谢楼主

出0入0汤圆

发表于 2019-5-15 22:15:40 | 显示全部楼层
好资料,谢谢分享!

出0入0汤圆

发表于 2019-5-16 09:21:30 | 显示全部楼层
好资料,收藏了

出0入0汤圆

发表于 2019-5-21 17:55:08 | 显示全部楼层
谢谢楼主无私奉献啊!!!!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 03:38

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表