連弩又稱”諸葛弩”,相傳為諸葛亮所制,可連續發射弩箭。但由於連弩用箭沒有箭羽,使鐵箭在遠距離飛行時會失去平衡而翻滾,且木製箭桿的製作要求精度高,人工製作難度大,不易大量製造使用。明朝以後,由於火器迅速發展,弩便不再受重視。
今天我們使用建模軟件和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)