python语言音频控制步进电机 - 增强版软件代码QZQ

Python音频控制步进电机实现

Arduino 代码

// 定义TB6600驱动器与Arduino的连接引脚
const int PUL_PIN = 8;
const int DIR_PIN = 9;
const int ENA_PIN = 10;

// 电机参数
const int STEPS_PER_REV = 200;
const int MICROSTEP = 16;
const float ANGLE_PER_STEP = 360.0 / (STEPS_PER_REV * MICROSTEP);

// 控制变量
int currentDir = 1;       // 当前方向(1正转,0反转)
int currentDelay = 500;   // 当前脉冲间隔(微秒)
bool isMoving = false;    // 是否正在运动

void setup() {
  pinMode(PUL_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(ENA_PIN, OUTPUT);
  digitalWrite(ENA_PIN, LOW);  // 使能驱动器
  Serial.begin(9600);
  Serial.println("设备就绪");
}

void loop() {
  // 接收Python发送的指令(格式:D[0/1]S[数字],例如D1S500)
  if (Serial.available() > 0) {
    String cmd = Serial.readStringUntil('\n');
    parseCommand(cmd);  // 解析指令
  }

  // 如果需要运动,持续发送脉冲
  if (isMoving) {
    digitalWrite(DIR_PIN, currentDir);
    digitalWrite(PUL_PIN, HIGH);
    delayMicroseconds(currentDelay);
    digitalWrite(PUL_PIN, LOW);
    delayMicroseconds(currentDelay);
  }
}

// 解析Python发送的指令
void parseCommand(String cmd) {
  // 提取方向(D后面的数字)
  if (cmd.startsWith("D")) {
    int dirIndex = cmd.indexOf('D') + 1;
    int sIndex = cmd.indexOf('S');
    if (sIndex > dirIndex) {
      String dirStr = cmd.substring(dirIndex, sIndex);
      currentDir = dirStr.toInt();
      Serial.print("方向已设置为:");
      Serial.println(currentDir == 1 ? "正转" : "反转");
    }

    // 提取速度(S后面的数字,脉冲间隔)
    if (sIndex != -1 && sIndex < cmd.length() - 1) {
      String speedStr = cmd.substring(sIndex + 1);
      currentDelay = speedStr.toInt();
      Serial.print("脉冲间隔已设置为:");
      Serial.println(currentDelay);
    }
  }

  // 控制运动状态(空格键触发的开始/停止指令)
  if (cmd == "MOVE_ON") {
    isMoving = true;
    Serial.println("开始运动");
  } else if (cmd == "MOVE_OFF") {
    isMoving = false;
    Serial.println("停止运动");
  }
}

python语言音频控制步进电机 - 增强版软件代码

import pyaudio
import numpy as np
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import serial
import serial.tools.list_ports
import threading
import time
import queue
import math


class AudioMotorController:
    def __init__(self, root):
        self.root = root
        self.root.title("音频控制步进电机 - 增强版")
        self.root.geometry("750x750")
        self.root.resizable(True, True)

        # 音频配置
        self.FORMAT = pyaudio.paFloat32
        self.CHANNELS = 1
        self.RATE = 44100
        self.CHUNK = 1024

        # 增强的音频控制参数
        self.VOLUME_THRESHOLD_HIGH = 0.005  # 高音量阈值
        self.VOLUME_THRESHOLD_LOW = 0.002  # 低音量阈值
        self.SENSITIVITY = 2.0  # 灵敏度系数

        # 频率分析参数
        self.LOW_FREQ_RANGE = (0, 200)  # 低音频率范围 (Hz)
        self.HIGH_FREQ_RANGE = (1000, 5000)  # 高音频率范围 (Hz)

        # 音频相关变量
        self.audio = None
        self.stream = None
        self.audio_running = False
        self.audio_thread = None

        # 串口相关变量
        self.ser = None
        self.connected = False
        self.port_queue = queue.Queue()

        # 电机控制参数
        self.current_speed = 500  # 初始脉冲间隔(微秒)
        self.min_speed = 100  # 最快速度(最小间隔)
        self.max_speed = 2000  # 最慢速度(最大间隔)
        self.speed_step = 100  # 速度调整步长
        self.direction = 1  # 1:正转, 0:反转
        self.moving = False
        self.active_key = None

        # 增强的音频控制状态
        self.audio_control_enabled = False
        self.volume_history = []  # 存储音量历史
        self.history_length = 20  # 历史记录长度

        # 持续时间计数器
        self.high_volume_duration = 0
        self.low_volume_duration = 0
        self.continuous_threshold = 10  # 持续计数阈值

        # 频率分析状态
        self.low_freq_strength = 0
        self.high_freq_strength = 0
        self.freq_direction = 1  # 基于频率的方向

        # 创建UI
        self.create_widgets()

        # 启动端口扫描线程
        self.scan_thread = threading.Thread(target=self.scan_ports_continuously, daemon=True)
        self.scan_thread.start()

        # 启动消息处理线程
        self.msg_thread = threading.Thread(target=self.process_messages, daemon=True)
        self.msg_thread.start()

    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 串口控制区域
        port_frame = ttk.LabelFrame(main_frame, text="串口设置", padding="10")
        port_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(port_frame, text="端口:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.port_var = tk.StringVar()
        self.port_combo = ttk.Combobox(port_frame, textvariable=self.port_var, width=20)
        self.port_combo.grid(row=0, column=1, padx=5, pady=5)

        self.refresh_btn = ttk.Button(port_frame, text="刷新", command=self.scan_ports)
        self.refresh_btn.grid(row=0, column=2, padx=5, pady=5)

        self.connect_btn = ttk.Button(port_frame, text="连接", command=self.toggle_connection)
        self.connect_btn.grid(row=0, column=3, padx=5, pady=5)

        # 连接状态指示
        self.connection_status = ttk.Label(port_frame, text="未连接", foreground="red")
        self.connection_status.grid(row=0, column=4, padx=10, pady=5)

        # 音频控制区域
        audio_frame = ttk.LabelFrame(main_frame, text="音频控制", padding="10")
        audio_frame.pack(fill=tk.X, pady=(0, 10))

        # 音频控制开关
        self.audio_btn = ttk.Button(audio_frame, text="开始音频控制", command=self.toggle_audio_control)
        self.audio_btn.grid(row=0, column=0, padx=5, pady=5)

        # 灵敏度调节
        ttk.Label(audio_frame, text="灵敏度:").grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        self.sensitivity_scale = tk.Scale(
            audio_frame,
            from_=0.5,
            to=5.0,
            orient=tk.HORIZONTAL,
            length=150,
            showvalue=True,
            resolution=0.1,
            command=self.on_sensitivity_change
        )
        self.sensitivity_scale.set(self.SENSITIVITY)
        self.sensitivity_scale.grid(row=0, column=2, padx=5, pady=5, sticky=tk.EW)

        # 控制模式选择
        ttk.Label(audio_frame, text="控制模式:").grid(row=0, column=3, padx=5, pady=5, sticky=tk.W)
        self.control_mode = tk.StringVar(value="volume")
        ttk.Radiobutton(audio_frame, text="音量控制", variable=self.control_mode,
                        value="volume").grid(row=0, column=4, padx=2, pady=5)
        ttk.Radiobutton(audio_frame, text="频率控制", variable=self.control_mode,
                        value="frequency").grid(row=0, column=5, padx=2, pady=5)

        # 音频状态显示区域
        audio_status_frame = ttk.Frame(audio_frame)
        audio_status_frame.grid(row=1, column=0, columnspan=6, sticky=tk.W, pady=5)

        ttk.Label(audio_status_frame, text="当前音量:").grid(row=0, column=0, padx=5, pady=2, sticky=tk.W)
        self.volume_var = tk.StringVar(value="0.0000")
        ttk.Label(audio_status_frame, textvariable=self.volume_var).grid(row=0, column=1, padx=5, pady=2)

        ttk.Label(audio_status_frame, text="低音强度:").grid(row=0, column=2, padx=5, pady=2, sticky=tk.W)
        self.low_freq_var = tk.StringVar(value="0.0000")
        ttk.Label(audio_status_frame, textvariable=self.low_freq_var).grid(row=0, column=3, padx=5, pady=2)

        ttk.Label(audio_status_frame, text="高音强度:").grid(row=0, column=4, padx=5, pady=2, sticky=tk.W)
        self.high_freq_var = tk.StringVar(value="0.0000")
        ttk.Label(audio_status_frame, textvariable=self.high_freq_var).grid(row=0, column=5, padx=5, pady=2)

        # 速度控制区域
        speed_frame = ttk.LabelFrame(main_frame, text="速度控制", padding="10")
        speed_frame.pack(fill=tk.X, pady=(0, 10))

        # 速度滑块
        ttk.Label(speed_frame, text="基础速度:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.speed_scale = tk.Scale(
            speed_frame,
            from_=self.max_speed,
            to=self.min_speed,
            orient=tk.HORIZONTAL,
            length=400,
            showvalue=True,
            resolution=50,
            command=self.on_speed_scale_change
        )
        self.speed_scale.set(self.current_speed)
        self.speed_scale.grid(row=0, column=1, columnspan=3, padx=5, pady=5, sticky=tk.EW)

        # 速度数值显示和按钮
        ttk.Label(speed_frame, text="当前速度:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        self.speed_var = tk.StringVar(value=f"{self.current_speed} μs")
        ttk.Label(speed_frame, textvariable=self.speed_var).grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)

        # 速度微调按钮
        ttk.Button(speed_frame, text="加速", command=self.speed_up).grid(row=1, column=2, padx=5, pady=5)
        ttk.Button(speed_frame, text="减速", command=self.slow_down).grid(row=1, column=3, padx=5, pady=5)

        # 方向键按钮控制区
        control_btn_frame = ttk.LabelFrame(main_frame, text="手动控制", padding="10")
        control_btn_frame.pack(fill=tk.X, pady=(0, 10))

        # 创建方向键按钮网格
        btn_frame = ttk.Frame(control_btn_frame)
        btn_frame.pack(pady=10)

        # 上方向键(正转)
        self.up_btn = ttk.Button(btn_frame, text="↑ 正转")
        self.up_btn.bind('<ButtonPress-1>', lambda e: self.start_movement(1))
        self.up_btn.bind('<ButtonRelease-1>', lambda e: self.stop_movement())
        self.up_btn.grid(row=0, column=1, padx=5, pady=5, ipady=10)

        # 左方向键(减速)
        self.left_btn = ttk.Button(btn_frame, text="← 减速", command=self.slow_down)
        self.left_btn.grid(row=1, column=0, padx=5, pady=5, ipadx=10)

        # 停止按钮
        self.stop_btn = ttk.Button(btn_frame, text="停止", command=self.stop_movement)
        self.stop_btn.grid(row=1, column=1, padx=5, pady=5, ipadx=10)

        # 右方向键(加速)
        self.right_btn = ttk.Button(btn_frame, text="→ 加速", command=self.speed_up)
        self.right_btn.grid(row=1, column=2, padx=5, pady=5, ipadx=10)

        # 下方向键(反转)
        self.down_btn = ttk.Button(btn_frame, text="↓ 反转")
        self.down_btn.bind('<ButtonPress-1>', lambda e: self.start_movement(0))
        self.down_btn.bind('<ButtonRelease-1>', lambda e: self.stop_movement())
        self.down_btn.grid(row=2, column=1, padx=5, pady=5, ipady=10)

        # 电机状态显示区域
        status_frame = ttk.LabelFrame(main_frame, text="电机状态", padding="10")
        status_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(status_frame, text="当前速度:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.speed_display_var = tk.StringVar(value=f"{self.current_speed} μs")
        ttk.Label(status_frame, textvariable=self.speed_display_var).grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(status_frame, text="当前方向:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.dir_var = tk.StringVar(value="停止")
        ttk.Label(status_frame, textvariable=self.dir_var).grid(row=0, column=3, padx=5, pady=5)

        ttk.Label(status_frame, text="运动状态:").grid(row=0, column=4, padx=5, pady=5, sticky=tk.W)
        self.move_var = tk.StringVar(value="停止")
        ttk.Label(status_frame, textvariable=self.move_var).grid(row=0, column=5, padx=5, pady=5)

        # 音频状态显示
        ttk.Label(status_frame, text="音频控制:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        self.audio_status_var = tk.StringVar(value="未启用")
        ttk.Label(status_frame, textvariable=self.audio_status_var).grid(row=1, column=1, padx=5, pady=5)

        ttk.Label(status_frame, text="控制模式:").grid(row=1, column=2, padx=5, pady=5, sticky=tk.W)
        self.mode_display_var = tk.StringVar(value="手动")
        ttk.Label(status_frame, textvariable=self.mode_display_var).grid(row=1, column=3, padx=5, pady=5)

        # 日志显示区域
        log_frame = ttk.LabelFrame(main_frame, text="操作日志", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True)

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=8)
        self.log_text.pack(fill=tk.BOTH, expand=True)
        self.log_text.config(state=tk.DISABLED)

        # 操作提示
        hint_frame = ttk.LabelFrame(main_frame, text="操作提示", padding="10")
        hint_frame.pack(fill=tk.X)

        hints = """
        增强音频控制模式:
        1. 音量控制模式:声音变大就正转,大声音持续就持续正转;声音变小就反转,小声音持续就持续反转
        2. 频率控制模式:高音就正转,高音持续就持续正转;低音就反转,低音持续就持续反转
        3. 手动控制:按钮或键盘方向键控制,速度滑块调节基础速度
        4. 灵敏度调节:根据环境噪音调整灵敏度,数值越大越敏感
        """
        ttk.Label(hint_frame, text=hints).pack(anchor=tk.W)

    def on_sensitivity_change(self, value):
        """灵敏度改变时的回调函数"""
        self.SENSITIVITY = float(value)
        # 根据灵敏度调整阈值
        base_threshold = 0.005
        self.VOLUME_THRESHOLD_HIGH = base_threshold / self.SENSITIVITY
        self.VOLUME_THRESHOLD_LOW = self.VOLUME_THRESHOLD_HIGH * 0.6

    def on_speed_scale_change(self, value):
        """滑块值改变时的回调函数"""
        new_speed = int(float(value))
        if new_speed != self.current_speed:
            self.current_speed = new_speed
            self.update_speed_display()

            if self.connected:
                try:
                    self.send_speed_command()
                    self.log(f"速度已更新: {self.current_speed} μs")
                except Exception as e:
                    self.log(f"发送速度指令失败: {str(e)}")

    def update_speed_display(self):
        """更新所有速度显示"""
        self.speed_var.set(f"{self.current_speed} μs")
        self.speed_display_var.set(f"{self.current_speed} μs")
        if self.speed_scale.get() != self.current_speed:
            self.speed_scale.set(self.current_speed)

    def log(self, message):
        """在日志区域显示消息"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, f"{time.strftime('%H:%M:%S')} - {message}\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)

    def toggle_audio_control(self):
        """切换音频控制状态"""
        if self.audio_control_enabled:
            self.stop_audio_control()
        else:
            self.start_audio_control()

    def start_audio_control(self):
        """开始音频控制"""
        try:
            self.audio = pyaudio.PyAudio()
            self.stream = self.audio.open(
                format=self.FORMAT,
                channels=self.CHANNELS,
                rate=self.RATE,
                input=True,
                frames_per_buffer=self.CHUNK
            )

            self.audio_running = True
            self.audio_control_enabled = True
            self.audio_thread = threading.Thread(target=self.audio_monitor, daemon=True)
            self.audio_thread.start()

            self.audio_btn.config(text="停止音频控制")
            self.audio_status_var.set("运行中")
            mode_text = "音量" if self.control_mode.get() == "volume" else "频率"
            self.mode_display_var.set(f"音频({mode_text})")
            self.log("增强音频控制已启动")

        except Exception as e:
            self.log(f"启动音频控制失败: {str(e)}")
            messagebox.showerror("错误", f"无法启动音频输入: {str(e)}")

    def stop_audio_control(self):
        """停止音频控制"""
        self.audio_running = False
        self.audio_control_enabled = False

        if self.stream:
            self.stream.stop_stream()
            self.stream.close()
        if self.audio:
            self.audio.terminate()

        self.audio_btn.config(text="开始音频控制")
        self.audio_status_var.set("未启用")
        self.mode_display_var.set("手动")
        self.volume_var.set("0.0000")
        self.low_freq_var.set("0.0000")
        self.high_freq_var.set("0.0000")
        self.log("音频控制已停止")

    def analyze_frequency(self, audio_data):
        """分析音频频率成分"""
        # 应用汉宁窗
        window = np.hanning(len(audio_data))
        windowed_data = audio_data * window

        # 执行FFT
        fft_data = np.fft.fft(windowed_data)
        frequencies = np.fft.fftfreq(len(fft_data), 1.0 / self.RATE)

        # 计算幅度
        magnitude = np.abs(fft_data)

        # 计算低音和高音强度
        low_mask = (frequencies >= self.LOW_FREQ_RANGE[0]) & (frequencies <= self.LOW_FREQ_RANGE[1])
        high_mask = (frequencies >= self.HIGH_FREQ_RANGE[0]) & (frequencies <= self.HIGH_FREQ_RANGE[1])

        self.low_freq_strength = np.mean(magnitude[low_mask]) if np.any(low_mask) else 0
        self.high_freq_strength = np.mean(magnitude[high_mask]) if np.any(high_mask) else 0

        # 更新显示
        self.root.after(0, lambda: self.low_freq_var.set(f"{self.low_freq_strength:.4f}"))
        self.root.after(0, lambda: self.high_freq_var.set(f"{self.high_freq_strength:.4f}"))

    def audio_monitor(self):
        """增强的音频监控函数"""
        last_update_time = 0
        update_interval = 0.1  # 更新间隔(秒)

        while self.audio_running:
            try:
                # 读取音频数据
                data = self.stream.read(self.CHUNK, exception_on_overflow=False)
                audio_data = np.frombuffer(data, dtype=np.float32)

                # 计算音量
                volume = np.sqrt(np.mean(np.square(audio_data)))

                # 更新音量显示
                self.root.after(0, lambda: self.volume_var.set(f"{volume:.4f}"))

                # 频率分析
                self.analyze_frequency(audio_data)

                current_time = time.time()
                if current_time - last_update_time >= update_interval:
                    last_update_time = current_time

                    # 根据选择的控制模式执行相应的控制逻辑
                    if self.control_mode.get() == "volume":
                        self.volume_based_control(volume)
                    else:
                        self.frequency_based_control()

                time.sleep(0.01)

            except Exception as e:
                self.root.after(0, lambda: self.log(f"音频处理错误: {str(e)}"))
                time.sleep(0.1)

    def volume_based_control(self, volume):
        """基于音量的控制逻辑"""
        # 动态阈值计算
        self.volume_history.append(volume)
        if len(self.volume_history) > self.history_length:
            self.volume_history.pop(0)

        avg_volume = np.mean(self.volume_history) if self.volume_history else volume
        dynamic_high_threshold = max(self.VOLUME_THRESHOLD_HIGH, avg_volume * 1.5)
        dynamic_low_threshold = max(self.VOLUME_THRESHOLD_LOW, avg_volume * 0.8)

        # 音量控制逻辑
        if volume > dynamic_high_threshold:
            self.high_volume_duration += 1
            self.low_volume_duration = 0

            if self.high_volume_duration >= self.continuous_threshold:
                # 持续高音量 - 持续正转
                if not self.moving or self.direction != 1:
                    self.root.after(0, lambda: self.start_audio_movement(1, "持续高音量正转"))
            else:
                # 瞬时高音量 - 正转
                if not self.moving:
                    self.root.after(0, lambda: self.start_audio_movement(1, "高音量正转"))

        elif volume < dynamic_low_threshold:
            self.low_volume_duration += 1
            self.high_volume_duration = 0

            if self.low_volume_duration >= self.continuous_threshold:
                # 持续低音量 - 持续反转
                if not self.moving or self.direction != 0:
                    self.root.after(0, lambda: self.start_audio_movement(0, "持续低音量反转"))
            else:
                # 瞬时低音量 - 反转
                if not self.moving:
                    self.root.after(0, lambda: self.start_audio_movement(0, "低音量反转"))
        else:
            # 中等音量 - 根据趋势决定
            self.high_volume_duration = max(0, self.high_volume_duration - 1)
            self.low_volume_duration = max(0, self.low_volume_duration - 1)

            if self.high_volume_duration > self.low_volume_duration and self.moving and self.direction != 1:
                self.root.after(0, lambda: self.start_audio_movement(1, "趋势正转"))
            elif self.low_volume_duration > self.high_volume_duration and self.moving and self.direction != 0:
                self.root.after(0, lambda: self.start_audio_movement(0, "趋势反转"))
            elif self.high_volume_duration == 0 and self.low_volume_duration == 0 and self.moving:
                self.root.after(0, self.stop_audio_movement)

    def frequency_based_control(self):
        """基于频率的控制逻辑"""
        # 计算频率强度阈值
        freq_threshold = max(self.low_freq_strength, self.high_freq_strength) * 0.3

        if self.high_freq_strength > freq_threshold and self.high_freq_strength > self.low_freq_strength:
            self.high_volume_duration += 1
            self.low_volume_duration = 0

            if self.high_volume_duration >= self.continuous_threshold:
                # 持续高音 - 持续正转
                if not self.moving or self.direction != 1:
                    self.root.after(0, lambda: self.start_audio_movement(1, "持续高音正转"))
            else:
                # 瞬时高音 - 正转
                if not self.moving:
                    self.root.after(0, lambda: self.start_audio_movement(1, "高音正转"))

        elif self.low_freq_strength > freq_threshold and self.low_freq_strength > self.high_freq_strength:
            self.low_volume_duration += 1
            self.high_volume_duration = 0

            if self.low_volume_duration >= self.continuous_threshold:
                # 持续低音 - 持续反转
                if not self.moving or self.direction != 0:
                    self.root.after(0, lambda: self.start_audio_movement(0, "持续低音反转"))
            else:
                # 瞬时低音 - 反转
                if not self.moving:
                    self.root.after(0, lambda: self.start_audio_movement(0, "低音反转"))
        else:
            # 平衡状态
            self.high_volume_duration = max(0, self.high_volume_duration - 1)
            self.low_volume_duration = max(0, self.low_volume_duration - 1)

            if self.moving:
                self.root.after(0, self.stop_audio_movement)

    def start_audio_movement(self, direction, reason=""):
        """音频控制启动运动"""
        if not self.connected:
            return

        self.direction = direction
        self.moving = True

        dir_text = "正转" if direction == 1 else "反转"
        self.dir_var.set(dir_text)
        self.move_var.set("音频控制")

        try:
            self.send_speed_command()
            time.sleep(0.02)
            self.ser.write(b"MOVE_ON\n")
            self.ser.flush()
            if reason:
                self.log(f"音频控制: {reason}")
        except Exception as e:
            self.log(f"发送音频启动指令失败: {str(e)}")

    def stop_audio_movement(self):
        """音频控制停止运动"""
        if not self.connected or not self.moving:
            return

        self.moving = False
        self.move_var.set("停止")
        self.dir_var.set("停止")

        try:
            self.ser.write(b"MOVE_OFF\n")
            self.ser.flush()
            self.log("音频控制: 停止运动")
        except Exception as e:
            self.log(f"发送音频停止指令失败: {str(e)}")

    # 以下方法保持不变(串口连接、手动控制等)
    def scan_ports(self):
        """扫描可用串口"""
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combo['values'] = ports
        if ports:
            self.port_var.set(ports[0])
        self.log(f"扫描到 {len(ports)} 个可用端口")

    def scan_ports_continuously(self):
        """持续扫描端口"""
        while True:
            ports = [port.device for port in serial.tools.list_ports.comports()]
            self.port_queue.put(ports)
            time.sleep(5)

    def process_messages(self):
        """处理队列消息"""
        while True:
            try:
                port_list = self.port_queue.get(block=True, timeout=1)
                current_values = self.port_combo['values']
                if list(current_values) != port_list:
                    self.port_combo['values'] = port_list
                    if port_list and self.port_var.get() not in port_list:
                        self.port_var.set(port_list[0])
                    self.log(f"端口列表已更新: {', '.join(port_list)}")
            except queue.Empty:
                pass
            time.sleep(0.1)

    def toggle_connection(self):
        """切换连接状态"""
        if self.connected:
            self.disconnect()
        else:
            self.connect()

    def connect(self):
        """连接串口"""
        port = self.port_var.get()
        if not port:
            messagebox.showerror("错误", "请选择端口")
            return

        try:
            self.ser = serial.Serial(
                port=port,
                baudrate=9600,
                timeout=1,
                write_timeout=1
            )
            if self.ser.is_open:
                self.connected = True
                self.connect_btn.config(text="断开")
                self.connection_status.config(text="已连接", foreground="green")
                self.initialize_motor()
                self.log(f"已连接到 {port}")
                return True
        except Exception as e:
            self.log(f"连接失败: {str(e)}")
            messagebox.showerror("连接失败", str(e))
        return False

    def initialize_motor(self):
        """连接后初始化电机"""
        try:
            if self.ser and self.ser.in_waiting > 0:
                self.ser.reset_input_buffer()
            self.ser.write(b"MOVE_OFF\n")
            time.sleep(0.1)
            self.send_speed_command()
            time.sleep(0.1)
            self.log("电机初始化完成")
        except Exception as e:
            self.log(f"电机初始化失败: {str(e)}")

    def send_speed_command(self):
        """发送速度指令"""
        if self.connected and self.ser:
            command = f"D{self.direction}S{self.current_speed}\n"
            self.ser.write(command.encode())
            self.ser.flush()

    def disconnect(self):
        """断开连接"""
        if self.audio_control_enabled:
            self.stop_audio_control()

        if self.ser and self.ser.is_open:
            try:
                self.ser.write(b"MOVE_OFF\n")
                self.ser.flush()
                time.sleep(0.1)
                self.ser.close()
            except:
                pass

        self.connected = False
        self.connect_btn.config(text="连接")
        self.connection_status.config(text="未连接", foreground="red")
        self.stop_movement()
        self.log("已断开连接")

    def start_movement(self, direction):
        """手动启动运动"""
        if not self.connected:
            messagebox.showwarning("警告", "请先连接设备")
            return

        self.direction = direction
        self.moving = True
        dir_text = "正转" if direction == 1 else "反转"
        self.dir_var.set(dir_text)
        self.move_var.set("手动控制")

        try:
            self.send_speed_command()
            time.sleep(0.05)
            self.ser.write(b"MOVE_ON\n")
            self.ser.flush()
            self.log(f"手动控制: 开始{dir_text},速度: {self.current_speed} μs")
        except Exception as e:
            self.log(f"发送指令失败: {str(e)}")
            self.moving = False
            self.move_var.set("停止")

    def stop_movement(self):
        """停止运动"""
        if not self.connected:
            return

        if self.moving:
            self.moving = False
            self.move_var.set("停止")
            self.dir_var.set("停止")

            try:
                self.ser.write(b"MOVE_OFF\n")
                self.ser.flush()
                self.log("手动控制: 停止运动")
            except Exception as e:
                self.log(f"发送停止指令失败: {str(e)}")

    def speed_up(self):
        """加速"""
        if self.current_speed > self.min_speed:
            self.current_speed = max(self.min_speed, self.current_speed - self.speed_step)
            self.update_speed_display()
            self.log(f"速度增加,当前间隔: {self.current_speed} μs")

            if self.connected:
                try:
                    self.send_speed_command()
                except Exception as e:
                    self.log(f"发送速度指令失败: {str(e)}")

    def slow_down(self):
        """减速"""
        if self.current_speed < self.max_speed:
            self.current_speed = min(self.max_speed, self.current_speed + self.speed_step)
            self.update_speed_display()
            self.log(f"速度降低,当前间隔: {self.current_speed} μs")

            if self.connected:
                try:
                    self.send_speed_command()
                except Exception as e:
                    self.log(f"发送速度指令失败: {str(e)}")

    def handle_keypress(self, event):
        """处理键盘按下事件"""
        if not self.connected:
            return

        key = event.keysym
        if key in ['Left', 'Right']:
            if key == 'Left':
                self.slow_down()
            elif key == 'Right':
                self.speed_up()
        elif key in ['Up', 'Down']:
            if key == self.active_key:
                return
            self.active_key = key
            if key == 'Up':
                self.start_movement(1)
            elif key == 'Down':
                self.start_movement(0)

    def handle_keyrelease(self, event):
        """处理键盘释放事件"""
        if not self.connected:
            return

        key = event.keysym
        if key in ['Up', 'Down'] and key == self.active_key:
            self.stop_movement()
            self.active_key = None

    def __del__(self):
        """析构函数,确保资源被正确释放"""
        if self.audio_control_enabled:
            self.stop_audio_control()
        if self.connected:
            self.disconnect()


if __name__ == "__main__":
    root = tk.Tk()
    app = AudioMotorController(root)

    root.bind('<KeyPress>', app.handle_keypress)
    root.bind('<KeyRelease>', app.handle_keyrelease)
    root.focus_set()

    root.mainloop()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Esoft Studio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值