Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

DIY автономная турель Nerf — умный Nerf-пушка на шаговых двигателях, камере и Arduino

Компоненты и материалы

КомпонентКоличество
Шаговый двигатель NEMA 17 (OpenBuilds)1
Шаговый двигатель с редуктором (DFRobot)1
Драйвер шагового двигателя (DFRobot DRV8825 dual)1
Камера Pixy 2 (DFRobot)1
Arduino Mega 25601
Ультразвуковой датчик расстояния HC-SR041
Nerf Nitron (пушка)1

Необходимые инструменты и станки

Программное обеспечение и сервисы

О проекте

Идея

Несколько лет назад я видел проект полуавтономной турели, способной стрелять после наведения. Это вдохновило использовать камеру Pixy 2 для захвата целей и автоматического наведения Nerf-пушки, который мог бы самостоятельно захватывать, наводиться и стрелять.

Компоненты

Пушке нужны «глаза» — выбрал Pixy 2 за лёгкую интеграцию с основной платой. Микроконтроллер — Arduino Mega 2560 за большое количество пинов. Две оси (курс и тангаж) требуют два шаговых двигателя. DFRobot предоставили двухканальный драйвер DRV8825.

CAD-моделирование

Начало в Fusion 360: загрузка эскиза пушки как прикреплённого холста, построение твердого тела. Затем платформа с опорами на подшипниках для вращения влево-вправо, шаговый двигатель рядом для привода. Для тангажа потребовалась линейная система: одна точка на подвижном блоке, вторая — на тыле пушки, соединяемые стержнем, позволяющим пушке качаться вокруг центральной оси.

Изготовление деталей

Почти все детали — для 3D-печати, изготовил на двух принтерах. Подвижную платформу фрезеровал на CNC: сгенерировал траектории в Fusion 360, выфрезеровал диск из фанеры.

Сборка

После печати — сборка. Подключение подшипниковых опор к вращающемуся диску. Сборка линейного тангажа: прокладка 6-мм алюминиевых валов и трапецеидального шпинделя через детали. Установка самой пушки на стальной стержень и две стойки из алюминиевого профиля.

Программирование

Самая сложная часть. Пушка, стреляющая снарядами — сложная система, математика запутанна. Начал с пошаговой логики состояний автомата:

  1. Захват цели (Acquire target)
  2. Наведение пушки (Position the gun)
  3. Разгон моторов (Spool up)
  4. Выстрел (Fire)
  5. Останов моторов (Wind down)

Захват цели: настройка 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; // расстояние от ствола к цели в метрах

#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 шаг/градус

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; // расстояние в метрах
}

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);
}

CAD-файлы и корпусы

Репозиторий на Thingiverse: CAD файлы проекта

Схемы подключения

Схема подключения доступна в исходном проекте (изображение схемы).

Производственный процесс

  1. Шаговые двигатели: типы, характеристики и практические применения
  2. Интегрированные драйверы упрощают дизайн высокомощных шаговых двигателей
  3. Raspoulette: Прототип солнечной автономной системы двери курятника
  4. Simple Pi Robot — Сборка 2WD автономного ровера на Raspberry Pi
  5. Полнофункциональная библиотека для управления биполярным шаговым двигателем на Arduino
  6. Автоматизированный свайпер для Tinder и Bumble на Arduino UNO и шаговом двигателе
  7. Автономный домашний помощник-робот: полный список деталей и руководство по настройке (ROS, Alexa, MoveIt)
  8. Сервоприводы vs Шаговые двигатели: проясняем современную серую зону
  9. Понимание линейных моторов: от электрической энергии к точному линейному движению
  10. Серводвигатели: принцип работы и значение