【树莓派】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)
跳至工具栏