开发手记 · 第 2 / 9 篇 合集目录 ‹ 上一篇 下一篇 ›

读一段老代码时,我注意到它处理旋转信号的方式和我习惯的完全相反。

它要测的是回转件一圈的径向变化,回答两个问题:偏差最大是多少,出现在圆周哪个位置。信号毛得很,传感器漂、装夹偏、表面有划痕、车间在震,全叠在上面。

我自己的第一反应一直是加滤波,把曲线抹平。但这套代码里我翻遍了也没找到任何滤波器。它干的事很奇怪:先把整条曲线做一次频谱分解,然后只取其中一个分量,剩下的全不要。

我看到那行代码时愣了一下:

var amplitude = Math.Sqrt(re * re + im * im) * 4 / nCount;

那个 * 4 / nCount 我看不懂,后来自己推了一遍才明白:对一串正弦做频谱分解,它在自己那个频点上的能量是幅值乘以点数的一半,反过来一解,再换算成峰峰值,正好是这个系数。一步就把幅值算出来了,连找最大值最小值都省了。

但真正让我改观的不是这个系数,是它背后的思路。它压根没打算把噪声抹掉。

我把频谱图摊开看,各个成分是分开待着的:零位漂移在最左边一格,我要的信号在转频那一格,划痕和键槽在更右边,随机噪声铺满整条。它们根本不住在一起。那我为什么要用一把刷子把整张图刷一遍,直接用梳子把那一格梳出来不就行了。

这件事后来改变了我看很多脏数据的习惯。以前拿到一条脏信号,第一反应是把它弄干净;现在我会先问一句,我要的和不要的,是不是在同一个格子里。在,那确实只能硬扛;不在,大多数情况都是这样,那抹平这个动作本身就找错了方向。

顺带提一个细节。那段代码里界面上是有"滤波点数"这个参数的,用户能设,配置也能存,可我 grep 了一圈,算法里从头到尾没人读过它。一个没人读的参数,安静地待在那里,像是在说这条路我们没走。

这种问法也不只用在信号上。一堆报错日志刷过来,我以前的做法是先按时间聚合、降采样,把量压下去再看,可真正有用的那几条往往就淹没在这个压下去的动作里。后来我改成先想清楚要找的那类错误长什么样,再按这个样子去筛,剩下的不看。动作还是那个动作,方向反过来了,一个是把多的变少,一个是把要的挑出来。