Zelfbouw autonom Nerf Sentry-turret – Slimme Nerf-pistool met stappenmotoren, camera en Arduino
Onderdelen en benodigdheden
| Onderdeel | Aantal |
|---|---|
| OpenBuilds NEMA 17 Stappenmotor | 1 |
| DFRobot Stappenmotor met tandwielkast | 1 |
| DFRobot Stappenmotor Driver (DRV8825 dual board) | 1 |
| DFRobot Pixy 2 Camera | 1 |
| Arduino Mega 2560 | 1 |
| Ultrasonische sensor HC-SR04 | 1 |
| Nerf Nitron pistool | 1 |
Benodigde tools en machines
- 3D-printer (generiek)
- Soldeerbout (generiek)
- CNC-freesmachine
Apps en online diensten
- Arduino IDE
- Autodesk Fusion 360
Over dit project
Idee
Een paar jaar geleden zag ik een project van een semi-autonome turret die zelf kon vuuren na aansturing. Dat gaf me het idee om een Pixy 2-camera te gebruiken voor doelexherkenning en de Nerf-pistool automatisch te richten, wat vervolgens zelfstandig kan vergrendelen en vuren.
De componenten
De pistol had 'ogen' nodig, dus koos ik de Pixy 2 vanwege de eenvoudige interfacing met de hoofdbord. Als microcontroller gekozen voor Arduino Mega 2560 vanwege de veelheid aan pinnen. Voor twee assen (gieren en kantelen) zijn twee stappenmotoren nodig; DFRobot leverde hun dubbele DRV8825 motor driver bord.
CAD-ontwerp
Begon in Fusion 360 met een canvas van de Nerf-pistool, maakte een vast lichaam. Daarna een platform met lagerondersteuningen voor links/rechts rotatie. Een stappenmotor naast het platform om dit aan te drijven. Voor op/neer kanteling (pitch) werd een lineaire aandrijving ontworpen: een punt op het bewegende blok, een punt aan de achterkant van de pistool, verbonden door een staaf zodat de pistool om zijn centrale as kan kantelen.
Fabricatie van de onderdelen
Bijna alle onderdelen 3D-geprint (twee printers). Het bewegende platform gefabriceerd met CNC-freesmachine: toolpaths gegenereerd in Fusion 360, schijf uitgezaagd uit multiplex.
Montage
Na fabricatie: montagelagerondersteuningen op draaiende schijf. Lineaire pitch-assemblage: 6 mm aluminium stangen en draadstang door de stukken. Tenslotte de Nerf-pistool bevestigen met een staalstang en twee pijlen van aluminium profielen.
Programmeren
Meest uitdagende deel: programmeren. Een projectiel-vurende machine is complex, de wiskunde erachter kan verwarrend. Begon met programma-stroom en logica stap voor stap, met toestanden:
- Doel verwerven (acquire target)
- Pistool positioneren
- Motoren op spoelen (spool up)
- Vuren
- Motoren afspoelen (wind down)
- Terugkeren naar zoekmodus
Doelverwerving: Pixy ingesteld op neon roze objecten als doelen. Pistool beweegt tot doel gecentreerd in Pixy-beeld; afstand van loop naar doel gemeten. Met deze afstand horizontale en verticale afstand berekend via basis trigonometrie. Functie get_angle() berekent benodigde hoek om doel te raken.
Pistool beweegt naar positie, zet motoren aan via MOSFET. Na 5 seconden op spoelen beweegt servomotor de trekker. MOSFET schakelt motoren uit, pistool keert terug naar zoekmodus.
Plezier beleven
Neon roze indexkaart aan muur als doelteken. Programma calibreert en corrigeert hoek voor gemeten afstand. Werkt goed. Video demonstratie beschikbaar.
Code
Schematisch (C/C++)
Upload naar Arduino Mega
#include <math.h>
#include <Arduino.h>
#include "BasicStepperDriver.h"
#include <Servo.h>
#include <Pixy2I2C.h>
// X is pitch, Y is yaw
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; // afstand van pistool tot doel in meters
#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 stappen / graad
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; // afstand in meters
}
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);
}
Aangepaste onderdelen en behuizingen
Thingiverse Repository: CAD-bestanden op thingiverse.com
Schema's
Schema-afbeelding beschikbaar in origineel.
Productieproces
- Raspoulette: Zonnestroom-gevoede Autonome Koeienpoort Systeem Prototype
- Simple Pi Robot – Bouw een 2-WD Autonome Rover met Raspberry Pi
- Uitgebreide bibliotheek voor bipolaire stappenmotoren met Arduino
- Geautomatiseerde Tinder & Bumble Swiper met Arduino UNO en Stappenmotor
- Bouw een Autonome Thuisassistent Robot: Complete Onderdelenlijst & Opzetgids
- SmartWay: Complete kit voor trilmotor, RGB LED, GPS-module & Arduino MKR Fox 1200 projecten
- Lineaire motoren begrijpen: Van elektrische energie naar nauwkeurige lineaire beweging
- Servomotoren Uitgelegd: Hoe Ze Werken en Waarom Ze Belangrijk Zijn
- Servo vs Stappenmotors: De Moderne Grijze Zone Verhelderd
- Stapelmotoren: typen, kenmerken en praktische toepassingen
- Geïntegreerde drivers vereenvoudigen high-power steppermotor-ontwerp