/**
* 创建一个带限幅的一阶低通滤波器(指数加权移动平均)
* @param {number} alpha - 平滑系数 (0.0 ~ 1.0),越小滤波越强
* @param {number} minValue - 输入/输出最小允许值(钳位下界)
* @param {number} maxValue - 输入/输出最大允许值(钳位上界)
* @param {number} [initialValue] - 初始滤波值;若不传,则首次调用时自动设为 clamp(newValue)
* @returns {(newValue: number) => number} 滤波函数,接收新值,返回滤波后值
*/
function createLowPassFilter(alpha, minValue, maxValue, initialValue) {
if (alpha < 0 || alpha > 1) throw new Error('alpha must be in [0, 1]');
if (minValue >= maxValue) throw new Error('minValue must be < maxValue');
let filtered = initialValue !== undefined
? Math.max(minValue, Math.min(initialValue, maxValue))
: undefined;
return function(newValue) {
if (typeof newValue !== 'number' || isNaN(newValue)) {
throw new Error('newValue must be a valid number');
}
// 钳位输入
const clamped = Math.max(minValue, Math.min(newValue, maxValue));
// 首次调用:用钳位后的值初始化
if (filtered === undefined) {
filtered = clamped;
return filtered;
}
// 指数滤波:y[n] = α·x[n] + (1−α)·y[n−1]
filtered = alpha * clamped + (1 - alpha) * filtered;
// 输出也做钳位(防御性设计,防止浮点累积误差越界)
return Math.max(minValue, Math.min(filtered, maxValue));
};
}
// ✅ 使用示例(直接复制即可运行)
const filter = createLowPassFilter(0.1, 0, 500); // alpha=0.1, range [0,500]
console.log(filter(100)); // → 100 (首次调用,初始化)
console.log(filter(450)); // → 135 (0.1×450 + 0.9×100 = 135)
console.log(filter(520)); // → 173.5 (0.1×500(已钳位) + 0.9×135 ≈ 173.5)