|
|
|
|
@ -24,6 +24,7 @@ import json # JSON数据格式处理
|
|
|
|
|
import os # 操作系统接口,文件系统操作
|
|
|
|
|
import sys # 系统相关参数和函数
|
|
|
|
|
import traceback # 异常追踪信息
|
|
|
|
|
import struct # 二进制数据打包和解包
|
|
|
|
|
from machine import UART, Timer, PWM # K210硬件接口:串口、定时器、PWM
|
|
|
|
|
from fpioa_manager import fm # K210引脚管理器
|
|
|
|
|
from Maix import GPIO, MIC_ARRAY as mic # K210 GPIO和麦克风阵列
|
|
|
|
|
@ -1839,6 +1840,519 @@ class DevelopmentBoard:
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"硬件关闭失败: {e}")
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
"""开发板主运行循环 - 实现完整的音频采集和传输流程"""
|
|
|
|
|
self.logger.info("=== 开发板主循环启动 ===")
|
|
|
|
|
self.logger.info(f"当前模式: {self.current_mode.value}")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 确保网络连接正常
|
|
|
|
|
if not self._ensure_network_connection():
|
|
|
|
|
self.logger.error("网络连接失败,无法启动主循环")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 主循环
|
|
|
|
|
while self.running:
|
|
|
|
|
try:
|
|
|
|
|
if self.current_mode == SystemMode.RECORDING:
|
|
|
|
|
self._audio_recording_loop()
|
|
|
|
|
elif self.current_mode == SystemMode.LOCATING:
|
|
|
|
|
self._location_processing_loop()
|
|
|
|
|
elif self.current_mode == SystemMode.ERROR:
|
|
|
|
|
self._error_recovery_loop()
|
|
|
|
|
elif self.current_mode == SystemMode.SHUTDOWN:
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"主循环异常: {e}")
|
|
|
|
|
self._switch_mode(SystemMode.ERROR)
|
|
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
self.logger.info("收到中断信号,准备关闭...")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"主循环严重异常: {e}")
|
|
|
|
|
finally:
|
|
|
|
|
self._cleanup_resources()
|
|
|
|
|
|
|
|
|
|
def _ensure_network_connection(self) -> bool:
|
|
|
|
|
"""确保网络连接正常"""
|
|
|
|
|
try:
|
|
|
|
|
# 检查WiFi连接
|
|
|
|
|
if self.wifi_status != WiFiStatus.CONNECTED:
|
|
|
|
|
if not self._connect_wifi():
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 检查Socket连接
|
|
|
|
|
if self.connection_status != ConnectionStatus.CONNECTED:
|
|
|
|
|
if not self._setup_socket_connections():
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"网络连接检查失败: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _connect_wifi(self) -> bool:
|
|
|
|
|
"""连接WiFi网络"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.info(f"正在连接WiFi: {self.network_config.wifi_ssid}")
|
|
|
|
|
self.wifi_status = WiFiStatus.CONNECTING
|
|
|
|
|
|
|
|
|
|
# 初始化WiFi
|
|
|
|
|
self.nic = network.WLAN(network.STA_IF)
|
|
|
|
|
self.nic.active(True)
|
|
|
|
|
|
|
|
|
|
# 连接WiFi
|
|
|
|
|
self.nic.connect(self.network_config.wifi_ssid, self.network_config.wifi_password)
|
|
|
|
|
|
|
|
|
|
# 等待连接
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
while not self.nic.isconnected():
|
|
|
|
|
if time.time() - start_time > self.network_config.connection_timeout:
|
|
|
|
|
raise WiFiConnectionError("WiFi连接超时", self.network_config.wifi_ssid)
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
# 获取网络信息
|
|
|
|
|
ip_info = self.nic.ifconfig()
|
|
|
|
|
self.logger.info(f"WiFi连接成功 - IP: {ip_info[0]}")
|
|
|
|
|
self.wifi_status = WiFiStatus.CONNECTED
|
|
|
|
|
self.wifi_connection_attempts = 0
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.wifi_connection_attempts += 1
|
|
|
|
|
self.logger.error(f"WiFi连接失败 (第{self.wifi_connection_attempts}次): {e}")
|
|
|
|
|
self.wifi_status = WiFiStatus.ERROR
|
|
|
|
|
|
|
|
|
|
if self.wifi_connection_attempts >= self.network_config.max_reconnect_attempts:
|
|
|
|
|
self.logger.error("WiFi连接尝试次数已达上限")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _setup_socket_connections(self) -> bool:
|
|
|
|
|
"""建立Socket连接"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.info("正在建立Socket连接...")
|
|
|
|
|
self.connection_status = ConnectionStatus.CONNECTING
|
|
|
|
|
|
|
|
|
|
# 创建音频数据Socket
|
|
|
|
|
self.audio_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
self.audio_socket.settimeout(self.network_config.socket_timeout)
|
|
|
|
|
self.audio_socket.connect((self.network_config.pc_ip, self.network_config.pc_port_audio))
|
|
|
|
|
|
|
|
|
|
# 创建指令Socket
|
|
|
|
|
self.cmd_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
self.cmd_socket.settimeout(self.network_config.socket_timeout)
|
|
|
|
|
self.cmd_socket.connect((self.network_config.pc_ip, self.network_config.pc_port_cmd))
|
|
|
|
|
|
|
|
|
|
# 创建定位数据Socket
|
|
|
|
|
self.location_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
self.location_socket.settimeout(self.network_config.socket_timeout)
|
|
|
|
|
self.location_socket.connect((self.network_config.pc_ip, self.network_config.pc_port_location))
|
|
|
|
|
|
|
|
|
|
self.connection_status = ConnectionStatus.CONNECTED
|
|
|
|
|
self.socket_connection_attempts = 0
|
|
|
|
|
self.logger.info("Socket连接建立成功")
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.socket_connection_attempts += 1
|
|
|
|
|
self.logger.error(f"Socket连接失败 (第{self.socket_connection_attempts}次): {e}")
|
|
|
|
|
self.connection_status = ConnectionStatus.ERROR
|
|
|
|
|
|
|
|
|
|
# 关闭失败的Socket
|
|
|
|
|
self._close_sockets()
|
|
|
|
|
|
|
|
|
|
if self.socket_connection_attempts >= self.network_config.max_reconnect_attempts:
|
|
|
|
|
self.logger.error("Socket连接尝试次数已达上限")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _close_sockets(self):
|
|
|
|
|
"""关闭所有Socket连接"""
|
|
|
|
|
try:
|
|
|
|
|
for socket_name, sock in [('audio', self.audio_socket),
|
|
|
|
|
('cmd', self.cmd_socket),
|
|
|
|
|
('location', self.location_socket)]:
|
|
|
|
|
if sock:
|
|
|
|
|
try:
|
|
|
|
|
sock.close()
|
|
|
|
|
self.logger.debug(f"关闭{socket_name} Socket")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"关闭{socket_name} Socket失败: {e}")
|
|
|
|
|
|
|
|
|
|
self.audio_socket = None
|
|
|
|
|
self.cmd_socket = None
|
|
|
|
|
self.location_socket = None
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"关闭Socket失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _audio_recording_loop(self):
|
|
|
|
|
"""音频录音循环 - 第一阶段的核心实现"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.debug("开始音频录音循环")
|
|
|
|
|
|
|
|
|
|
# 1. 采集麦克风阵列音频数据
|
|
|
|
|
audio_data = self._capture_audio_data()
|
|
|
|
|
if audio_data is None:
|
|
|
|
|
self.logger.warning("音频数据采集失败")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 2. 音频预处理
|
|
|
|
|
processed_audio = self._preprocess_audio(audio_data)
|
|
|
|
|
if processed_audio is None:
|
|
|
|
|
self.logger.warning("音频预处理失败")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 3. 网络传输到PC服务器
|
|
|
|
|
if not self._send_audio_data(processed_audio):
|
|
|
|
|
self.logger.error("音频数据发送失败")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 4. 监听PC端指令
|
|
|
|
|
self._listen_for_commands()
|
|
|
|
|
|
|
|
|
|
# 5. 发送心跳包
|
|
|
|
|
self._send_heartbeat_if_needed()
|
|
|
|
|
|
|
|
|
|
# 6. 更新性能统计
|
|
|
|
|
self.performance_monitor.increment('audio_packets_sent')
|
|
|
|
|
|
|
|
|
|
# 控制循环频率
|
|
|
|
|
time.sleep(self.system_config.location_update_interval)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"音频录音循环异常: {e}")
|
|
|
|
|
self.performance_monitor.increment('errors')
|
|
|
|
|
|
|
|
|
|
def _capture_audio_data(self) -> Optional[bytes]:
|
|
|
|
|
"""采集麦克风阵列音频数据"""
|
|
|
|
|
try:
|
|
|
|
|
# 使用麦克风阵列采集音频
|
|
|
|
|
# 假设mic.record()返回原始音频数据
|
|
|
|
|
audio_data = mic.record(self.audio_config.chunk_size)
|
|
|
|
|
|
|
|
|
|
if audio_data is None or len(audio_data) == 0:
|
|
|
|
|
self.logger.warning("麦克风阵列返回空数据")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 转换为字节格式
|
|
|
|
|
if isinstance(audio_data, (list, tuple)):
|
|
|
|
|
# 如果是数值列表,转换为字节
|
|
|
|
|
audio_bytes = b''
|
|
|
|
|
for sample in audio_data:
|
|
|
|
|
audio_bytes += struct.pack('<h', int(sample))
|
|
|
|
|
return audio_bytes
|
|
|
|
|
elif isinstance(audio_data, bytes):
|
|
|
|
|
return audio_data
|
|
|
|
|
else:
|
|
|
|
|
self.logger.warning(f"未知的音频数据格式: {type(audio_data)}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"音频数据采集失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _preprocess_audio(self, audio_data: bytes) -> Optional[bytes]:
|
|
|
|
|
"""音频预处理"""
|
|
|
|
|
try:
|
|
|
|
|
# 1. 数据有效性检查
|
|
|
|
|
if len(audio_data) < self.audio_config.chunk_size * 2: # 假设16位采样
|
|
|
|
|
self.logger.warning("音频数据长度不足")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 2. 音频格式转换(如果需要)
|
|
|
|
|
# 这里可以添加音频格式转换逻辑
|
|
|
|
|
|
|
|
|
|
# 3. 添加时间戳
|
|
|
|
|
timestamp = time.time()
|
|
|
|
|
header = struct.pack('<d', timestamp) # 8字节时间戳
|
|
|
|
|
|
|
|
|
|
# 4. 组合数据
|
|
|
|
|
processed_data = header + audio_data
|
|
|
|
|
|
|
|
|
|
return processed_data
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"音频预处理失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _send_audio_data(self, audio_data: bytes) -> bool:
|
|
|
|
|
"""发送音频数据到PC服务器"""
|
|
|
|
|
try:
|
|
|
|
|
if self.audio_socket is None:
|
|
|
|
|
self.logger.error("音频Socket未连接")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 发送数据
|
|
|
|
|
self.audio_socket.send(audio_data)
|
|
|
|
|
|
|
|
|
|
self.logger.debug(f"音频数据发送成功: {len(audio_data)} bytes")
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except socket.error as e:
|
|
|
|
|
self.logger.error(f"音频数据发送失败: {e}")
|
|
|
|
|
self.connection_status = ConnectionStatus.ERROR
|
|
|
|
|
return False
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"音频数据发送异常: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _listen_for_commands(self):
|
|
|
|
|
"""监听PC端指令"""
|
|
|
|
|
try:
|
|
|
|
|
if self.cmd_socket is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 设置非阻塞模式
|
|
|
|
|
self.cmd_socket.settimeout(0.01) # 10ms超时
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
data = self.cmd_socket.recv(1024)
|
|
|
|
|
if data:
|
|
|
|
|
command = data.decode('utf-8').strip()
|
|
|
|
|
self.logger.info(f"收到PC端指令: {command}")
|
|
|
|
|
|
|
|
|
|
# 处理指令
|
|
|
|
|
self._process_command(command)
|
|
|
|
|
|
|
|
|
|
except socket.timeout:
|
|
|
|
|
# 超时是正常的,继续循环
|
|
|
|
|
pass
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"指令接收异常: {e}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"指令监听失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _process_command(self, command: str):
|
|
|
|
|
"""处理PC端指令"""
|
|
|
|
|
try:
|
|
|
|
|
if command == "START_LOCATION":
|
|
|
|
|
self.logger.info("收到切换到定位模式指令")
|
|
|
|
|
self._switch_mode(SystemMode.LOCATING)
|
|
|
|
|
|
|
|
|
|
elif command == "STOP_LOCATION":
|
|
|
|
|
self.logger.info("收到停止定位模式指令")
|
|
|
|
|
self._switch_mode(SystemMode.RECORDING)
|
|
|
|
|
|
|
|
|
|
elif command == "SHUTDOWN":
|
|
|
|
|
self.logger.info("收到关闭系统指令")
|
|
|
|
|
self._switch_mode(SystemMode.SHUTDOWN)
|
|
|
|
|
|
|
|
|
|
elif command == "HEARTBEAT":
|
|
|
|
|
self.logger.debug("收到心跳包")
|
|
|
|
|
self._send_heartbeat()
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.logger.warning(f"未知指令: {command}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"指令处理失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _send_heartbeat_if_needed(self):
|
|
|
|
|
"""发送心跳包(如果需要)"""
|
|
|
|
|
try:
|
|
|
|
|
current_time = time.time()
|
|
|
|
|
if current_time - self.last_heartbeat > self.system_config.heartbeat_interval:
|
|
|
|
|
self._send_heartbeat()
|
|
|
|
|
self.last_heartbeat = current_time
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"心跳包发送失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _send_heartbeat(self):
|
|
|
|
|
"""发送心跳包"""
|
|
|
|
|
try:
|
|
|
|
|
if self.cmd_socket is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
heartbeat_data = {
|
|
|
|
|
'type': 'heartbeat',
|
|
|
|
|
'timestamp': time.time(),
|
|
|
|
|
'mode': self.current_mode.value,
|
|
|
|
|
'status': self.system_status,
|
|
|
|
|
'uptime': time.time() - self.start_time,
|
|
|
|
|
'memory': gc.mem_free(),
|
|
|
|
|
'error_count': self.error_count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
heartbeat_json = json.dumps(heartbeat_data)
|
|
|
|
|
self.cmd_socket.send(heartbeat_json.encode('utf-8'))
|
|
|
|
|
|
|
|
|
|
self.logger.debug("心跳包发送成功")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"心跳包发送失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _location_processing_loop(self):
|
|
|
|
|
"""定位处理循环 - 第二阶段的核心实现"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.debug("开始定位处理循环")
|
|
|
|
|
|
|
|
|
|
# 1. 采集麦克风阵列数据
|
|
|
|
|
audio_data = self._capture_audio_data()
|
|
|
|
|
if audio_data is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 2. 声源定位计算
|
|
|
|
|
location_data = self._calculate_source_location(audio_data)
|
|
|
|
|
if location_data is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 3. 发送定位数据到PC服务器
|
|
|
|
|
if not self._send_location_data(location_data):
|
|
|
|
|
self.logger.error("定位数据发送失败")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 4. 监听PC端指令
|
|
|
|
|
self._listen_for_commands()
|
|
|
|
|
|
|
|
|
|
# 5. 更新性能统计
|
|
|
|
|
self.performance_monitor.increment('location_packets_sent')
|
|
|
|
|
|
|
|
|
|
# 控制循环频率
|
|
|
|
|
time.sleep(self.system_config.location_update_interval)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"定位处理循环异常: {e}")
|
|
|
|
|
self.performance_monitor.increment('errors')
|
|
|
|
|
|
|
|
|
|
def _calculate_source_location(self, audio_data: bytes) -> Optional[LocationData]:
|
|
|
|
|
"""计算声源位置"""
|
|
|
|
|
try:
|
|
|
|
|
# 创建声源定位器
|
|
|
|
|
locator = SoundSourceLocator(self.hardware_config)
|
|
|
|
|
|
|
|
|
|
# 将字节数据转换为数值列表
|
|
|
|
|
audio_values = self._bytes_to_audio_values(audio_data)
|
|
|
|
|
|
|
|
|
|
# 进行声源定位
|
|
|
|
|
location = locator.process_audio_frame(audio_values)
|
|
|
|
|
|
|
|
|
|
if location:
|
|
|
|
|
self.logger.debug(f"声源定位成功: X={location.x:.2f}, Y={location.y:.2f}")
|
|
|
|
|
return location
|
|
|
|
|
else:
|
|
|
|
|
self.logger.debug("声源定位失败")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"声源定位计算失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _bytes_to_audio_values(self, audio_bytes: bytes) -> List[float]:
|
|
|
|
|
"""将字节数据转换为音频数值列表"""
|
|
|
|
|
try:
|
|
|
|
|
audio_values = []
|
|
|
|
|
|
|
|
|
|
# 假设音频数据是16位整数格式
|
|
|
|
|
for i in range(0, len(audio_bytes), 2):
|
|
|
|
|
if i + 1 < len(audio_bytes):
|
|
|
|
|
sample = struct.unpack('<h', audio_bytes[i:i+2])[0]
|
|
|
|
|
audio_values.append(float(sample) / 32768.0) # 归一化到[-1, 1]
|
|
|
|
|
|
|
|
|
|
return audio_values
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"音频数据转换失败: {e}")
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def _send_location_data(self, location_data: LocationData) -> bool:
|
|
|
|
|
"""发送定位数据到PC服务器"""
|
|
|
|
|
try:
|
|
|
|
|
if self.location_socket is None:
|
|
|
|
|
self.logger.error("定位Socket未连接")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 转换为字符串格式发送
|
|
|
|
|
location_str = f"{location_data.x:.3f},{location_data.y:.3f},{location_data.strength:.3f},{location_data.angle:.3f}"
|
|
|
|
|
self.location_socket.send(location_str.encode('utf-8'))
|
|
|
|
|
|
|
|
|
|
self.logger.debug(f"定位数据发送成功: {location_str}")
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except socket.error as e:
|
|
|
|
|
self.logger.error(f"定位数据发送失败: {e}")
|
|
|
|
|
self.connection_status = ConnectionStatus.ERROR
|
|
|
|
|
return False
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"定位数据发送异常: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _error_recovery_loop(self):
|
|
|
|
|
"""错误恢复循环"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.info("进入错误恢复模式")
|
|
|
|
|
|
|
|
|
|
# 尝试重新连接网络
|
|
|
|
|
if self._ensure_network_connection():
|
|
|
|
|
self.logger.info("网络连接恢复成功")
|
|
|
|
|
self._switch_mode(SystemMode.RECORDING)
|
|
|
|
|
else:
|
|
|
|
|
self.logger.warning("网络连接恢复失败,继续尝试...")
|
|
|
|
|
time.sleep(5) # 等待5秒后重试
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"错误恢复失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _cleanup_resources(self):
|
|
|
|
|
"""清理资源"""
|
|
|
|
|
try:
|
|
|
|
|
self.logger.info("开始清理资源...")
|
|
|
|
|
|
|
|
|
|
# 关闭Socket连接
|
|
|
|
|
self._close_sockets()
|
|
|
|
|
|
|
|
|
|
# 关闭WiFi
|
|
|
|
|
if self.nic:
|
|
|
|
|
self.nic.active(False)
|
|
|
|
|
|
|
|
|
|
# 关闭硬件组件
|
|
|
|
|
self._shutdown_hardware()
|
|
|
|
|
|
|
|
|
|
# 停止定时器
|
|
|
|
|
if self.heartbeat_timer:
|
|
|
|
|
self.heartbeat_timer.stop()
|
|
|
|
|
if self.health_check_timer:
|
|
|
|
|
self.health_check_timer.stop()
|
|
|
|
|
|
|
|
|
|
self.logger.info("资源清理完成")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"资源清理失败: {e}")
|
|
|
|
|
|
|
|
|
|
def _get_mic_direction(self) -> Optional[LocationData]:
|
|
|
|
|
"""获取麦克风方向 - 用于测试麦克风阵列功能"""
|
|
|
|
|
try:
|
|
|
|
|
# 采集一小段音频数据进行测试
|
|
|
|
|
test_audio = self._capture_audio_data()
|
|
|
|
|
if test_audio is None:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 转换为音频数值
|
|
|
|
|
audio_values = self._bytes_to_audio_values(test_audio)
|
|
|
|
|
if not audio_values:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 使用声源定位器进行测试
|
|
|
|
|
locator = SoundSourceLocator(self.hardware_config)
|
|
|
|
|
test_location = locator.process_audio_frame(audio_values)
|
|
|
|
|
|
|
|
|
|
return test_location
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"麦克风方向测试失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# ========== 声源定位核心算法 ==========
|
|
|
|
|
class KalmanFilter:
|
|
|
|
|
|