You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python OpenCV光流中设置阈值检测电机线圈振动

基于光流的电机线圈振动状态区分方案

核心思路

光流的**幅值(mag)**直接对应运动强度,是区分振动状态的核心指标——光流可视化的颜色变化本质是幅值/角度的变化,因此优先从幅值入手设计阈值逻辑是最高效的方案。


具体实现方案

1. 基于幅值统计量的固定阈值划分

直接计算裁剪区域内光流幅值的统计特征,通过设定固定阈值区分三种状态:

  • 统计整区域的平均幅值:平稳运转时平均幅值极低,低振动时小幅上升,高振动时显著升高
  • 统计幅值超过基准值的像素占比:振动时会有大量像素的幅值超过平稳状态的最大值

修改原代码,添加状态判断逻辑:

import cv2
import numpy as np

cap = cv2.VideoCapture("Coil.mp4")
ret, frame1 = cap.read()
frame1 = frame1[284: 383, 498:516]
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255

# 阈值需根据你的平稳状态视频采集数据调整,以下为示例值
LOW_MAG_THRESH = 10          # 平均幅值低于此值为无振动
HIGH_MAG_THRESH = 30         # 平均幅值高于此值为高振动
HIGH_PIXEL_RATIO_THRESH = 0.2 # 幅值>5的像素占比超20%判定为振动

while cap.isOpened():
    ret, frame2 = cap.read()
    if not ret:
        break
    frame2 = frame2[284: 383, 498:516]
    next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    flow = cv2.calcOpticalFlowFarneback(prvs, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    hsv[..., 0] = ang * 180 / np.pi / 2
    hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

    # 计算幅值统计特征
    avg_mag = np.mean(mag)
    high_mag_pixel_ratio = np.sum(mag > 5) / (mag.shape[0] * mag.shape[1])

    # 状态判定
    if avg_mag < LOW_MAG_THRESH and high_mag_pixel_ratio < HIGH_PIXEL_RATIO_THRESH:
        status = "无振动"
    elif avg_mag < HIGH_MAG_THRESH:
        status = "低振动"
    else:
        status = "高振动"

    # 在画面显示状态
    cv2.putText(rgb, f"状态: {status}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
    cv2.imshow('Optical Flow', rgb)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break
    prvs = next_frame

cap.release()
cv2.destroyAllWindows()

2. 基于光流可视化图的颜色特征阈值

如果你想基于可视化的RGB/HSV颜色判断,可以提取**HSV通道中的V通道(对应幅值归一化值)**的统计值:

  • 平稳状态下V通道整体偏暗,像素值集中在0-50区间
  • 振动时V通道会出现大量高亮度像素

替换状态判定逻辑即可:

# 从hsv的V通道提取特征
v_channel = hsv[..., 2]
avg_v = np.mean(v_channel)
bright_pixel_ratio = np.sum(v_channel > 100) / (v_channel.shape[0] * v_channel.shape[1])

# 用V通道统计值判定状态
if avg_v < 30 and bright_pixel_ratio < 0.1:
    status = "无振动"
elif avg_v < 100:
    status = "低振动"
else:
    status = "高振动"

3. 动态自适应阈值(应对环境波动)

如果电机运转时存在轻微背景波动或环境光变化,可采用滑动窗口平均动态调整阈值:

  • 维护最近N帧的平均幅值队列,当当前帧幅值超过队列均值的X倍时判定为振动
  • 避免固定阈值带来的误判

示例代码片段:

from collections import deque

# 初始化滑动窗口,保存最近20帧的平均幅值
window = deque(maxlen=20)
# 先采集10帧平稳状态数据填充窗口
for _ in range(10):
    ret, frame2 = cap.read()
    if not ret:
        break
    frame2 = frame2[284: 383, 498:516]
    next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    flow = cv2.calcOpticalFlowFarneback(prvs, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    mag, _ = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    window.append(np.mean(mag))
    prvs = next_frame

# 后续循环中的动态判定逻辑
avg_mag = np.mean(mag)
window.append(avg_mag)
window_mean = np.mean(window)
window_std = np.std(window)

if avg_mag < window_mean + window_std:
    status = "无振动"
elif avg_mag < window_mean + 3*window_std:
    status = "低振动"
else:
    status = "高振动"

调优建议

  • 先采集一段平稳运转的视频,统计幅值的均值、最大值,以此作为阈值的基准
  • 调整cv2.calcOpticalFlowFarneback的参数(如pyr_scale、winsize),可让光流更敏感地捕捉微小振动
  • 高振动状态可结合幅值的标准差判断:振动越剧烈,幅值的离散程度越高

内容的提问来源于stack exchange,提问作者S Andrew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:42:52