金牌会员
- 积分
- 1497
- 威望
- 753
- 贡献
- 436
- 兑换币
- 471
- 注册时间
- 2013-10-27
- 在线时间
- 154 小时
|
2#
楼主 |
发表于 2013-11-1 13:37:24
|
只看该作者
PID调节分为位置式和增量式,小车里面应该用的是位置式。看似很复杂的公式其实也没有那么令人捉摸不透,假设现在就是一个控制小车的车速度的问题,在任何一个时刻,一个环境,要想让小车的速度按照一个速度输出。必然存在着一个输入。这个输入值不能确定,确切说不能用数学公式准确的刻画,而且环境时刻在改变,即使知道了输入值和输出速度的关系,环境一旦改变对应关系自然也就变了,为了解决这个复杂问题,所以才引入了PID调节。
需要明白的是小车的速度和输入之间的关系不是线性的,肯定不存在v=k*in这种表达式。如果真的是这样的话,只需要一个k就可以调节了,假如现在的速度是3m/s,输入电压是3v,要想达到4m/s仅仅需要使得输入增大1v就搞定了,存在着数学关系in=in+(v1-v)*k0;v1是想要达到的速度,v是现在的速度,这个k0是可以确定的也就是k的倒数。但是这么做不切实际。
当初存在很多疑惑,认为通过这个k0调节足够,当初我认为即使只有k0输出也会在目标输出值上下进行震荡,但是会慢慢的靠近,一直可以成功达到标准的输出。没有必要用积分和微分,后来就用C语言编写一个程序简单的验证下自己的猜想。
已经知道了v和i之间的不存在线性关系。所以自己假设在一切条件不变的情况下存在着一个固定的数学关系式,当然不是线性的,为了方便起见,我们假设v=in*in;这个是存在的,但是假设我们还不知道,所以不能直接用这个公式控制速度。
假设现在in=1;v自然等于1,现在目标是要求v输出4,应该怎么办呢。
假设只有比例项控制C语言程序如下所示:
double y=16,x=4;,y1=100;//y=x*x,然后取得系数k=0.1
//double err=0;
//double err_array[100]={0};
int i,t;
while(1)
{
for(i=1;i<100;i++)
{
x=x+(y1-y)*0.1;//+(err_array[i]-err_array[i-1])*0.001;
//err_array[i]=(64-y);
y=x*x;
printf("%d: %lf %lf\n",i,x,y);
}
通过更改x和y的初始值和目标值运行可以观测程序运行结果,现在目标值是y1=100;系数目前是0.1
我把目标值改了。改成了81,x=9的时候才是理想控制,看一下运行结果。
果然和我想的一样开始x的值在应该输出的值上下震荡,逐渐接近于9,在第68次x=9.0000;这只是一个,貌似看着这样也可以控制,不需要积分和微分,
但是把x和y的值改变了,目标值改变下就不那么乐观了,比如说我现在把y1改成100,那么x输出应该是10才对,或者是一个非常非常接近与10的数,
这就是事实,执行10000次任然是一个10.07误差还是非常非常大的,说明系统存在某些不可消除的误差,仅仅靠比例项是不可行,很自然,比例项越大,达到目标值越快,但是也不是越大越好,过大震荡就很难消除,时间变长。要求控制速度是快,准,狠。时间长了自然是不行啊。电脑上不能随便设定系数,否则就没有结果了。上面这个次数当然是越少越好了。下面是引入了积分,其实这个积分就是求和,这个不像数学表达式是连续的,是间断的。采样周期也就是1ms吧。
下面是引入了积分的程序:
double y=16,x=4; //y=x*x,然后取得系数k=0.1
double err=0;
double err_array[200]={0};
int i,t;
while(1)
{
for(i=1;i<100;i++)
{
x=x+(100-y)*0.1+(err_array[i-1]-err_array[i])*0.001;
err_array[i]=(64-y);
y=x*x;
printf("%d: %lf %lf\n",i,x,y);
}
在没有引入积分前100次后的结果是这个
引入积分后则是:
10.100和10.656,发现积分确实可以减少误差,一定存在着积分系数使得x更逼近或者能够达到目标值,只不过我没有仔细一个个实验,调节系数。系数一个个调,第一个确定了才能继续调节第二个,先调的是比例项,然后是积分项。这里只是一个简单的比方,没有过多的实际用处,也就是帮助理解下关系。实际用的时候恐怕要复杂点。
还有一个是微分系数,微分是就是求两次误差差值的差,反应了误差的变化趋势,有预测的意味。
下面是使用了微分后的代码
double y=16,x=4; //y=x*x,然后取得系数k=0.1
double err=0;
double err_array[200]={0};
double err_array1[200]={0};
int i,t;
while(1)
{
for(i=1;i<100;i++)
{
x=x+(100-y)*0.1+(err_array[i-1]-err_array[i])*0.001+(err_array1[i]-err_array1[i-1])*0.001;
err_array1[i]=err_array[i-1]-err_array[i];
err_array[i]=(64-y);
y=x*x;
printf("%d: %lf %lf\n",i,x,y);
}
结果是:
更精准了一步,这就是一个简单的PID吧。
这是文本内容,图片内容见下载的文档 |
|