减小float累加运算产生的误差

本文探讨了在浮点数累加过程中由于精度问题导致的误差,并提供了两种解决方案:分段累加和贮存残留值。通过实例代码展示了这两种方法如何有效减少误差,使结果更精确。

问题代码

float a = 0.f;
for(int i = 0; i < 10000; ++i){
	a += 0.01f;
}

输出a的值结果为:
输出结果:

可以看到结果存在0.002953的误差,这是因为float的精度问题,在多次运算绝对误差不断累加所导致的。

解决方法

方法一:分段累加

float a=0.f;
for(int i = 0; i < 100; ++i){
	float b = 0.f;
	for(int j = 0; j < 100; j++){
		b += 0.01f;
	}
	a += b;
}

采用这种方法输出a的值结果为99.999985,存在的误差为0.000015,比之前小了很多。减小相加两个浮点数之间的大小差距可以在一定程度上减少相加导致的误差。

方法二:贮存残留值

float a = 0.f;
float r = 0.f; //类积未加上的值
float t;
for(int i = 0; i < 10000; ++i){
   r += 0.01f; 
   t = a; 		//保存本次加法前的a值
   a += r; 		//执行本次加法
   t = a - t;	//本次加法中a实际增加的值
   r  =  r - t;		//理想a应增加的值 - 实际a增加的值 = 本次未加上的值
}

这个技巧初见比较不好理解,所以笔者基本每行代码都加了注释,用这个方法计算得到的a的值正正好为100,误差为0。
这个方法的核心就是用r记录每次加法后残留的没能加上的值,残留值在r中不断积累,最终在某次加法中成功加上。

#include "stm32f10x.h" // Device header typedef struct PID { float Kp; // Proportional Const P系数 float Ki; // Integral Const I系数 float LastError; // Error[-1] float Error; // Error[0 ] float integral; float output; float integralmax; //积分项的最大值 float outputmax; //输出项的最大值 } PID; // PID初始化 //在积分项累加时,可添加积分限幅,防止积分项过大导致系统不稳定。 void PID_Init(PID *pid, float Kp , float Ki ,float outputmax,float integralmax) { pid->Kp= Kp; pid->Ki= Ki; pid->integral=pid->LastError = pid->Error = pid->output = 0; pid->outputmax = outputmax; pid->integralmax = integralmax; } //比例系数(Kp):Kp 增大,系统响应速度加快, //但可能会导致系统超调增大,甚至出现振荡。例如,当 Kp 过小时, //小车对灰度传感器检测到的偏差响应缓慢,不能及时调整方向; //而 Kp 过大时,小车会在轨迹左右剧烈摆动。可先将 Ki 和 Kd 设置为 0, //逐步增大 Kp,直到小车开始出现轻微振荡。 //积分系数(Ki):Ki 主要用于消除系统的稳态误差。当系统存在稳态误差时,适当增大 Ki 可以 //使误差逐渐减小。但 Ki 过大可能会导致积分饱和, //使系统出现较大的超调。在 Kp 调整好后,逐步增加 Ki,直到稳态误差基本消除。 float PID_Incremental_Calc(PID *pid, float Target_val, float Actual_val) { pid->Error = Target_val- Actual_val; //OUT+=Kp[e(k)-e(k-1)]+Ki*e(k) pid->output += pid->Kp* ( pid->Error - pid->LastError )+ pid->Ki* pid->Error ; //更新误差记录 pid->LastError = pid->Error; pid->integral += pid->Error; if (pid->integral > pid->integralmax) { pid->integral = pid->integralmax; } else if (pid->integral < -pid->integralmax) { pid->integral = -pid->integralmax; } //限制输出最大值,防止出现突发意外。输出outputmax的最大值 if(pid->output > pid->outputmax ) pid->output = pid->outputmax; if(pid->output < - pid->outputmax ) pid->output = -pid->outputmax; return pid->output ; //输出为pwm值 }
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值