【树莓派】3D打印制作,可全自动连发的诸葛神弩
连弩又称”诸葛弩”,相传为诸葛亮所制,可连续发射弩箭。但由于连弩用箭没有箭羽,使铁箭在远距离飞行时会失去平衡而翻滚,且木制箭杆的制作要求精度高,人工制作难度大,不易大量制造使用。明朝以后,由于火器迅速发展,弩便不再受重视。
今天我们使用建模软件和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)