如何通过高速相机视频测量物体振动?基于帧位移的实现方案
视频振动测量技术方案(Python实现)
一、从帧间位移数据计算振动频率
你已经通过2D FFT得到了帧间的x/y轴位移序列,接下来通过以下步骤将位移数据转化为振动频率:
1. 构建位移-时间序列
首先明确视频的实际帧率fps,每帧的时间间隔为1/fps秒。假设你有N个位移数据(对应N+1帧视频),则时间戳数组可通过以下代码生成:
import numpy as np fps = 30 # 替换为你的视频实际帧率 displacement_y = [y1, y2, ..., yN] # 你的y轴位移数组 timestamps = np.arange(len(displacement_y)) / fps
2. 位移信号预处理
- 去趋势:移除位移序列中的线性漂移(比如相机缓慢移动导致的整体偏移),避免干扰频率分析:
from scipy.signal import detrend detrended_y = detrend(displacement_y) - 低通滤波:过滤高频噪声,保留振动信号:
from scipy.signal import butter, filtfilt def butter_lowpass(cutoff, fs, order=5): nyq = 0.5 * fs normal_cutoff = cutoff / nyq b, a = butter(order, normal_cutoff, btype='low', analog=False) return b, a cutoff_freq = 10 # 替换为你预期的振动最高频率 b, a = butter_lowpass(cutoff_freq, fps) filtered_y = filtfilt(b, a, detrended_y)
3. 频率提取(两种核心方法)
方法1:FFT功率谱分析
对预处理后的位移序列做FFT,找到功率谱峰值对应的频率:
from scipy.fft import fft, fftfreq n = len(filtered_y) # 计算FFT和频率轴 yf = fft(filtered_y) xf = fftfreq(n, 1/fps)[:n//2] # 只取正频率部分 power_spectrum = 2.0/n * np.abs(yf[:n//2]) # 找到峰值对应的频率(排除0Hz直流分量) peak_idx = np.argmax(power_spectrum[1:]) + 1 vibration_freq = xf[peak_idx] print(f"振动频率:{vibration_freq:.2f} Hz")
方法2:自相关法(噪声鲁棒性更强)
通过计算位移序列的自相关,找到信号周期,再转换为频率:
corr = np.correlate(filtered_y, filtered_y, mode='full') corr = corr[len(corr)//2:] # 取自相关的正延迟部分 # 找到第一个峰值位置(对应周期) peak_pos = np.argmax(corr[1:]) + 1 period = peak_pos / fps vibration_freq = 1 / period
二、替代的视频振动测量方法
除了2D FFT帧间位移检测,还有以下更实用的方法:
1. 光流法(适合局部区域振动)
通过计算像素的光流场,获取感兴趣区域(ROI)的平均位移,鲁棒性优于直接帧差FFT:
import cv2 cap = cv2.VideoCapture("your_video.mp4") ret, prev_frame = cap.read() prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY) fps = cap.get(cv2.CAP_PROP_FPS) displacements = [] # 定义振动物体的ROI(x1,y1,x2,y2) roi = (100, 100, 300, 300) while ret: ret, curr_frame = cap.read() if not ret: break curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY) # 计算稠密光流 flow = cv2.calcOpticalFlowFarneback(prev_gray, curr_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0) # 提取ROI内的光流并计算平均位移 x1, y1, x2, y2 = roi roi_flow = flow[y1:y2, x1:x2] avg_x = np.mean(roi_flow[..., 0]) avg_y = np.mean(roi_flow[..., 1]) displacements.append((avg_x, avg_y)) prev_gray = curr_gray cap.release()
2. 特征点跟踪法(适合有明显特征的物体)
检测物体上的特征点,跟踪其帧间运动,过滤无效跟踪点后取平均位移:
cap = cv2.VideoCapture("your_video.mp4") ret, prev_frame = cap.read() prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY) # 检测ORB特征点 orb = cv2.ORB_create(500) kp_prev, des_prev = orb.detectAndCompute(prev_gray, None) displacements = [] fps = cap.get(cv2.CAP_PROP_FPS) while ret: ret, curr_frame = cap.read() if not ret: break curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY) kp_curr, des_curr = orb.detectAndCompute(curr_gray, None) # 匹配特征点 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des_prev, des_curr) # 计算平均位移 dx_list, dy_list = [], [] for match in matches: prev_pt = kp_prev[match.queryIdx].pt curr_pt = kp_curr[match.trainIdx].pt dx_list.append(curr_pt[0] - prev_pt[0]) dy_list.append(curr_pt[1] - prev_pt[1]) if dx_list: avg_dx = np.mean(dx_list) avg_dy = np.mean(dy_list) displacements.append((avg_dx, avg_dy)) kp_prev, des_prev = kp_curr, des_curr cap.release()
3. 像素强度时域分析法(适合亚像素级微小振动)
对于振动幅度极小的场景,直接分析ROI内像素平均强度的时域变化,通过FFT提取频率:
cap = cv2.VideoCapture("your_video.mp4") fps = cap.get(cv2.CAP_PROP_FPS) roi = (100, 100, 300, 300) intensity_list = [] while ret: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) x1, y1, x2, y2 = roi roi_intensity = np.mean(gray[y1:y2, x1:x2]) intensity_list.append(roi_intensity) cap.release() # 对强度序列做FFT提取频率(步骤同位移序列的FFT分析)
三、关键注意事项
- 帧率校准:必须使用视频的实际帧率(通过
cap.get(cv2.CAP_PROP_FPS)获取),否则频率计算会出现偏差;高速相机需确认硬件帧率参数准确。 - 物理单位转换:若需要实际长度的位移,需用已知尺寸的标定物,建立像素与实际长度的映射关系。
- 噪声抑制:环境光变化、相机抖动会干扰结果,可通过缩小ROI、增加滤波强度、多帧平均等方式降低噪声影响。
内容的提问来源于stack exchange,提问作者tryingmybest
相关产品推荐
相关产品推荐

