往期回顾
前期准备
摄像头bug解决
手搓机械臂
视觉模块的封装
下面是题目部分:
第二问我们继续延续第一问的思路:
将棋子坐标与棋盘上标定的坐标进行绑定。
代码展示:
import RPi.GPIO as GPIO
import time
import cv2
import numpy as np
import copy
from pynput import keyboardclass RelayController: # 电磁铁控制def __init__(self, relay_pin, debounce_time=0.5):"""初始化继电器控制类:param relay_pin: 继电器连接的 GPIO 引脚:param debounce_time: 控制继电器的状态保持时间(秒)"""self.relay_pin = relay_pinself.debounce_time = debounce_time# 设置 GPIO 模式为 BCMGPIO.setmode(GPIO.BCM)# 设置引脚为输出模式GPIO.setup(self.relay_pin, GPIO.OUT)def turn_on(self):"""开启继电器"""print("继电器开启")GPIO.output(self.relay_pin, GPIO.HIGH) # 继电器开启def turn_off(self):"""关闭继电器"""print("继电器关闭")GPIO.output(self.relay_pin, GPIO.LOW) # 继电器关闭def run(self):"""控制继电器的状态"""try:while True:self.turn_on()time.sleep(self.debounce_time) # 保持状态self.turn_off()time.sleep(self.debounce_time) # 保持状态except KeyboardInterrupt:print("程序终止,清理 GPIO 设置")finally:self.cleanup()def cleanup(self):"""清理 GPIO 设置"""GPIO.cleanup()def on_release(key):if key == keyboard.Key.esc:# 退出程序的条件return False # 停止监听class ArmControl:def __init__(self):GPIO.setwarnings(False)# 初始化舵机引脚self.servos = {'base': 16,'shoulder': 20,'elbow': 21}# 设定每个九宫格位置对应的舵机角度self.position_angles = {0:(60 ,60 , 90), 9: (94, 69, 46), # 位置1: 基座97°,肩部14°,肘部0° true1 9 18: (89, 67, 48), # 位置2: 基座90°,肩部14°,肘部0° true1 8 17: (83, 63, 51), # 位置3: 基座82°,肩部14°,肘部0° true1 7 16: (96,61, 67), # 位置4: 基座97°,肩部5°,肘部3° true1 6 15: (89, 61, 73), # 位置5: 基座90°,肩部0°,肘部0° true1 5 14: (82, 61, 74), # 位置6: 基座82°,肩部5°,肘部3° true1 4 13: (98, 60, 89), # 位置7: 基座99°,肩部0°,肘部45° true1 3 12: (89, 60, 93), # 位置8: 基座90°,肩部0°,肘部45° true1 2 11: (80, 60, 97) , # 位置9: 基座80°,肩部0°,肘部46° true1 1 1'A1': (75,64,63), # 位置1: 基座97°,肩部14°,肘部0° 'B1':(104,67,49), # 位置1: 基座97°,肩部14°,肘部0° true'C1':(106,66,67), # 位置1: 基座97°,肩部14°,肘部0° ture'D1':(108, 70, 102), # 位置1: 基座97°,肩部14°,肘部0°'A2':(75, 69,43), # 位置1: 基座97°,肩部14°,肘部0° true 1'B2':(75, 64, 67), # 位置1: 基座97°,肩部14°,肘部0° true'C2':(73, 64, 85), # 位置1: 基座97°,肩部14°,肘部0°'D2':(70, 70, 112), # 位置1: 基座97°,肩部14°,肘部0°}# 设置GPIO模式GPIO.setmode(GPIO.BCM)# 设置舵机引脚self.pwm = {}self.last_time = 0 # 上一次设置时间self.debounce_time = 0.03 # 防抖时间(秒)for servo in self.servos.values():GPIO.setup(servo, GPIO.OUT)self.pwm[servo] = GPIO.PWM(servo, 50) # 50Hzself.pwm[servo].start(0)def set_servo_angle(self, pwm, target_angle, speed):"""将舵机平滑旋转到指定角度,并实现防抖逻辑"""current_time = time.time() # 获取当前时间# 防抖逻辑if current_time - self.last_time < self.debounce_time:return # 如果距离上次设置时间小于防抖时间,则不执行# 计算占空比duty = self.angle_to_duty_cycle(target_angle)# 确保占空比在合理范围内if duty < 0:duty = 0elif duty > 12: # 假设最大是180度duty = 12pwm.ChangeDutyCycle(duty)time.sleep(speed) # 按速度控制时间延迟pwm.ChangeDutyCycle(0) # 停止PWM信号self.last_time = current_time # 更新最后设置时间def move_servo(self, servo_name, angle):"""指定舵机移动到指定角度"""self.set_servo_angle(self.pwm[self.servos[servo_name]], angle, 0.5) # 设置速度为0.5秒def move_to_position(self, position):"""移动机械臂到指定棋盘位置(1-9)"""if position in self.position_angles:angles = self.position_angles[position] # 获取角度元组self.move_servo('base', angles[0])# 移动基座time.sleep(0.5)self.move_servo('elbow', angles[2])time.sleep(1)self.move_servo('shoulder', angles[1])# 移动肩部# 移动肘部def remove_to_position(self, position):"""移动机械臂到指定棋盘位置(1-9)"""if position in self.position_angles:angles = self.position_angles[position] # 获取角度元组self.move_servo('shoulder', angles[1])# 移动基座time.sleep(0.5)self.move_servo('elbow', angles[2])time.sleep(1)self.move_servo('base', angles[0])else:print("无效的位置编号!")def angle_to_duty_cycle(self, angle):"""将角度转换为PWM占空比(0到100之间)"""return (angle / 18) + 2 # 示例转换,具体根据舵机类型调整def cleanup(self):"""清理GPIO设置"""for pwm in self.pwm.values():pwm.stop()GPIO.cleanup()class ArmControlA(ArmControl):def __init__(self):super().__init__()self.position_angles.update({11: (104, 68, 49),12: (75, 64, 67),13: (106, 66, 67),14: (73, 64, 85)})self.sequence_positions = [11, 12, 13, 14]self.current_sequence_index = 0def move_to_next_sequence_position(self):if self.current_sequence_index < len(self.sequence_positions):position = self.sequence_positions[self.current_sequence_index]self.move_to_position(position)self.current_sequence_index += 1else:print("序列完成。重置到起始位置。")self.current_sequence_index = 0self.move_to_next_sequence_position()# 初始化一个计数器变量来记录 '1' 键的按下次数
key1_press_count = 0def on_press(key):global relay_controller, key1_press_countrelay_controller = RelayController(relay_pin=17)try:# 替换按下 '1' 键来执行相应的功能if key.char == '1':arm_control_a.move_to_next_sequence_position()print("yidongwanc")# 如果按下 'q', 'w', 'e', 'a', 's', 'd', 'z', 'x', 'c'if key.char in 'qweasdzxc':position = 'qweasdzxc'.index(key.char) + 1 # 将字符转换为对应的数字 1-9relay_controller.turn_on()q1w1e1atime.sleep(2)arm_control_a.remove_to_position(position)time.sleep(2)relay_controller.turn_off()except AttributeError:# 如果按下的是功能键(如 Shift、Ctrl 等),则不做处理relay_controller.cleanup()passdef main6():global arm_control_aarm_control_a = ArmControlA()# 示例继电器 GPIO 引脚print("按 'i' 键依次移动到序列位置 11-14。按 'qweasdzxc' 键移动到 ArmControl 的位置。")# 启动键盘监听with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:try:listener.join() # 开始监听,直到 Esc 键被按下except KeyboardInterrupt:print("程序终止。清理 GPIO 设置。")arm_control_a.cleanup()relay_controller.cleanup()# 1
# q w e a s d z x cif __name__ == "__main__":main6()