산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

DIY 자율 너프 센트리 터렛 – 스테퍼 모터, 카메라, 아두이노로 스마트 너프건 만들기

부품 및 소모품

부품 수량
OpenBuilds NEMA 17 스테퍼 모터 1
DFRobot 기어박스 스테퍼 모터 1
DFRobot 스테퍼 모터 드라이버 1
DFRobot Pixy 2 카메라 1
Arduino Mega 2560 1
초음파 센서 HC-SR04 1
너프 나이트론 건(Nerf Nitron Gun) 1

필요 도구 및 장비

앱 및 온라인 서비스

프로젝트 소개

아이디어

몇 년 전, 한 번 조준하면 자동으로 발사하는 반자율 터렛 프로젝트를 보았습니다. 그 프로젝트에서 영감을 받아 Pixy 2 카메라로 타겟을 획득하고 너프건을 자동으로 조준하여 스스로 락온하고 발사할 수 있게 만들었습니다.

구성 요소

이 프로젝트에서 건에는 눈이 필요했기 때문에 메인보드와 쉽게 인터페이스할 수 있는 Pixy 2를 선택했습니다. 마이크로컨트롤러로는 핀 수가 많은 Arduino Mega 2560을 선택했습니다.

건이 요(yaw)와 피치(pitch) 두 축이 필요하므로 두 개의 스테퍼 모터가 필요합니다. DFRobot에서 듀얼 DRV8825 모터 드라이버 보드를 지원받았습니다.

CAD 설계

Fusion 360을 실행하고 너프건 캔버스를 불러온 후 솔리드 바디를 생성했습니다. 건 설계 후 베어링 기반 지지대가 있는 플랫폼을 만들어 건이 좌우로 회전할 수 있게 했습니다. 회전 플랫폼 옆에 스테퍼 모터를 배치해 구동했습니다.

더 큰 질문은 건을 상하로 피치하게 만드는 방법입니다. 이를 위해 이동 블록에 한 점, 건 후면에 다른 한 점을 연결하는 리니어 드라이브 시스템이 필요했습니다. 두 점을 연결하는 로드로 건이 중심축을 따라 피벗할 수 있게 했습니다.

부품 제작

내 설계 거의 모든 부품은 3D 프린팅용이어서 두 대의 프린터로 제작했습니다. 이동 플랫폼은 Fusion 360으로 CNC 라우터 툴패스를 생성한 후 합판에서 디스크를 잘라 만들었습니다.

조립

모든 부품 제작 후 조립을 시작했습니다. 베어링 서포트를 회전 디스크에 연결했습니다. 6mm 알루미늄 로드와 나사산 로드를 부품을 통해 통과시켜 리니어 피치 어셈블리를 조립했습니다.

마지막으로 강철 로드와 알루미늄 익스트루전으로 만든 두 개의 포스트로 너프건을 부착했습니다.

프로그래밍

이제 프로젝트에서 가장 어려운 부분인 프로그래밍입니다. 발사체 발사 머신은 매우 복잡하고 그 뒤의 수학도 혼란스러울 수 있습니다. 프로그램 흐름과 로직을 단계별로 작성하여 각 머신 상태에서 일어날 일을 자세히 기술했습니다. 상태는 다음과 같습니다:

타겟 획득은 먼저 Pixy를 네온 핑크 물체 추적용으로 설정하는 것으로 시작합니다. 그런 다음 타겟이 Pixy 화면 중앙에 올 때까지 건을 움직입니다. 이때 건 배럴에서 타겟까지 거리를 측정합니다. 이 거리를 이용해 기본 삼각함수로 수평/수직 거리를 구합니다. 코드에는 이 두 거리를 이용해 타겟을 맞출 데 필요한 각도를 계산하는 get_angle() 함수가 있습니다.

건이 이 위치로 이동하고 MOSFET을 통해 모터를 켭니다. 5초간 스풀업 후 서보 모터를 움직여 트리거를 당깁니다. MOSFET이 모터를 끄고 너프건은 다시 타겟 탐색 상태로 돌아갑니다.

