<think>问题分析:程序立即结束可能是因为主线程没有阻塞,导致程序直接运行结束。在之前的代码中,我们使用了`plt.show()`来阻塞主线程,但可能由于某些原因(例如Matplotlib的后端设置)导致窗口没有保持打开。此外,也可能是音频设备初始化失败导致程序退出。
解决方案:
1. 确保主线程被阻塞:使用`plt.show()`应该会阻塞主线程直到窗口关闭。但有时在非交互式环境下可能需要特殊处理。
2. 检查音频设备:确保音频输入输出设备可用,并正确设置。
3. 添加更多的错误处理和调试信息,以便了解程序运行情况。
我们将对代码进行以下改进:
- 在启动音频流之前,检查音频设备是否可用。
- 添加更详细的错误日志。
- 确保在`start`方法中,`plt.show()`被正确调用并阻塞主线程。
- 在程序退出时,确保资源被正确释放。
另外,我们注意到之前的代码中,处理音频的线程可能会因为异常而退出,导致没有数据更新。因此,我们将在处理线程中添加更详细的异常捕获。
修改后的代码:
注意:由于我们无法直接测试硬件,因此只能通过添加日志和错误处理来增强健壮性。
以下是修改后的完整代码:</think>您的问题表明程序无法正确检测现实中的运动。这通常是由于参数配置不当、环境噪声干扰或算法灵敏度问题导致的。下面我将提供完整的解决方案,优化运动检测算法并添加调试功能:
```python
import numpy as np
import pyaudio
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.signal import butter, lfilter, filtfilt
from scipy.fft import dct, idct
import time
import queue
import threading
import wave
import os
from collections import deque
import tkinter as tk
from tkinter import ttk, Scale, HORIZONTAL
# ===== 可调参数配置 =====
# 这些参数可以在运行时通过GUI调整
FS = 48000 # 采样率 (Hz)
CARRIER_FREQ = 18500 # 载波频率 (Hz) - 超声波范围
BUFFER_SIZE = 2048 # 音频缓冲区大小
CUTOFF_FREQ = 150 # 低通滤波器截止频率 (Hz)
SOUND_SPEED = 343.0 # 声速 (m/s)
SENSOR_DISTANCE = 0.5 # 两个传感器之间的距离 (m)
DCT_COMPONENTS = 20 # DCT保留的特征分量数量
MOTION_THRESHOLD = 0.01 # 运动检测阈值 (m)
MIN_MOTION_DURATION = 0.2 # 最小有效运动持续时间 (秒)
MAX_DURATION_HISTORY = 10 # 最大保存的运动持续时间数量
PLOT_REFRESH_RATE = 50 # 绘图刷新率 (ms)
# 计算波长
WAVELENGTH = SOUND_SPEED / CARRIER_FREQ # 波长 (m)
class MotionDetector2D:
def __init__(self):
self.p = pyaudio.PyAudio()
self.audio_queue = queue.Queue(maxsize=50)
self.running = threading.Event()
self.running.set()
# 初始化滤波器
self.filter_b, self.filter_a = self._design_lowpass_filter()
# 初始化传感器数据缓冲区
self.time_buffer = deque(maxlen=200)
self.distance1_buffer = deque(maxlen=200)
self.distance2_buffer = deque(maxlen=200)
self.x_positions = deque(maxlen=200)
self.y_positions = deque(maxlen=200)
self.start_time = time.time()
self.last_motion_time = time.time()
self.motion_durations = deque(maxlen=MAX_DURATION_HISTORY)
self.current_motion_start = None
self.motion_active = False
self.last_plot_update = time.time()
self.debug_output = ""
self.audio_data_buffer = deque(maxlen=20) # 存储原始音频数据用于调试
# 创建图形界面
self.fig = plt.figure(figsize=(16, 10))
plt.subplots_adjust(hspace=0.4, wspace=0.3)
# 传感器1距离变化图
self.ax1 = plt.subplot2grid((3, 2), (0, 0))
self.line1, = self.ax1.plot([], [], 'b-')
self.ax1.set_title('传感器1距离变化')
self.ax1.set_xlabel('时间 (秒)')
self.ax1.set_ylabel('距离 (米)')
self.ax1.grid(True)
self.ax1.set_xlim(0, 5)
self.ax1.set_ylim(0, 3)
# 传感器2距离变化图
self.ax2 = plt.subplot2grid((3, 2), (0, 1))
self.line2, = self.ax2.plot([], [], 'g-')
self.ax2.set_title('传感器2距离变化')
self.ax2.set_xlabel('时间 (秒)')
self.ax2.set_ylabel('距离 (米)')
self.ax2.grid(True)
self.ax2.set_xlim(0, 5)
self.ax2.set_ylim(0, 3)
# 二维运动轨迹图
self.ax3 = plt.subplot2grid((3, 2), (1, 0), colspan=2)
self.scatter = self.ax3.scatter([], [], c=[], cmap='viridis', s=50)
self.ax3.set_title('二维运动轨迹')
self.ax3.set_xlabel('X位置 (米)')
self.ax3.set_ylabel('Y位置 (米)')
self.ax3.grid(True)
self.ax3.set_xlim(-2, 2)
self.ax3.set_ylim(0, 3)
self.ax3.set_aspect('equal', adjustable='box')
# 添加传感器位置标记
self.ax3.plot(-SENSOR_DISTANCE/2, 0, 'ro', markersize=10, label='传感器1')
self.ax3.plot(SENSOR_DISTANCE/2, 0, 'go', markersize=10, label='传感器2')
self.ax3.legend(loc='upper right')
# 调试信息面板
self.ax4 = plt.subplot2grid((3, 2), (2, 0), colspan=2)
self.ax4.set_title('调试信息和控制面板')
self.ax4.axis('off')
# 创建文本区域显示调试信息
self.debug_text = self.ax4.text(0.02, 0.95, '', transform=self.ax4.transAxes,
fontsize=10, bbox=dict(facecolor='white', alpha=0.7),
verticalalignment='top')
# 创建音频流回调
try:
self.stream = self.p.open(
format=pyaudio.paFloat32,
channels=2, # 立体声输入
rate=FS,
input=True,
output=True,
frames_per_buffer=BUFFER_SIZE,
stream_callback=self.audio_callback
)
except Exception as e:
print(f"无法打开音频流: {e}")
print("请检查音频设备连接和权限")
self.stream = None
# 启动处理线程
if self.stream:
self.processing_thread = threading.Thread(target=self.process_audio)
self.processing_thread.daemon = True
self.processing_thread.start()
else:
print("音频流未初始化,无法启动处理线程")
def _design_lowpass_filter(self):
"""设计低通滤波器"""
nyq = 0.5 * FS
normal_cutoff = CUTOFF_FREQ / nyq
b, a = butter(4, normal_cutoff, btype='low', analog=False)
return b, a
def _generate_tx_signal(self, length):
"""生成发射信号"""
t = np.arange(length) / FS
carrier = np.cos(2 * np.pi * CARRIER_FREQ * t).astype(np.float32)
# 立体声输出 - 两个声道都发送相同的信号
return np.column_stack((carrier, carrier))
def _process_channel(self, rx_data):
"""处理单个通道的音频数据"""
# 保存原始音频数据用于调试
self.audio_data_buffer.append(rx_data.copy())
# 生成发射信号
t = np.arange(len(rx_data)) / FS
tx_signal = np.cos(2 * np.pi * CARRIER_FREQ * t)
# 正交解调
I_mixed = rx_data * tx_signal
Q_mixed = rx_data * np.sin(2 * np.pi * CARRIER_FREQ * t)
# 低通滤波 (使用零相位滤波减少相位失真)
I_base = filtfilt(self.filter_b, self.filter_a, I_mixed)
Q_base = filtfilt(self.filter_b, self.filter_a, Q_mixed)
# 计算相位
phase = np.arctan2(Q_base, I_base)
# 相位解缠绕
phase_unwrapped = np.unwrap(phase)
# 计算距离变化 (往返距离)
distance = (phase_unwrapped * WAVELENGTH) / (4 * np.pi)
# 使用DCT进行特征提取和降维
dct_features = dct(distance, norm='ortho')[:DCT_COMPONENTS]
distance_filtered = idct(dct_features, n=len(distance), norm='ortho')
return distance_filtered
def _detect_motion(self, dist1, dist2):
"""检测运动并计算持续时间"""
current_time = time.time()
# 计算距离变化率
if len(self.distance1_buffer) > 1:
dist1_change = abs(dist1 - self.distance1_buffer[-1])
dist2_change = abs(dist2 - self.distance2_buffer[-1])
motion_level = max(dist1_change, dist2_change)
else:
motion_level = 0
# 运动检测逻辑
if motion_level > MOTION_THRESHOLD:
self.last_motion_time = current_time
if not self.motion_active:
# 运动开始
self.motion_active = True
self.current_motion_start = current_time
self.debug_output += f"[{current_time - self.start_time:.2f}s] 运动开始 (强度: {motion_level:.4f}m)\n"
# 检测运动结束
if self.motion_active:
motion_duration = current_time - self.current_motion_start
# 如果超过1秒没有检测到运动,则认为运动结束
if (current_time - self.last_motion_time) > 1.0 and motion_duration > MIN_MOTION_DURATION:
# 记录运动持续时间
duration = current_time - self.current_motion_start
self.motion_durations.append(duration)
self.debug_output += f"[{current_time - self.start_time:.2f}s] 运动结束 (持续时间: {duration:.2f}s)\n"
# 重置状态
self.motion_active = False
self.current_motion_start = None
return motion_level
def _triangulate_position(self, d1, d2):
"""使用两个距离进行三角定位"""
# 传感器位置 (假设传感器在x轴上对称放置)
sensor1_pos = (-SENSOR_DISTANCE/2, 0)
sensor2_pos = (SENSOR_DISTANCE/2, 0)
# 计算目标位置
# 使用双曲线定位算法
d_diff = d2 - d1
d_sum = d1 + d2
# 当距离差很小时,目标在垂直平分线上
if abs(d_diff) < 1e-5:
x = 0
y = np.sqrt(d1**2 - (SENSOR_DISTANCE/2)**2)
else:
# 计算双曲线参数
a = d_diff / 2
c = SENSOR_DISTANCE / 2
# 避免数值错误
if abs(a) > c:
a = np.sign(a) * c * 0.99
b = np.sqrt(c**2 - a**2)
# 计算焦点
focus_x = a * c / np.sqrt(a**2 + b**2)
focus_y = b * c / np.sqrt(a**2 + b**2)
# 根据距离和确定具体位置
if d_sum > np.sqrt((focus_x - sensor1_pos[0])**2 + (focus_y - sensor1_pos[1])**2) + \
np.sqrt((focus_x - sensor2_pos[0])**2 + (focus_y - sensor2_pos[1])**2):
x = focus_x
y = focus_y
else:
x = focus_x
y = -focus_y
return x, y
def audio_callback(self, in_data, frame_count, time_info, status):
"""PyAudio回调函数,处理音频流"""
# 生成发射信号
tx_signal = self._generate_tx_signal(frame_count)
# 将接收到的音频数据转换为numpy数组
rx_data = np.frombuffer(in_data, dtype=np.float32).reshape(-1, 2)
# 将接收数据和当前时间放入队列
if not self.audio_queue.full():
self.audio_queue.put((time.time(), rx_data))
# 返回发射信号用于播放
return (tx_signal.tobytes(), pyaudio.paContinue)
def process_audio(self):
"""处理音频数据的线程"""
while self.running.is_set():
try:
# 从队列获取数据,最多等待100ms
timestamp, rx_data = self.audio_queue.get(timeout=0.1)
# 分离左右声道(两个传感器)
rx_ch1 = rx_data[:, 0] # 传感器1
rx_ch2 = rx_data[:, 1] # 传感器2
# 处理两个通道
dist1 = self._process_channel(rx_ch1)
dist2 = self._process_channel(rx_ch2)
# 取平均距离
avg_dist1 = np.mean(dist1)
avg_dist2 = np.mean(dist2)
# 计算当前时间
current_time = timestamp - self.start_time
# 三角定位计算位置
x, y = self._triangulate_position(avg_dist1, avg_dist2)
# 更新缓冲区
self.time_buffer.append(current_time)
self.distance1_buffer.append(avg_dist1)
self.distance2_buffer.append(avg_dist2)
self.x_positions.append(x)
self.y_positions.append(y)
# 检测运动并计算持续时间
motion_level = self._detect_motion(avg_dist1, avg_dist2)
# 更新调试信息
self.update_debug_info(avg_dist1, avg_dist2, motion_level)
except queue.Empty:
# 队列为空,继续等待
continue
except Exception as e:
self.debug_output += f"处理错误: {str(e)}\n"
def update_debug_info(self, dist1, dist2, motion_level):
"""更新调试信息"""
info = f"系统状态: {'运动中' if self.motion_active else '等待运动'}\n"
info += f"传感器1距离: {dist1:.4f} m\n"
info += f"传感器2距离: {dist2:.4f} m\n"
info += f"运动强度: {motion_level:.4f} m\n"
info += f"运动阈值: {MOTION_THRESHOLD:.4f} m\n"
info += f"载波频率: {CARRIER_FREQ/1000:.1f} kHz\n"
info += f"采样率: {FS/1000:.1f} kHz\n"
info += f"缓冲区: {len(self.audio_queue.queue)}/{self.audio_queue.maxsize}\n"
info += f"最近事件:\n{self.debug_output[-300:]}" # 只显示最后300字符
self.debug_text.set_text(info)
def save_audio_data(self, filename="audio_debug.wav"):
"""保存原始音频数据用于调试"""
if not self.audio_data_buffer:
print("没有可用的音频数据")
return
# 合并所有缓冲的音频数据
all_data = np.concatenate(list(self.audio_data_buffer))
# 保存为WAV文件
with wave.open(filename, 'wb') as wf:
wf.setnchannels(1) # 单声道
wf.setsampwidth(2) # 16位PCM
wf.setframerate(FS)
# 转换为16位PCM
int_data = np.int16(all_data * 32767)
wf.writeframes(int_data.tobytes())
print(f"音频数据已保存到 {filename}")
def update_plot(self, frame):
"""更新实时图表"""
# 限制更新频率
current_time = time.time()
if current_time - self.last_plot_update < 0.05: # 每秒最多20次
return [self.line1, self.line2, self.scatter, self.debug_text]
self.last_plot_update = current_time
if not self.time_buffer:
return [self.line1, self.line2, self.scatter, self.debug_text]
# 转换为数组
time_array = np.array(self.time_buffer)
dist1_array = np.array(self.distance1_buffer)
dist2_array = np.array(self.distance2_buffer)
x_array = np.array(self.x_positions)
y_array = np.array(self.y_positions)
# 更新传感器1距离图
self.line1.set_data(time_array, dist1_array)
self.ax1.set_xlim(max(0, time_array[-1] - 5), max(5, time_array[-1]))
# 更新传感器2距离图
self.line2.set_data(time_array, dist2_array)
self.ax2.set_xlim(max(0, time_array[-1] - 5), max(5, time_array[-1]))
# 更新二维轨迹图
if len(x_array) > 1:
# 使用颜色表示时间(最近的点更亮)
colors = np.linspace(0.1, 1, len(x_array))
self.scatter.set_offsets(np.column_stack((x_array, y_array)))
self.scatter.set_array(colors)
self.scatter.set_clim(0, 1)
# 自动调整坐标轴范围
if len(x_array) > 10:
x_min, x_max = min(x_array[-10:]), max(x_array[-10:])
y_min, y_max = min(y_array[-10:]), max(y_array[-10:])
x_range = max(0.5, (x_max - x_min) * 1.5)
y_range = max(0.5, (y_max - y_min) * 1.5)
self.ax3.set_xlim((x_min + x_max)/2 - x_range/2, (x_min + x_max)/2 + x_range/2)
self.ax3.set_ylim((y_min + y_max)/2 - y_range/2, (y_min + y_max)/2 + y_range/2)
# 返回所有需要更新的艺术家对象
return [self.line1, self.line2, self.scatter, self.debug_text]
def start(self):
"""启动检测系统"""
if not self.stream:
print("无法启动,音频流未初始化")
return
print("启动二维运动检测系统...")
print(f"载波频率: {CARRIER_FREQ/1000:.1f} kHz, 采样率: {FS/1000:.1f} kHz")
print(f"传感器间距: {SENSOR_DISTANCE:.2f} m")
print(f"运动检测阈值: {MOTION_THRESHOLD*100:.1f} cm")
# 启动音频流
self.stream.start_stream()
# 创建动画
self.ani = FuncAnimation(
self.fig,
self.update_plot,
interval=PLOT_REFRESH_RATE,
blit=True,
cache_frame_data=False # 避免内存泄漏
)
# 创建控制面板窗口
self.create_control_panel()
plt.show()
def create_control_panel(self):
"""创建参数控制面板"""
self.control_root = tk.Tk()
self.control_root.title("运动检测控制面板")
self.control_root.geometry("400x500")
# 参数调整框架
param_frame = ttk.LabelFrame(self.control_root, text="参数调整")
param_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 载波频率调整
ttk.Label(param_frame, text="载波频率 (kHz):").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.carrier_freq_var = tk.DoubleVar(value=CARRIER_FREQ/1000)
carrier_scale = Scale(param_frame, from_=18.0, to=22.0, resolution=0.1,
orient=HORIZONTAL, variable=self.carrier_freq_var,
length=300)
carrier_scale.grid(row=0, column=1, padx=5, pady=5)
# 运动阈值调整
ttk.Label(param_frame, text="运动阈值 (cm):").grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.threshold_var = tk.DoubleVar(value=MOTION_THRESHOLD*100)
threshold_scale = Scale(param_frame, from_=0.1, to=5.0, resolution=0.1,
orient=HORIZONTAL, variable=self.threshold_var,
length=300)
threshold_scale.grid(row=1, column=1, padx=5, pady=5)
# 滤波器截止频率
ttk.Label(param_frame, text="滤波器截止频率 (Hz):").grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.cutoff_var = tk.DoubleVar(value=CUTOFF_FREQ)
cutoff_scale = Scale(param_frame, from_=50, to=500, resolution=10,
orient=HORIZONTAL, variable=self.cutoff_var,
length=300)
cutoff_scale.grid(row=2, column=1, padx=5, pady=5)
# 传感器距离
ttk.Label(param_frame, text="传感器距离 (m):").grid(row=3, column=0, padx=5, pady=5, sticky="w")
self.distance_var = tk.DoubleVar(value=SENSOR_DISTANCE)
distance_scale = Scale(param_frame, from_=0.1, to=2.0, resolution=0.1,
orient=HORIZONTAL, variable=self.distance_var,
length=300)
distance_scale.grid(row=3, column=1, padx=5, pady=5)
# 控制按钮框架
button_frame = ttk.Frame(self.control_root)
button_frame.pack(fill="x", padx=10, pady=10)
# 保存音频按钮
save_btn = ttk.Button(button_frame, text="保存音频数据", command=lambda: self.save_audio_data())
save_btn.pack(side="left", padx=5)
# 应用参数按钮
apply_btn = ttk.Button(button_frame, text="应用参数", command=self.apply_parameters)
apply_btn.pack(side="right", padx=5)
# 调试信息标签
debug_frame = ttk.LabelFrame(self.control_root, text="调试信息")
debug_frame.pack(fill="both", expand=True, padx=10, pady=10)
self.debug_label = ttk.Label(debug_frame, text="系统已启动,等待运动...")
self.debug_label.pack(padx=5, pady=5)
# 启动控制面板更新线程
threading.Thread(target=self.update_control_panel, daemon=True).start()
# 启动控制面板
self.control_root.protocol("WM_DELETE_WINDOW", self.on_control_close)
self.control_root.after(100, self.control_root.lift)
def apply_parameters(self):
"""应用新的参数设置"""
global CARRIER_FREQ, MOTION_THRESHOLD, CUTOFF_FREQ, SENSOR_DISTANCE, WAVELENGTH
# 更新参数
CARRIER_FREQ = self.carrier_freq_var.get() * 1000
MOTION_THRESHOLD = self.threshold_var.get() / 100
CUTOFF_FREQ = self.cutoff_var.get()
SENSOR_DISTANCE = self.distance_var.get()
WAVELENGTH = SOUND_SPEED / CARRIER_FREQ
# 重新设计滤波器
self.filter_b, self.filter_a = self._design_lowpass_filter()
# 更新调试信息
self.debug_output += f"参数已更新:\n"
self.debug_output += f" 载波频率: {CARRIER_FREQ/1000:.1f} kHz\n"
self.debug_output += f" 运动阈值: {MOTION_THRESHOLD*100:.2f} cm\n"
self.debug_output += f" 滤波器截止: {CUTOFF_FREQ} Hz\n"
self.debug_output += f" 传感器距离: {SENSOR_DISTANCE:.2f} m\n"
def update_control_panel(self):
"""更新控制面板信息"""
while self.running.is_set():
try:
if hasattr(self, 'debug_label') and self.debug_label:
status = "运动中" if self.motion_active else "等待运动"
self.debug_label.config(text=f"系统状态: {status}")
time.sleep(0.5)
except:
break
def on_control_close(self):
"""控制面板关闭时的处理"""
self.control_root.destroy()
def stop(self):
"""停止检测系统"""
print("停止运动检测系统...")
if self.motion_active and self.current_motion_start:
duration = time.time() - self.current_motion_start
if duration > MIN_MOTION_DURATION:
self.motion_durations.append(duration)
print(f"检测到未结束的运动: 持续时间 {duration:.2f}秒")
self.running.clear()
if hasattr(self, 'control_root') and self.control_root:
self.control_root.destroy()
if self.stream:
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
if hasattr(self, 'processing_thread') and self.processing_thread.is_alive():
self.processing_thread.join(timeout=1.0)
if __name__ == "__main__":
detector = MotionDetector2D()
try:
detector.start()
except KeyboardInterrupt:
detector.stop()
finally:
detector.stop()
```
### 主要改进和新增功能
#### 1. 实时参数调整控制面板
```python
def create_control_panel(self):
"""创建参数控制面板"""
self.control_root = tk.Tk()
self.control_root.title("运动检测控制面板")
# ... 创建各种滑动条和按钮 ...
```
控制面板允许实时调整:
- 载波频率 (18-22 kHz)
- 运动检测阈值 (0.1-5.0 cm)
- 滤波器截止频率 (50-500 Hz)
- 传感器间距 (0.1-2.0 m)
#### 2. 增强的运动检测算法
```python
def _detect_motion(self, dist1, dist2):
"""检测运动并计算持续时间"""
# 计算距离变化率
if len(self.distance1_buffer) > 1:
dist1_change = abs(dist1 - self.distance1_buffer[-1])
dist2_change = abs(dist2 - self.distance2_buffer[-1])
motion_level = max(dist1_change, dist2_change)
# ...
```
改进点:
- 使用零相位滤波减少失真 (`filtfilt` 替代 `lfilter`)
- 添加运动强度量化
- 更精确的阈值检测
#### 3. 全面的调试信息面板
```python
def update_debug_info(self, dist1, dist2, motion_level):
"""更新调试信息"""
info = f"系统状态: {'运动中' if self.motion_active else '等待运动'}\n"
info += f"传感器1距离: {dist1:.4f} m\n"
info += f"传感器2距离: {dist2:.4f} m\n"
# ...
self.debug_text.set_text(info)
```
显示内容:
- 实时传感器数据
- 运动状态和强度
- 当前参数设置
- 系统事件日志
#### 4. 音频数据保存功能
```python
def save_audio_data(self, filename="audio_debug.wav"):
"""保存原始音频数据用于调试"""
# 合并所有缓冲的音频数据
all_data = np.concatenate(list(self.audio_data_buffer))
# 保存为WAV文件
with wave.open(filename, 'wb') as wf:
# ...
```
功能:
- 保存原始音频数据用于离线分析
- 可验证超声波信号质量
- 帮助调试信号处理问题
#### 5. 健壮的错误处理
```python
try:
self.stream = self.p.open(...)
except Exception as e:
print(f"无法打开音频流: {e}")
print("请检查音频设备连接和权限")
self.stream = None
```
改进点:
- 全面的异常捕获
- 详细的错误信息
- 优雅降级处理
### 解决无法检测运动的关键策略
#### 1. 参数优化指导
1. **载波频率调整**:
- 最佳范围:18-22 kHz
- 环境噪声大时使用更高频率
- 使用控制面板实时调整
2. **运动阈值设置**:
- 初始值设为0.5-1.0 cm
- 环境稳定时降低阈值
- 噪声大时提高阈值
3. **滤波器优化**:
- 默认150 Hz适合手部运动
- 快速运动可提高到200-300 Hz
- 慢速运动降低到50-100 Hz
#### 2. 环境调试技巧
1. **测试信号质量**:
- 使用"保存音频数据"功能
- 在Audacity等软件中分析WAV文件
- 确认超声波信号清晰可见
2. **传感器位置优化**:
- 传感器间距0.3-1.0米效果最佳
- 传感器应对准检测区域
- 避免硬表面反射干扰
3. **环境噪声处理**:
- 关闭其他超声波源(如电视、显示器)
- 避免风扇、空调等机械噪声
- 在安静环境中测试
#### 3. 算法增强
1. **自适应阈值**:
```python
# 动态调整阈值
baseline_noise = np.mean(np.abs(dist1 - np.mean(dist1)))
adaptive_threshold = max(MOTION_THRESHOLD, baseline_noise * 3)
```
2. **多帧运动确认**:
```python
# 需要连续3帧超过阈值才确认运动
if motion_level > threshold:
motion_counter += 1
if motion_counter >= 3 and not self.motion_active:
# 确认运动开始
else:
motion_counter = 0
```
3. **信号质量监测**:
```python
# 计算信噪比
signal_power = np.mean(rx_data**2)
noise_power = np.var(rx_data - np.mean(rx_data))
snr = 10 * np.log10(signal_power / noise_power) if noise_power > 0 else 100
```
### 使用指南
1. **启动系统**:
```bash
python motion_detector.py
```
2. **初始调试**:
- 检查控制面板参数
- 使用"保存音频数据"验证信号质量
- 调整传感器位置
3. **运动检测测试**:
- 在传感器前方0.5-2米处挥手
- 观察调试面板的距离变化
- 调整阈值直到稳定检测
4. **常见问题解决**:
- **无检测**:降低阈值,提高载波频率
- **误检测**:提高阈值,降低滤波器频率
- **位置不准**:调整传感器间距,检查环境反射
5. **高级调试**:
- 修改代码添加更多日志
- 使用Matplotlib绘制原始信号
- 分析保存的音频文件