我们每天都在重复地做一些事情. 如果一件无趣的工作需要重复地执行, 我认为, 如果这个动作的执行者只要具备一点缺乏耐性的品质, 他就会陷入一种被折磨的状态. 即使这件事情本身有趣, 频繁重复也会降低它的趣味性.
幸运的是, 我们拥有许多方法可以代替人工来执行这些需要重复运行的烦琐事情. 对烦琐工作的自动化实现保持的敏感度越高, 产生消除这种重复的想法就越快.
本周四, 上级让我干这样的活儿: 在一台正在开发阶段的具有变倍和变焦功能的网络摄像机测出变倍范围内的几组物距的最清晰点的zoom-focus坐标值, 我需要在不同的倍率和不同的物距下调整控制感光传感器和镜头之间距离的电机, 使感光传感器落在焦点的位置, 也就是找到成像最清晰时的focus电机转动的位置, 并把这个位置的电机步数记录下来. 从数据量来开, 我要重复这个找清晰点的过程几百次, 而完成一次过程的周期大概两三分钟, 试验完全部数据就要上千分钟了.
* 问题是什么: 忽略所有的光学和相机的知识, 可以这样描述这个搜索指定倍率指定物距的焦点工作过程: 变量z和变量u会确定一条曲线f[z,u](x), 找到f[z, u](x)在区间(x0, x1)的最值点(x, f[z, u](x)), 把这时的z, u, x保存下来. 对我来说, z对应了反映摄像机镜头倍率的电机步数, u对应了物距, x对应了反映感光传感器到镜头距离的电机步数. 最值点时的x值就是这个倍率及物距下成像最清晰时的focus电机步数值.
* 如何解决: 要完成这个工作, 需要实现一些最基本的操作:
- 为z赋值, 也就是控制zoom电机转动到指定步数;
- 为u赋值, 也就是移动镜头到指定物距的位置;
- 为x赋值, 也就是控制focus电机转动到指定步数;
- f[z, u](x), 也就是每个状态反映对焦清晰度的函数, 输入参数为zoom电机步数, 物距, focus电机步数, 输出反映这个状态时的对焦清晰度的值;
我已经可以用这些基本操作了. 这样,我原先说的需要上千分钟时间完成工作的流程可以这样干:
- 我(人)从文件读取还没有试验过的zoom步数, 然后将zoom电机转动到这个位置, 如果全都试验过了工作就结束;
- 我(人)从文件读取还没有试验过的物距, 然后自己搬动网络摄像机到这个位置, 如果这个zoom电机步数对应的所有物距都试验过了就跳转到1;
- 把focus电机转动到最小的位置, 从这个位置开始, 直到转动focus电机到最大的位置, 每转动一次步数就调用查询清晰度的函数, 并把返回最大清晰度值时的focus电机步数值以及对应的zoom电机步数值和物距保存起来;
- 跳转到1.
* 改进, 让计算机代替人来干活
这个流程我自己重复了几十遍之后, 才迟迟的领悟到没有一个真正的程序员会愚蠢到自己来充当解释器来执行这些乏味而且循环会很久才跳出的流程. 这个网络摄像机跑的是linux系统, unix的天然胶水可以帮我把这些东西组合起来, 除了搬动摄像机这个动作需要我来完成外. 我不需要写一个完善的对焦算法, 只需要再写一个简单的查找指定区间函数最值的函数: 它传入的参数是区间, 还有一个函数指针.基本的函数写完之后, 就需要将它们用 "胶水" 组合起来了. 我写了个shell脚本, 它用sed和cut来读入文件指定的行和列, 它们对应zoom电机步数和focus电机需要移动的范围. 然后以这些参数调用我封装的一个可执行文件, 它是这样运行的: 将电机移动到第一个参数的位置, 再以第2, 3个参数调用查找指定区间函数最值的函数, 然后输出成像最清晰时的focus电机位置. (关键代码我放在github上了)
之后我除了敲入要试验的数据和搬动摄像机之后, 剩下的就坐在屏幕前等着结果了. 它执行这些愚蠢而繁琐的工作效率远远在我之上.