想象一下,传送带上的产品呼啸而过,流水线上的机械臂精确抓取,风力发电机叶片高速旋转——这些场景的核心秘密,在于可靠的速度测量。光电开关,凭借其非接触、响应快、寿命长的特性,成为了工程师手中的精密测速利器。
在自动化生产线、传送带控制、旋转机械监测等众多领域,精确的速度测量是实现高效运行、安全保障和质量控制的关键。光电开关以其非接触、高响应速度、抗干扰能力强等显著优势,成为了速度测量中极其重要的传感器。
光电开关测速的核心原理在于利用物体通过时光信号变化产生脉冲。
物体经过光电开关(特别是槽型/对射式或反射式有效检测区域)时,会遮挡或反射光信号。这个过程会在光电开关的输出端产生一个清晰的电信号脉冲。测量程序的核心任务就是精确捕捉这些脉冲信号的时间点。
最核心的速度计算方法基于物理学基本公式:速度 (v) = 距离 (d) / 时间差 (Δt)。这里的d指两个光电开关安装位置之间的距离,或者利用单个开关配合特定标记(如电机轴上的反光片、输送带上的特定凸起)形成的等效距离。Δt则是两个连续脉冲(两个开关相继触发或同一个开关两次检测到标记)之间的时间间隔。
选型前哨:为速度测量选出“火眼金睛”
槽型/对射式: 发射器与接收器分离。物体穿过“光槽”阻断光束,产生信号变化。结构稳定,抗环境光干扰强,适用于精确位置检测。
反射式(漫反射): 发射器接收器一体。物体将光漫反射回接收器。安装简便,适用于检测多样化的物体,但易受物体表面颜色和环境光影响。
反射式(回归反射/反光板式): 发射器对准专用反光板,物体阻断反射光束。检测距离较远,光路调节要求低于对射式,抗干扰能力优于漫反射。
响应时间: 单位通常是微秒 (μs) 甚至纳秒 (ns)。响应时间直接影响它们能检测到的最小间隔时间,从而决定了它们能捕捉的最大速度。
检测距离: 根据实际安装空间和待测物的大小选择合适的检测距离。需考虑一定的余量。
输出类型: NPN或PNP晶体管输出(开关量信号),可直接接入控制器(如PLC、单片机、Arduino)的I/O口。
环境因素: 考虑粉尘、水汽、温度、振动、强光等现场环境,选择相应防护等级(IP等级)的产品。
灵魂所在:时间差的精确捕捉
精确测量脉冲间的时间差 (Δt) 是整个程序成败的核心。
- 外部事件捕获(捕获中断): 这是最精确的方法之一。控制器的特定输入引脚(通常标记为ICP或类似功能)能在输入信号边沿(如上升沿)发生的瞬间,“冻结”当前系统定时器的计数值。通过记录两个连续脉冲捕获点的定时器值,其差值乘以定时器计数周期即为精确的Δt。
- 外部中断: 当光电开关输出信号发生指定的跳变沿(如上升沿)时,触发控制器中断。在中断服务程序内部读取系统时间戳(如
micros())。连续两次时间戳的差值即为Δt。此方法依赖于中断响应速度和中断服务程序的执行时间。 - 计数器模式: 将光电开关的脉冲信号接入计数器的外部计数源引脚。结合定时器中断,在固定时间间隔(如1秒)内读取计数器的累计值。速度 ≈ (计数值 * 标记距离) / 时间间隔。此方法更适用于测量平均速度或转速。
代码实战:Arduino测速框架解析
const int photoSensorPin = 2; // 接光电开关输出(如棕色线OUT)
volatile unsigned long timePrev = 0; // 上一次触发时间(毫秒或微秒)
volatile unsigned long deltaT = 0; // 两次触发时间差
const float distance = 0.05; // 两个开关间距或等效距离(米),如0.05米=5cm
void setup() {
Serial.begin(9600);
pinMode(photoSensorPin, INPUT);
// 配置中断:当引脚2(INT0)检测到信号上升沿时,触发函数onSensorTrigger
attachInterrupt(digitalPinToInterrupt(photoSensorPin), onSensorTrigger, RISING);
}
void loop() {
if (deltaT > 0) { // 确保有有效的Δt计算过
// 计算速度:速度 = 距离 / 时间差 (单位:米/秒)
float speedMperS = distance / (deltaT / 1000000.0); // 若deltaT单位为微秒需转换
// 可选:转换为其他单位,如米/分、公里/小时等
Serial.print("当前速度: ");
Serial.print(speedMperS, 2);
Serial.println(" m/s");
// 重置时间差,等待下一次有效计算(谨慎使用,根据实际逻辑调整)
// deltaT = 0;
}
// 主循环可处理其他任务...
delay(100); // 防止串口输出刷屏过快
}
// 中断服务函数(ISR) - 必须简短快速!
void onSensorTrigger() {
unsigned long currentTime = micros(); // 获取当前微秒级时间戳
if (timePrev > 0) { // 确保不是第一次触发
deltaT = currentTime - timePrev; // 计算与前次触发的时间差(微秒)
}
timePrev = currentTime; // 更新前次触发时间
}
代码说明关键点:
- 中断配置 (
attachInterrupt): 指定哪个引脚(INT0/INT1对应特定引脚)响应哪种信号边沿(RISING上升沿)以及触发哪个函数。 volatile变量:timePrev和deltaT在中断服务函数中被修改,在主循环中被读取。volatile关键字告诉编译器不要优化掉这些变量的读写操作,确保数据在主程序与中断间同步的正确性。- 中断服务函数 (
ISR):onSensorTrigger必须极其精简快速。避免使用delay()、耗时计算、或调用可能被阻塞的函数(如Serial.print)。理想情况下只做时间戳读取、简单计算和变量更新。 - 时间戳获取:
micros():返回自Arduino启动以来的微秒数(精度≈4微秒)。适用于速度较高、需要微秒级别精度的场景。millis():返回毫秒数。适用于速度较低的场景。
- 速度计算:
速度 = distance / Δt。注意单位一致性(距离单位是米,时间单位是秒)。 - 输出处理: 在主循环 (
loop()) 中输出计算结果,避免在ISR中做耗时操作。使用串口(Serial) 输出是常见做法。
常见问题:稳定性与准确性进阶
- 信号抖动与噪声: 光电开关输出信号可能在触发点附近出现微小、快速的抖动。**在硬件上



















