Donanım Bileşenleri
Yazılım Uygulamaları
El Aletleri ve İmalat Makineleri
Oyun
Oyunun kuralları oldukça basit. Lazer ile rakip tankı vurun (4 sensörden birine lazer tutmalısınız). Her sensör bir mürettebat üyesine karşılık gelir: nişancı, sürücü, komutan ve makineli tüfek. Mürettebatın 4'de 3'ü vurulursa kaybedersiniz. Bir nişancı veya şoför vurulursa, yeterli mürettebatınız varsa bunlar değiştirilebilir. Vurulma durumları için cezalar mevcut. Nişancı vurulursa, tank 6 saniye ateşleyemez ve yeniden yükleme süresi artar. Ayrıca, sürücü vurulursa, tankın hızını azaltır ve 6 saniye boyunca hareketi önler. Bu oyun kuralları War Thunder video oyunu na dayanmaktadır.
Tank Şasesi Yapımı
Şasi, braket eklemenize izin veren birçok montaj deliğine sahiptir. İki motor ve tekerleğin yanı sıra güç eklemek için iki set tekerleği ile birlikte geliyor. Montaj kolay ve basittir. Tek yapmanız gereken, her motora ve tekerlek topuna 2 vida takmak.
Sonra, 3 mm vida ve somunu kullanarak kasaya vidalanan 8 çubuk destekçiyi 3D yazıcıda yazdırdım. Her çift, bir pirinç çubuğun bunları kapsamasına izin vererek şasi boyunca sıralanır.
Tankların Tasarımı
Fusion 360 ile tank gövdelerini tasarladım. Sonra temel katı modelin etrafında parçalar tasarladım. Bu işlem, M4A3E8 Sherman Tank'ı oluşturmak için bir kez daha yapıldı.
Tank Elektroniği Bölüm 1: Motorlar ve Beyinler
Tankları oluşturmanın en zorlu adımı elektronik bölümdür.
Raspberry Pi, motorları sürmek için gerekli akımı ve voltajı sağlayamaz, bu nedenle bir motor sürücüsü gereklidir. Raspberry Pi sadece 4 pim kullandığı için boyutu ve sadeliği ön planda tutarak L293d H-köprü sürücüsünü seçtim. Bu sürücü, motorlara 1 amper 12v'ye kadar güç sağlayabilir ve mükemmel bir seçimdir.
İkincisi, onlara çarpan ışık miktarına göre bir değer veren 4 LDR'yi (değişken ışık dirençleri) ölçmek için bir yola ihtiyacım vardı. Düşük miktarda ışık direnci arttırır ve yüksek miktarda ışık da azaltarak dijital olarak okunmasını imkansız hale getirir. Raspberry Pi'nin ADC girişi yok, bu yüzden bunu yapabilen bir çip bulmak zorunda kaldım. ADS1115, I2C üzerinden iletişim kuran şaşırtıcı, küçük, 4 kanallı bir analogdan dijitale dönüştürücüdür ve sadece birkaç kablo kullanır. Bu da birkaç değerli GPIO pimini boşa çıkarır. ADS1115, Adafruit tarafından yapılan ve dört kanalın her birinin ayarlanmasına ve okunmasına izin veren bir Python kütüphanesine de sahiptir. İsteğe bağlı olarak, ses efektleri istiyorsanız bir lm386n ekleyebilirsiniz. Sadece IN 18 pinini ve VCC'yi + 5v'ye bağlayın.
Tank Elektroniği Bölüm 2: Güç ve Servolar
Aklımda “Buna nasıl güç verebilirim?” sorusu vardı. 2 servo, bir Raspberry Pi Zero, bir ADC, bir lazer diyot ve 2 motor için güç sağlamak zor iş. İlk önce her bileşeni voltaj gereksinimlerine ayırdım. Pi, lazer ve servolar için 5V; ADC ve servo sürücü için 3.3V; Motorlar için 6 ~ 12V.
5V regülatörler, tüm 5V bileşenlerine güç sağlamak için yeterli akım (3 amper) sağlayabilir. Bir güç kaynağı için, 30C 11.7V sağlayabilen ve 1800mAh kapasiteye sahip genel bir RC LiPo pil kullanmayı seçtim.
Artık güç ve güç dağıtımı yapıldığından servo sürüşüne geçebiliriz. İlk başta servoları doğrudan Pi'nin GPIO pinlerinden kontrol etmek için GPIO PWM sınıfını kullanmaya çalıştım, ancak bir mikroişlemci özel bir çipin aksine gerekli tam zamanlamaları sağlayamıyor. Bu yüzden aramaya gittim ve sonunda bir I2C 16 kanal servo sürücüsü olan PCA9685'e, tam da ihtiyacım olana rastladım. Ayrıca I2C protokolünü kullandığından, ADS1115'e kolayca bağlayabilirim.
Adafruit ayrıca her kanalı belirli bir nabızla kontrol eden yararlı bir kütüphane sağlar. ADS1115 kütüphanesini burada bulabilirsiniz: PYTHON ADS1x15 PCA9685 kütüphanesini burada bulabilirsiniz: PYTHON PCA9685
Programlama
Programlara ihtiyaç duyan iki pano vardır. Biri Python kullanan bir Raspberry Pi, diğeri C ++ kullanan bir Arduino Nano. Python kodu için, motor ve servo kurulumunu işleyen, seri veri alan ve tankın tüm elektroniklerini kontrol eden bir sınıf oluşturdum. Geriye kalan tek şey GPIO pin numaralarını girip seri arabelleği güncelleyen bir ana dosya oluşturmaktı. Denetleyicinin beyni, bir Arduino Nano, dört analog giriş alır, I2C verileri gönderir ve 4 LED'i kontrol eder.
Yeniden yükleme hızlarını ve lazer ateşleme süresini kontrol etmenin yanı sıra analog verilerin ne sıklıkta örnekleneceğini kontrol etmek için Arduino Zamanlayıcı kitaplığını kullanır. Daha fazlasını görmek için ekteki kodu görüntüleyin.
Kurulum
Her tankı ve denetleyiciyi titizlikle inşa ettikten sonra, nihayet onları bir oyunda kullanma zamanı geldi. Önce her tankı ilgili denetleyiciyle eşleştirmek zorunda kaldım. Bunu yapmak için, bir denetleyiciyi açtım ve daha sonra
bluetoothctl yazdıktan sonra
default-agent yazdım ve ardından denetleyici HC-05 olarak göründükten sonra
scan yazdım ve
MAC_ADDR çiftine girdim; burada MAC_ADDR HC-05 modülü için ortaya çıkan MAC adresidir. PIN'in eşleşmesini isteyecektir. Şifre 1234'tür. Şimdi sadece
exit yazın ve ardından programı önce
sudo rfcomm bind / dev / rfcomm1 MAC_ADDR yazarak çalıştırın. Bu, Bluetooth bağlantı noktasını bir seri bağlantı noktasına bağlar ve
pyserial kütüphanesinin HC-05'ten veri okumasına izin verir.
Oynanış
Ve bu kadar! Bu eğlenceli lazer oyunu için hepinize teşekkür etmek istiyorum. Eğlenceli bir makale oldu.
Özel Parçalar ve Muhafazalar
Thingiverse Bağlantısı
from __future__ import division
import RPi.GPIO as GPIO
from time import sleep
from serial import Serial
import json
import Adafruit_PCA9685
class Car(object):
def __init__(self, BT_Port, laser_pin):
GPIO.setmode(GPIO.BCM)
self.ser = Serial(BT_Port, baudrate=9600,timeout=0)
self.laser_pin = laser_pin
self.laser_state = False
GPIO.setup(self.laser_pin, GPIO.OUT)
def configMotors(self, mL1, mL2, mR1, mR2, invertLR=False):
self.motor_array = [mL1, mL2, mR1, mR2]
self.invert = invertLR
for pin in self.motor_array:
GPIO.setup(pin, GPIO.OUT)
#self.motor_pwm.append(GPIO.PWM(pin, 490))
self.motorL1_pwm = GPIO.PWM(mL1, 100)
self.motorL2_pwm = GPIO.PWM(mL2, 100)
self.motorR1_pwm = GPIO.PWM(mR1, 100)
self.motorR2_pwm = GPIO.PWM(mR2, 100)
self.motorL1_pwm.start(0)
self.motorL2_pwm.start(0)
self.motorR1_pwm.start(0)
self.motorR2_pwm.start(0)
def configServos(self):
self.pwm = Adafruit_PCA9685.PCA9685()
self.servo_min = 150
self.servo_max = 600
self.pwm.set_pwm_freq(60)
def deg_to_pulse(self, degree):
pulse = (degree - 0) * (self.servo_max - self.servo_min) / (180 - 0) + 150
print('Pulse: '+str(pulse))
return int(pulse)
def receive_command(self):
self.cmd = ""
if self.ser.inWaiting():
self.cmd = self.ser.readline()
if self.cmd != None:
try:
data = self.cmd.decode('utf-8')
data = json.loads(data.strip())
print data
if data != "":
return data
else:
return ""
except ValueError:
pass
#except IOError:
#pass
def turn_wheel(self, motor1, motor2):
if self.invert:
temp = motor1
motor1 = motor2
motor2 = temp
motor1 = (motor1 / 2)
motor2 = (motor2 / 2)
if motor1<0:
self.motorL2_pwm.ChangeDutyCycle(motor1*-1)
self.motorL1_pwm.ChangeDutyCycle(0)#make positive if negative
else:
self.motorL1_pwm.ChangeDutyCycle(motor1)
self.motorL2_pwm.ChangeDutyCycle(0)
if motor2<0: self.motorR2_pwm.ChangeDutyCycle(motor2*-1) self.motorR1_pwm.ChangeDutyCycle(0)#make positive if negative else: self.motorR1_pwm.ChangeDutyCycle(motor2) self.motorR2_pwm.ChangeDutyCycle(0) def move_turret(self, turret_deg, barrel_deg): self.pwm.set_pwm(0, 0, self.deg_to_pulse(turret_deg)) self.pwm.set_pwm(1, 0, self.deg_to_pulse(barrel_deg)) def fire_laser(self, state): GPIO.output(self.laser_pin, state) def update(self): #Run to make the robot work self.json_obj = self.receive_command() print self.json_obj try: if "motor" in self.cmd: if self.json_obj["motor"] != None: val1 = self.json_obj["motor"][0] # if val1 > 0:
# val1 += 10
val2 = self.json_obj["motor"][1]
# if val2 <0:
# val2 += 4
self.turn_wheel(val1, val2)
elif "shoot" in self.cmd:
self.laser_state = self.json_obj["shoot"]
self.fire_laser(self.json_obj["shoot"])
elif "turret" in self.cmd:
val1 = self.json_obj["turret"][0]
val2 = self.json_obj["turret"][1]
self.move_turret(val1, val2)
except TypeError:
pass
sleep(.01)
import car
bt_port = "/dev/rfcomm3"
laser_pin = 20
rc_car = car.Car(bt_port, laser_pin)
rc_car.configMotors(6,5,19,13, invertLR=True)
rc_car.configServos() #Turret drive servo, barrel servo
while 1:
rc_car.update()
#include "Timer.h"
#include
#include
int JS_VALS[2] = {};
int JS_VALS_R[2] = {};
const int JS_PINS[4] = {A0, A1, A2, A3}; //L_X, L_Y, R_X, R_Y
const int led_pins[4] = {4,5,6,7};
const int button_pin = 3;
const int deadzone = 8;
bool laser = false;
const int servo_limits[5] = {0, 180, 50, 95, 75}; //Left, Right, Lower, Upper, Middle limits for the turret
int turret_positions[2] = {90, servo_limits[4]}; //Initial positions
unsigned long last_time = 0;
long motor_delay = 30; //Delay 30 ms between motor control polls
long turret_delay = 150; //Delay 150 ms between turret control polls
long reloadSpeed = 8000;
float driveSpeed = 1.0;
int crewValues[4] = {3,3,2,1};
bool crewLeft[4] = {true,true,true,true};
int crewLeftNum = 4;
int reloadTimer, crewReplaceTimer, fireTimer;
bool is_reloaded= true;
bool can_move = true;
bool can_fire = true;
LiquidCrystal_I2C lcd(0x3F, 16, 2);
Timer t;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.print("Begin fighting!");
delay(2000);
lcd.clear();
lcd.home();
pinMode(button_pin, INPUT_PULLUP);
Serial.println("Begin");
t.every(motor_delay, control_motors);
t.every(turret_delay, control_turret);
t.every(50, checkButton);
for(int i=0;i<4;i++){
pinMode(led_pins[i], OUTPUT);
}
updateCrew();
}
void loop() {
/*control_motors();
control_turret();
delay(150);*/
t.update();
if(Serial.available()>0){
String data = Serial.readStringUntil('\n');
if(data=="HG"){
crewLeft[0] = false;
crewLeftNum -= 1;
reloadSpeed += 2000;
can_fire = false;
crewReplaceTimer = t.after(6000, replaceCrew);
}
else if(data=="HD"){
crewLeft[1] = false;
crewLeftNum -= 1;
driveSpeed -= .2;
can_move = false;
crewReplaceTimer = t.after(6000, replaceCrew);
}
else if(data=="HC"){
crewLeft[2] = false;
crewLeftNum -= 1;
driveSpeed -= .1;
reloadSpeed += 1500;
crewReplaceTimer = t.after(6000, replaceCrew);
}
else if(data=="HM"){
crewLeft[3] = false;
crewLeftNum -= 1;
crewReplaceTimer = t.after(6000, replaceCrew);
}
updateCrew();
}
if(crewLeftNum<=1){
lcd_clear();
lcd.print("You lose");
while(1);
}
}
void checkButton(){
if(digitalRead(button_pin)==0){
fire_laser();
}
}
void replaceCrew(){
if(crewLeft[0]==false){
if(crewLeft[2]==true){
crewLeft[2] = false;
crewLeft[0] = true;
can_fire = true;
}
else if(crewLeft[3]==true){
crewLeft[3] = false;
crewLeft[0] = true;
can_fire = true;
}
}
else if(crewLeft[1]==false){
if(crewLeft[2]==true){
crewLeft[2] = false;
crewLeft[1] = true;
can_move = true;
}
else if(crewLeft[3]==true){
crewLeft[3] = false;
crewLeft[1] = true;
can_move = true;
}
}
updateCrew();
t.stop(crewReplaceTimer);
}
void updateCrew(){
for(int i=0;i<4;i++){
digitalWrite(led_pins[i],crewLeft[i]);
}
}
void lcd_clear(){
lcd.clear();
lcd.home();
}
void fire_laser(){
if(is_reloaded){
if(can_fire){
lcd.clear();
lcd.print("Firing laser");
is_reloaded = false;
String json_string = "{\"motor\":[" + String(0)+","+String(0)+"]}"; //Stop tank before firing
Serial.println(json_string);
laser = true;
fireTimer = t.after(2000, stopLaser);
}
}
}
void stopLaser(){
t.stop(fireTimer);
laser = false;
lcd_clear();
lcd.print("Reloading");
reloadTimer = t.after(reloadSpeed, reloadGun);
}
void reloadGun(){
lcd_clear();
lcd.print("Reloaded");
is_reloaded = true;
t.stop(reloadTimer);
}
void control_motors(){
JS_VALS[0] = analogRead(JS_PINS[0]);
JS_VALS[1] = analogRead(JS_PINS[1]);
int x = map(JS_VALS[0], 0, 1023, -50, 50);
x = constrain(x, -100, 100);
int y = map(JS_VALS[1], 0, 1023, -50, 50);
y = constrain(y, -100, 100);
int motor1 = x+y;
int motor2 = y-x;
motor1 = constrain(motor1, -100, 100);
motor2 = constrain(motor2, -100, 100);
if(abs(motor1) <= deadzone){
motor1 = 0;
}
if(abs(motor2) <= deadzone){
motor2 = 0;
}
motor1 = int(float(motor1)*driveSpeed);
motor2 = int(float(motor2)*driveSpeed);
if(can_move==false){
motor1 = 0;
motor2 = 0;
}
String json_string = "{\"motor\":[" + String(motor1)+","+String(motor2)+"]}";
Serial.println(json_string);
String json_string2;
if(laser){
json_string2 = "{\"shoot\":1}";
Serial.println(json_string2);
}
else if(!laser){
json_string2 = "{\"shoot\":0}";
Serial.println(json_string2);
}
}
void control_turret(){
JS_VALS_R[0] = analogRead(JS_PINS[2]); //Get X values
JS_VALS_R[1] = analogRead(JS_PINS[3]); //Get Y values
int x = map(JS_VALS_R[0]+10, 0, 1023, 5, -5);
x = constrain(x, -5, 5); //Catch outliers
int y = map(JS_VALS_R[1], 0, 1023, 2, -2);
y = constrain(y, -2, 2);
turret_positions[0] += x;
turret_positions[1] += y;
if(turret_positions[0] <= servo_limits[0]){
turret_positions[0] = servo_limits[0];
}
else if(turret_positions[0] >= servo_limits[1]){
turret_positions[0] = servo_limits[1];
}
if(turret_positions[1] <= servo_limits[2]){
turret_positions[1] = servo_limits[2];
}
else if(turret_positions[1] >= servo_limits[3]){
turret_positions[1] = servo_limits[3];
}
String json_string = "{\"turret\":[" + String(turret_positions[0])+","+String(turret_positions[1])+"]}";
Serial.println(json_string);