테스트 및 즐기기

네온 핑크 인덱스 카드를 벽에 붙여 건 정확도를 테스트했습니다. 프로그램이 측정된 거리에 따라 각도를 보정하고 조정하여 잘 작동했습니다. 작동 영상은 다음과 같습니다.

코드

회로도 (C/C++)

Arduino Mega에 업로드

#include <math.h> 
#include <Arduino.h> 
#include "BasicStepperDriver.h" 
#include <Servo.h> 
#include <Pixy2I2C.h> 
// X는 피치, Y는 요
const int pins[] = {6,7,8,5,4,12}; // MX STEP, DIR, EN, MY STEP, DIR, EN
const int limit_switch = 26, laser_pin = 11, spool_pin = 10, servo_pin = 13, distance_trig = 29, distance_echo = 30;
double velocity = 21.336;
double velocity_squared = 455.225;
float current_angle = 0.0;
float hyp_distance; // 건에서 타겟까지 거리(m)
#define X_MID 164
#define Y_MID 150
#define DEADZONE 15
#define G 9.8
#define STP_PER_DEG_YAW 3.333
#define STP_PER_DEG_PITCH 184859
#define MICROSTEPS 32
#define RPM 120
#define MOTOR_STEPS_Y 200
#define MOTOR_STEPS_X 1036 // 17.7777 steps / degree

BasicStepperDriver pitch_stepper(MOTOR_STEPS_X, pins[1], pins[0]);
BasicStepperDriver yaw_stepper(MOTOR_STEPS_X, pins[4], pins[3]);
Servo trigger;
Pixy2I2C pixy;

enum States { ACQUIRE, POSITION, SPOOL, FIRE, WIND_DOWN, RETURN };
States state = ACQUIRE;

void setup() {
  Serial.begin(115200);
  init_pins();
  delay(1000);
  // home_pitch();
  pixy.init();
  Serial.println("Ready...");
}

void loop() {
  switch(state){
    case ACQUIRE:
      acquire_target();
      state = POSITION;
      digitalWrite(laser_pin,HIGH);
      break;
    case POSITION:
      Serial.println("positioning");
      position_gun();
      state = SPOOL;
      break;
    case SPOOL:
      Serial.println("spooling");
      digitalWrite(spool_pin,HIGH);
      delay(5000);
      state = FIRE;
      break;
    case FIRE:
      fire_gun();
      state = WIND_DOWN;
      break;
    case WIND_DOWN:
      Serial.println("winding down");
      digitalWrite(spool_pin,LOW);
      delay(2000);
      state = RETURN;
      digitalWrite(laser_pin,LOW);
      state = ACQUIRE;
      break;
  }
}

void fire_gun(){
  Serial.println("Firing gun!");
  trigger.write(108);
  delay(400);
  trigger.write(90);
  delay(2000);
}

void position_gun(){
  float x, y;
  hyp_distance = ping();
  hyp_distance /= 100;
  while(!hyp_distance){
    hyp_distance = ping();
    hyp_distance /= 100;
  }
  Serial.println(hyp_distance);
  x = cos(current_angle) * hyp_distance;
  y = sin(current_angle) * hyp_distance;
  float target_angle = get_angle(x,y);
  target_angle /= 100;
  Serial.println(target_angle);
  move_pitch(target_angle - current_angle);
  current_angle = target_angle;
}

