import threading import time import serial import numpy as np import keyboard # 需要 pip install keyboard from enum import Enum # === 官方驱动引用 (请确保这3个文件在同级目录) === from class_ch341 import * from class_sensorcmd import * from class_finger import * # ========================================== # PART 1: 传感器驱动 (完整版 TactileSensorDAQ) # ========================================== DEF_MAX_FINGER_NUM = 3 PCA_ADDR = 0x70 SAMPLE_RATE_MS = 10 class EnumCh341ConnectStatus(Enum): CH341_CONNECT_INIT = 0 CH341_CONNECT_OPEN = 1 CH341_CONNECT_SET_SPEED = 2 CH341_CONNECT_SAMPLE_START = 3 CH341_CONNECT_CHECK = 4 class TactileSensorDAQ: def __init__(self): # 1. 硬件初始化 self.ch341 = ClassCh341() self.fingers = list() # 初始化3个传感器对象 for i in range(DEF_MAX_FINGER_NUM): self.fingers.append(ClassFinger(4 + i, self.ch341)) # 2. 状态机变量 self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT self.ch341CheckTimer = 0 self.pcaAddr = PCA_ADDR self.syncTimer = 0 # 3. 数据容器 self.raw_data = np.zeros(12, dtype=np.float32) self.offset = np.zeros(12, dtype=np.float32) self.clean_data = np.zeros(12, dtype=np.float32) # 4. 线程控制 self.running = False self.lock = threading.Lock() self.thread = None def _set_sensor_enable(self, idx): _pack = list() _pack.append(idx) self.ch341.write(self.pcaAddr, _pack) def _update_state_machine(self): if self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_INIT: if self.ch341.init(): self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_OPEN elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_OPEN: if self.ch341.open(): self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SET_SPEED else: self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_SET_SPEED: if self.ch341.set_speed(self.ch341.IIC_SPEED_400): self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START else: self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START: self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_CHECK def _read_hardware(self): connectedSensorChan = 0 temp_data_buffer = [] for fingerIndex in range(len(self.fingers)): self._set_sensor_enable(1 << (self.fingers[fingerIndex].pcaIdx)) connectedSensorChan |= 1 << (self.fingers[fingerIndex].pcaIdx) current_finger = self.fingers[fingerIndex] if not current_finger.connect: if current_finger.checkSensor(): print(f"[Sensor] Finger {fingerIndex} Connected!") else: current_finger.capRead() for unit_i in range(current_finger.projectPara.ydds_num): fn = current_finger.readData.nf[unit_i] ft = current_finger.readData.tf[unit_i] temp_data_buffer.append(fn) temp_data_buffer.append(ft) if len(temp_data_buffer) == 12: with self.lock: self.raw_data = np.array(temp_data_buffer, dtype=np.float32) self.clean_data = self.raw_data - self.offset if (time.time() - self.syncTimer) > 1.0: self.syncTimer = time.time() self._set_sensor_enable(connectedSensorChan) for f in self.fingers: if f.connect: f.snsCmd.setSensorSync(0) break def _thread_worker(self): while self.running: if self.connectStatus != EnumCh341ConnectStatus.CH341_CONNECT_CHECK: self._update_state_machine() time.sleep(0.1) continue start_time = time.time() try: self._read_hardware() except Exception as e: print(f"Read Error: {e}") self.ch341CheckTimer += (time.time() - start_time) * 1000 if self.ch341CheckTimer >= 1000: self.ch341CheckTimer = 0 if not self.ch341.connectCheck(): print("[Sensor] CH341 Disconnected!") self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT elapsed = (time.time() - start_time) * 1000 sleep_time = (SAMPLE_RATE_MS - elapsed) / 1000.0 if sleep_time > 0: time.sleep(sleep_time) def start(self): if self.running: return self.running = True self.thread = threading.Thread(target=self._thread_worker) self.thread.daemon = True self.thread.start() print("[System] Sensor Thread Started.") def stop(self): self.running = False if self.thread: self.thread.join() self.ch341.disconnect() print("[System] Sensor Thread Stopped.") def tare(self): print("[System] Taring sensors... please wait.") time.sleep(1) with self.lock: self.offset = np.copy(self.raw_data) print("[System] Tare complete.") def get_max_force(self): with self.lock: data = np.copy(self.clean_data) max_force = 0.0 for i in range(0, 12, 2): if data[i] > max_force: max_force = data[i] return max_force # ========================================== # PART 2: 执行器驱动 (RobotDriver) # ========================================== class RobotDriver: def __init__(self, port='COM9', baud=115200): try: self.ser = serial.Serial(port, baud, timeout=1) print(f"[System] Robot Connected to {port}") time.sleep(2) except Exception as e: print(f"[Error] Robot Connection failed: {e}") self.ser = None def _send(self, cmd): if self.ser: # 加上 \r\n 以防万一 full_cmd = f"{cmd}\r\n" self.ser.write(full_cmd.encode()) time.sleep(0.05) def motor_open(self): self._send("M:OPEN") def motor_close(self): self._send("M:CLOSE") def motor_stop(self): self._send("M:STOP") def set_servo(self, servo_id, angle): self._send(f"S{servo_id}:{angle}") def set_config(self, mode): print(f"[Robot] Switching to Config Mode {mode}...") if mode == 0: self.set_servo(1, 90) self.set_servo(2, 90) elif mode == 1: self.set_servo(1, 30) self.set_servo(2, 150) elif mode == 2: self.set_servo(1, 120) self.set_servo(2, 60) time.sleep(1) def close(self): self.motor_stop() if self.ser: self.ser.close() # ========================================== # PART 3: 全局急停逻辑 (SafetyGuard) # ========================================== class SafetyGuard: def __init__(self, robot): self.robot = robot self.is_paused = False keyboard.add_hotkey('space', self.toggle_safety) def toggle_safety(self): self.is_paused = not self.is_paused if self.is_paused: self.robot.motor_stop() print("\n\n" + "!" * 40) print("!!! 紧急停止触发 (EMERGENCY STOP) !!!") print("!!! 电机已锁死,程序挂起 !!!") print("!!! 再按一次 [空格] 恢复运行 !!!") print("!" * 40 + "\n") else: print("\n" + "=" * 40) print(">>> 解除急停,继续任务 (Resuming)...") print("=" * 40 + "\n") def check_pause(self): if not self.is_paused: return None while self.is_paused: time.sleep(0.1) time.sleep(0.5) return True # ========================================== # PART 4: 主逻辑 (Main Controller) # ========================================== def run_auto_grasp_task(robot, sensor, safety): print("\n>>> 任务开始 <<<") # 1. 构型输入 try: mode_str = input("请输入目标构型 (0:初始, 1:错位, 2:对握): ").strip() mode = int(mode_str) if mode not in [0, 1, 2]: raise ValueError except: print("[Error] 无效输入") return # 2. 变构型 safety.check_pause() robot.set_config(mode) # === 限制任务开始时的张开等待,避免过度张开 === OPEN_WAIT_TIME = 1.0 # 秒 safety.check_pause() print(f"[Task] 初始化:直线电机张开... (等待 {OPEN_WAIT_TIME} 秒)") robot.motor_open() # 3. 延时 (带倒计时的充分等待) steps = int(OPEN_WAIT_TIME * 10) # 转换为 0.1s 的步数 print(f"[Task] 正在张开并等待放置番茄 (按空格可急停)...") for i in range(steps): if safety.check_pause(): print("[Task] 暂停恢复,重置倒计时...") time.sleep(0.1) # 每秒打印一次倒计时 seconds_left = OPEN_WAIT_TIME - (i / 10) if i % 10 == 0: print(f"{int(seconds_left)}...", end=' ', flush=True) print("Go!") # =============================================== # 4. 开始闭合 safety.check_pause() print("[Task] 直线电机开始闭合...") robot.motor_close() # 5. 力反馈循环 # ================= 修改位置:力控阈值 ================= FORCE_THRESHOLD = 0.3 # =================================================== start_time = time.time() try: while True: # 急停恢复检查 recover_flag = safety.check_pause() if recover_flag: print("[Task] 恢复运动:重新下发闭合指令...") robot.motor_close() start_time = time.time() # 获取传感器数据 current_max_force = sensor.get_max_force() # 打印状态 if (time.time() * 1000) % 200 < 20: print(f"\r[Grasping] Force: {current_max_force:.1f} / {FORCE_THRESHOLD}", end="") # 触发判断 if current_max_force > FORCE_THRESHOLD: print(f"\n[Task] 触觉触发!停止。") break # 超时保护 (15秒,防止闭合行程也很长) if time.time() - start_time > 15.0: print("\n[Task] 抓取超时 (未检测到受力)。") break time.sleep(0.01) except KeyboardInterrupt: print("\n[Task] 人工强制中断!") # 6. 停止 robot.motor_stop() print("[Task] 任务结束。\n") if __name__ == "__main__": sensor = TactileSensorDAQ() sensor.start() # ================= 修改位置:串口号 ================= # 请务必确认这里是 STM32 的串口号,而不是传感器的 robot = RobotDriver(port='COM9') # ================================================== safety = SafetyGuard(robot) try: print("等待传感器稳定...") time.sleep(2) sensor.tare() while True: print("========================") print(" [Enter] 运行抓取任务") print(" [Space] 随时急停/恢复") print(" [t] 重新去皮") print(" [q] 退出") cmd = input("Command > ").strip().lower() if cmd == '': run_auto_grasp_task(robot, sensor, safety) elif cmd == 't': sensor.tare() elif cmd == 'q': break except KeyboardInterrupt: pass finally: robot.close() sensor.stop() try: keyboard.unhook_all() except: pass print("System All Shutdown.")