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()
Python音频控制步进电机实现
25

被折叠的 条评论
为什么被折叠?