void acquire_target(){
  int x=0, y=0;
  long steps_taken=0;
  bool lock = false;
  while(!lock){
    pixy.ccc.getBlocks();
    if(pixy.ccc.numBlocks){
      x = pixy.ccc.blocks[0].m_x;
      y = pixy.ccc.blocks[0].m_y;
      Serial.print("Target seen at location X: ");
      Serial.print(x);
      Serial.print(", Y: ");
      Serial.println(y);
      if(x <= (X_MID - DEADZONE)){
        // 너무 왼쪽에 있으면 건을 왼쪽으로 이동
        move_yaw(1);
      } else if(x >= (X_MID + DEADZONE)){
        move_yaw(-1);
      } else if(y <= (Y_MID - DEADZONE)){
        // 너무 위에 있으면 건을 위로
        pitch_stepper.move(33152);
        steps_taken += 33152;
      } else if(y >= (Y_MID + DEADZONE)){
        pitch_stepper.move(33152);
        steps_taken += 33152;
      } else{
        lock = true;
        Serial.print("Target locked at location X: ");
        Serial.print(x);
        Serial.print(", Y: ");
        Serial.println(y);
        Serial.print("Steps taken: ");
        Serial.println(steps_taken);
      }
    }
  }
  current_angle = steps_taken / STP_PER_DEG_PITCH;
  Serial.print("Current angle: ");
  Serial.println(current_angle);
}

void init_pins(){
  pinMode(pins[2],OUTPUT);
  pinMode(pins[5],OUTPUT);
  pinMode(limit_switch, INPUT_PULLUP);
  pinMode(laser_pin, OUTPUT);
  pinMode(spool_pin, OUTPUT);
  pinMode(distance_echo, INPUT);
  pinMode(distance_trig, OUTPUT);
  digitalWrite(pins[2],LOW);
  digitalWrite(pins[5],LOW);
  digitalWrite(laser_pin,LOW);
  digitalWrite(spool_pin,LOW);
  trigger.attach(servo_pin);
  pitch_stepper.begin(RPM, MICROSTEPS);
  yaw_stepper.begin(5, MICROSTEPS);
  trigger.write(90);
}

void move_yaw(float degrees){
  yaw_stepper.move(degrees*STP_PER_DEG_YAW*32);
}

void move_pitch(float degrees){
  current_angle += degrees;
  pitch_stepper.move(degrees*STP_PER_DEG_PITCH);
}

float get_angle(float distance, float height){
  float i = 2 * height * 455.225;
  float j = G * distance * distance;
  i += j;
  j = 9.8 * i;
  i = sqrt(pow(velocity_squared,2) - j);
  return atan((velocity_squared-i) / (G * distance))*(180/PI);
}

float ping(){
  Serial.println("Getting distance...");
  long duration;
  digitalWrite(distance_trig, LOW);
  delayMicroseconds(5);
  digitalWrite(distance_trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(distance_trig, LOW);
  duration = pulseIn(distance_echo, HIGH);
  return duration / 2 / 29.1; // 거리(m)
}

void home_pitch(){
  Serial.println(digitalRead(limit_switch));
  if(!digitalRead(limit_switch)){
    // 스위치 활성화 시
    pitch_stepper.rotate(720);
  }
  while(digitalRead(limit_switch)){
    pitch_stepper.move(-32);
  }
  pitch_stepper.rotate(2880*2);
}

커스텀 파트 및 인클로저

Thingiverse 저장소: CAD 파일

회로도

회로도 이미지 참조

제조공정

  1. 스테퍼 모터: 유형, 특성 및 실용적 응용 분야 완벽 가이드
  2. 통합 드라이버로 고전력 스테퍼 모터 설계 단순화
  3. Raspoulette: 태양광 자율 닭장 문 시스템 프로토타입
  4. 심플 파이 로봇 – 라즈베리파이로 2WD 자율 로버 만들기
  5. 아두이노용 양극성 스테퍼 모터 종합 라이브러리
  6. 아두이노 우노 스테퍼 모터로 만드는 틴더/범블 자동 스와이퍼
  7. 자율 홈 어시스턴트 로봇 'O'nine' 제작 – ROS, Alexa, MoveIt, 내비게이션 완전 가이드
  8. 서보 vs 스테퍼 모터: 현대적 회색 지대 명확히 하기
  9. 리니어 모터 이해: 전기 에너지에서 정밀 직선 운동으로
  10. 서보 모터 설명: 작동 원리와 중요성