连弩又称”诸葛弩”,相传为诸葛亮所制,可连续发射弩箭。但由于连弩用箭没有箭羽,使铁箭在远距离飞行时会失去平衡而翻滚,且木制箭杆的制作要求精度高,人工制作难度大,不易大量制造使用。明朝以后,由于火器迅速发展,弩便不再受重视。
今天我们使用建模软件和3D打印技术制作一把可全自动连发的弩,使用树莓派控制舵机运动,程序代码全部为Python编写,包括一个2个自由度的云台。上弹以及发射动作由两个舵机和棘轮结构完成,可以通过增加橡皮筋的数量来改变射击力量。
STL模型和程序已经开源,可以免费下载:
https://www.gewbot.com/learn/1070.html
弹药为大约10x10x4的小方块,由于3D打印机精度各不相同所以这里不提供这个小方块的模型。
实验所用的代码如下:
#!/usr/bin/python3 # File name : crossbow.py # Author : William # Date : 2019/10/09 import Adafruit_PCA9685 import time import socket import threading pwm = Adafruit_PCA9685.PCA9685() pwm.set_pwm_freq(50) port_L = 0 port_R = 1 port_P = 2 port_T = 3 direction_L = 1 direction_R = -1 direction_P = -1 direction_T = -1 servo_middle_L = 290 servo_middle_R = 280 servo_middle_P = 300 servo_middle_T = 300 P_position = servo_middle_P T_position = servo_middle_T PT_speed = 1 P_command = '' T_command = '' CB_command = '' nocked = 0 wiggle_f = 60 wiggle_b = 90 wiggle_loose = 60 steps_delay = 0.15 draw_steps = 5 time_delay = 0.001 def hold(): pwm.set_pwm(port_L, 0, servo_middle_L) pwm.set_pwm(port_R, 0, servo_middle_R) def draw(steps): global nocked for i in range(0, steps): pwm.set_pwm(port_L, 0, servo_middle_L wiggle_f*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R - wiggle_b*direction_R) time.sleep(steps_delay) pwm.set_pwm(port_L, 0, servo_middle_L - wiggle_b*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R - wiggle_b*direction_R) time.sleep(steps_delay) pwm.set_pwm(port_L, 0, servo_middle_L - wiggle_b*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R wiggle_f*direction_R) time.sleep(steps_delay) pwm.set_pwm(port_L, 0, servo_middle_L - wiggle_b*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R - wiggle_b*direction_R) time.sleep(steps_delay) hold() nocked = 1 time.sleep(steps_delay) def loose(): global nocked if nocked: pwm.set_pwm(port_L, 0, servo_middle_L wiggle_loose*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R wiggle_loose*direction_R) time.sleep(steps_delay) nocked = 0 else: draw(draw_steps) pwm.set_pwm(port_L, 0, servo_middle_L wiggle_loose*direction_L) pwm.set_pwm(port_R, 0, servo_middle_R wiggle_loose*direction_R) time.sleep(steps_delay) nocked = 0 def up(speed_input): global T_position T_position =speed_input*direction_T pwm.set_pwm(port_T, 0, T_position) def down(speed_input): global T_position T_position-=speed_input*direction_T pwm.set_pwm(port_T, 0, T_position) def left(speed_input): global P_position P_position-=speed_input*direction_P pwm.set_pwm(port_P, 0, P_position) def right(speed_input): global P_position P_position =speed_input*direction_P pwm.set_pwm(port_P, 0, P_position) class PT_ctrl(threading.Thread): def __init__(self, *args, **kwargs): super(PT_ctrl, self).__init__(*args, **kwargs) self.__flag = threading.Event() self.__flag.set() self.__running = threading.Event() self.__running.set() def run(self): while self.__running.isSet(): self.__flag.wait() if T_command == 'up': up(PT_speed) elif T_command == 'down': down(PT_speed) if P_command == 'left': left(PT_speed) print('11') elif P_command == 'right': right(PT_speed) if P_command == 'P_no' and T_command =='T_no': self.pause() time.sleep(time_delay) def pause(self): self.__flag.clear() def resume(self): self.__flag.set() def stop(self): self.__flag.set() self.__running.clear() class CB_ctrl(threading.Thread): def __init__(self, *args, **kwargs): super(CB_ctrl, self).__init__(*args, **kwargs) self.__flag = threading.Event() self.__flag.set() self.__running = threading.Event() self.__running.set() def run(self): global goal_pos, servo_command, init_get, if_continue, walk_step while self.__running.isSet(): self.__flag.wait() if CB_command == 'draw': draw(draw_steps) self.pause() elif CB_command == 'loose': loose() self.pause() time.sleep(time_delay) def pause(self): self.__flag.clear() def resume(self): self.__flag.set() def stop(self): self.__flag.set() self.__running.clear() PT = PT_ctrl() PT.start() PT.pause() CB = CB_ctrl() CB.start() CB.pause() if __name__ == '__main__': HOST = '' PORT = 10223 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpSerSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpSerSock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) print('waiting for connection...') tcpCliSock, addr = tcpSerSock.accept() print('...connected from :', addr) while True: data = '' data = str(tcpCliSock.recv(BUFSIZ).decode()) if not data: continue elif 'draw' == data: CB_command = data CB.resume() elif 'loose' == data: CB_command = data CB.resume() elif 'P_no' in data: P_command = data elif 'T_no' in data: T_command = data elif 'left' == data: P_command = data PT.resume() elif 'right' == data: P_command = data PT.resume() elif 'up' in data: T_command = data PT.resume() elif 'down' in data: T_command = data PT.resume() print(data)
,