• Hallo Zusammen, Aufgrund der aktuellen Situation setzten wir die Möglichkeit aus, sich mit Gmail zu registrieren. Wir bitten um Verständnis Das RCMP Team

Projekt Arduino Panzersteuerung

hugentobler

Mitglied
Hallo liebe Community

Ich bin Hans aus der Schweiz. Ich bin 42 Jahre alt und habe alle möglichen Hobbys. Jetzt habe ich mit dem 3D-Drucker einen Panzer gebaut und möchte auch die Steuerung selber machen. Ich habe Dans Transmitter nachgebaut. https://howtomechatronics.com/projects/diy-arduino-rc-transmitter/

Als Empfänger habe ich ein Arduino Uno oder ein Arduino Mega zur Verfügung. Dan hat auch alle möglichen Beispiele auf seiner Seite, wie diesen Empfänger hier z.B. https://howtomechatronics.com/projects/diy-arduino-rc-receiver/

Sender und Empfänger funktionieren. Ein Auto kann ich fahren und lenken.

Wie gesagt, ich möchte aber auch meinen Panzer über den Uno oder Mega mit dem oben genannten Sender steuern.

Aber ich bin nicht so vertraut, dass ich meine komplizierte Idee umsetzen kann.

Eins vorweg, ich will das programmieren auch lernen und können. Aber ich kanns nicht von heute auf morgen. Ich verstehe mitlerweile ein paar Dinge aber dennoch ist es zu komplex (im Moment) um mein vorhaben ohne Hilfe weiter zu treiben.

Das Meiste vom Code ist vorhanden aber es geht noch ums komplettieren. Ich habe mich dafür extra bei Arduino Cracks in zwei Foren angemeldet und um Hilfe gefragt. Aber ich wurde beinahe gelyncht. Von ich solle zuerst programmieren lernen, es gebe keine Abkürzung bis zu Lebensbelehrungen usw..

Vielleicht stosse ich hier bei gleichgesinnten vielleicht auf etwas Verständnis.

Entschuldigt den kleinen Ausschweifer.

Wie gesagt es würde darum gehen, den Receiver Code zu kompletieren.

Der Sendercode lautet wie folgt:

Code:
/*
        DIY Arduino based RC Transmitter
  by Dejan Nedelkovski, www.HowToMechatronics.com
  Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>

// Define the digital inputs
#define jB1 1  // Joystick button 1
#define jB2 0  // Joystick button 2
#define t1 7   // Toggle switch 1
#define t2 4   // Toggle switch 1
#define b1 8   // Button 1
#define b2 9   // Button 2
#define b3 2   // Button 3
#define b4 3   // Button 4

const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY;
float angleX, angleY;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY;
float elapsedTime, currentTime, previousTime;
int c = 0;

RF24 radio(5, 6);   // nRF24L01 (CE, CSN)
const byte address[6] = "00001"; // Address

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};
Data_Package data; //Create a variable with the above structure
void setup() {
  Serial.begin(9600);
 
  // Initialize interface to the MPU6050
  initialize_MPU6050();
  // Call this function if you need to get the IMU error values for your module
  //calculate_IMU_error();
 
  // Define the radio communication
  radio.begin();
  radio.openWritingPipe(address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
 
  // Activate the Arduino internal pull-up resistors
  pinMode(jB1, INPUT_PULLUP);
  pinMode(jB2, INPUT_PULLUP);
  pinMode(t1, INPUT_PULLUP);
  pinMode(t2, INPUT_PULLUP);
  pinMode(b1, INPUT_PULLUP);
  pinMode(b2, INPUT_PULLUP);
  pinMode(b3, INPUT_PULLUP);
  pinMode(b4, INPUT_PULLUP);
 
  // Set initial default values
  data.j1PotX = 127; // Values from 0 to 255. When Joystick is in resting position, the value is in the middle, or 127. We actually map the pot value from 0 to 1023 to 0 to 255 because that's one BYTE value
  data.j1PotY = 127;
  data.j2PotX = 127;
  data.j2PotY = 127;
  data.j1Button = 1;
  data.j2Button = 1;
  data.pot1 = 1;
  data.pot2 = 1;
  data.tSwitch1 = 1;
  data.tSwitch2 = 1;
  data.button1 = 1;
  data.button2 = 1;
  data.button3 = 1;
  data.button4 = 1;
}
void loop() {
 
 // Read all analog inputs and map them to one Byte value
  data.j1PotX = map(analogRead(A1), 0, 1023, 0, 255); // Convert the analog read value from 0 to 1023 into a BYTE value from 0 to 255
  data.j1PotY = map(analogRead(A0), 0, 1023, 0, 255);
  data.j2PotX = map(analogRead(A2), 0, 1023, 0, 255);
  data.j2PotY = map(analogRead(A3), 0, 1023, 0, 255);
  data.pot1 = map(analogRead(A7), 0, 1023, 0, 255);
  data.pot2 = map(analogRead(A6), 0, 1023, 0, 255);
 
 // Read all digital inputs
  data.j1Button = digitalRead(jB1);
  data.j2Button = digitalRead(jB2);
  data.tSwitch2 = digitalRead(t2);
  data.button1 = digitalRead(b1);
  data.button2 = digitalRead(b2);
  data.button3 = digitalRead(b3);
  data.button4 = digitalRead(b4);
 
 // If toggle switch 1 is switched on
  if (digitalRead(t1) == 0) {
    read_IMU();    // Use MPU6050 instead of Joystick 1 for controling left, right, forward and backward movements
  }
  // Send the whole data from the structure to the receiver
  radio.write(&data, sizeof(Data_Package));
}
void initialize_MPU6050() {
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
 
  // Configure Accelerometer
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
 
 // Configure Gyro
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000dps full scale)
  Wire.endTransmission(true);
}
void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gury data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
 
 // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 4, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 32.8);
    GyroErrorY = GyroErrorY + (GyroY / 32.8);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
}
void read_IMU() {
 
 // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
 
  //For a range of +-8g, we need to divide the raw values by 4096, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 4096.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 4096.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0; // Z-axis value
 
  // Calculating angle values using
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) + 1.15; // AccErrorX ~(-1.15) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) - 0.52; // AccErrorX ~(0.5)
 
  // === Read gyro data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000;   // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 4, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 32.8; // For a 1000dps range we have to divide first the raw value by 32.8, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 32.8;
  GyroX = GyroX + 1.85; //// GyroErrorX ~(-1.85)
  GyroY = GyroY - 0.15; // GyroErrorY ~(0.15)
 
  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = GyroX * elapsedTime;
  gyroAngleY = GyroY * elapsedTime;
 
  // Complementary filter - combine acceleromter and gyro angle values
  angleX = 0.98 * (angleX + gyroAngleX) + 0.02 * accAngleX;
  angleY = 0.98 * (angleY + gyroAngleY) + 0.02 * accAngleY;
 
  // Map the angle values from -90deg to +90 deg into values from 0 to 255, like the values we are getting from the Joystick
  data.j1PotX = map(angleX, -90, +90, 255, 0);
  data.j1PotY = map(angleY, -90, +90, 0, 255);
}

Der noch nicht komplette Receiver Code lautet:

Code:
/*
  DIY RC Receiver - Servos and Brushless motors control
  by Dejan, www.HowToMechatronics.com
  Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>

RF24 radio(3, 2);   // nRF24L01 (CE, CSN)

const byte address[6] = "00001";
unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;

Servo esc;  // create servo object to control the ESC
Servo servo1;
Servo servo2;
int escValue, servo1Value, servo2Value;

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};
Data_Package data; //Create a variable with the above structure
void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  radio.startListening(); //  Set the module as receiver
  resetData();
  esc.attach(10);   // Arduino digital pin D10 - CH9 on PCB board
  servo1.attach(4); // D4 - CH1
  servo2.attach(5); // D5 - CH2
}
void loop() {
  // Check whether we keep receving data, or we have a connection between the two modules
  currentTime = millis();
  if ( currentTime - lastReceiveTime > 1000 ) { // If current time is more then 1 second since we have recived the last data, that means we have lost connection
    resetData(); // If connection is lost, reset the data. It prevents unwanted behavior, for example if a drone jas a throttle up, if we lose connection it can keep flying away if we dont reset the function
  }
 
 // Check whether there is data to be received
  if (radio.available()) {
    radio.read(&data, sizeof(Data_Package)); // Read the whole data and store it into the 'data' structure
    lastReceiveTime = millis(); // At this moment we have received the data
  }
 
 // Controlling servos
  servo1Value = map(data.j2PotX, 0, 255, 0, 180); // Map the receiving value form 0 to 255 to 0 to 180(degrees), values used for controlling servos
  servo2Value = map(data.j2PotY, 0, 255, 0, 180);
  servo1.write(servo1Value);
  servo2.write(servo2Value);
 
 // Controlling brushless motor with ESC
  escValue = map(data.j1PotY, 127, 255, 1000, 2000); // Map the receiving value form 127 to 255 to  1000 to 2000, values used for controlling ESCs
  esc.writeMicroseconds(escValue); // Send the PWM control singal to the ESC
 
}
void resetData() {
  // Reset the values when there is no radio connection - Set initial default values
  data.j1PotX = 127;
  data.j1PotY = 127;
  data.j2PotX = 127;
  data.j2PotY = 127;
  data.j1Button = 1;
  data.j2Button = 1;
  data.pot1 = 1;
  data.pot2 = 1;
  data.tSwitch1 = 1;
  data.tSwitch2 = 1;
  data.button1 = 1;
  data.button2 = 1;
  data.button3 = 1;
  data.button4 = 1;
}
Meine Idee wäre so;

Joystick links nach oben - Tank fährt vorwärts (über Motortreiber L298N zu den Motoren)

Joystick links nach unten - Tank fährt rückwärts (über Motortreiber L298N zu den Motoren)

Joystick links nach links - Während der Fahrt lenkt der Panzer nach links, Im Stehen dreht die linke Raupe rückwärts, die rechte vorwärts (auf der Stelle drehen)

Joystick rechts nach rechts - Während der Fahrt lenkt der Panzer nach rechts, Im Stehen dreht die rechte Raupe rückwärts, die linke vorwärts (auf der Stelle drehen)

Joystick links-Druckknopf - Panzer schießt - (steuert einen Motor, eine motorisierte Feuereinheit) gleichzeitig oder etwas zeitversetzt wird der Kanonenrohr vor- und zurückgeschoben (Rückstoß) - wird von einem Servo gesteuert

Joystick rechts nach links - Turm dreht sich nach links (steuert einen Motor mit Getriebe)

Joystick rechts nach rechts - Strom dreht sich nach rechts (steuert Motor mit Gang)

Joystick rechts hoch - Panzerrohr hebt sich (steuert Servo)

Joystick rechts nach unten - Panzerrohr senkt sich (steuert Servo)

Joystick rechts - Druckknopf - Laser ein- / ausschalten

4 Drucktasten unten:
1 Licht ein- und ausschalten
1 Ton ein und aus
1 Rauchmodul ein- und ausschalten
1 Kamermodul im Turm ein- und ausschalten

linkes oder rechtes Drehpotentiometer - Lautstärke Soundmodul regeln

Kann mir hier vielleicht bitte jemand helfen?

Hans
 

hugentobler

Mitglied
Interessant

Noch nie etwas von einem Kettenmischer gehört. Wäre jetzt auch nie auf die Idee gekommen nach sowas zu suchen. Denn auf der Stelle drehen, sollte der Panzer bzw. jedes Kettenfahrzeug schon können.

Wieder was gelernt.
 
D

Deleted member 1492

Gast
Zumindest wäre das die einfachste Möglichkeit ohne große Programmierarbeiten.
Das Bauteil ist einstellbar und funktioniert sehr gut, ich selbst nutze es in einem Boot mit 2 Motoren.
 

DFENCE

Mitglied
Wenn ich mir den Code so anschauen, bezweifle ich das der Arduino genug Rechenleistung besitzt, 8Bit sind für sowas dann doch bisl arg arg wenig. Ich würde auf nen 32bit Arduino gehen, alias Arduino Due der von der Bauform und Pin belegung fast dem Mega entspricht.
 

hugentobler

Mitglied
Wenn ich mir den Code so anschauen, bezweifle ich das der Arduino genug Rechenleistung besitzt, 8Bit sind für sowas dann doch bisl arg arg wenig. Ich würde auf nen 32bit Arduino gehen, alias Arduino Due der von der Bauform und Pin belegung fast dem Mega entspricht.
Das wäre hoffentlich das kleinste Problem, einen Due zu organisieren

Ginge das noch mit dem selben Sender oder müsste der auch 32 bit sein?
 

DFENCE

Mitglied
Ne das mit dem Sender geht noch, es geht ja eher darum das du sehr viel Signalverarbeitung auf einmal machen musst, entweder geht das halt verdammt langsam mit Latenz oder nahezu Latenzfrei, dazu setzt es aber fast zwingend nen schnellen 32bit Prozessor voraus, was aber auch wieder ein Problem ergibt, Signalspannung bzw Logicspannung beträgt 3.3V für die L298N brauchste 5V dazu müsstest noch paar Bauteile mehr benutzen.

Aber das ist nicht dein Hauptproblem, das ist nen ziemlich zerstückelter Demo Code, du brauchst im Grunde jemand der dir das Komplett Programmiert :unsure: Weil zum Lernen ist das 10 Nummer zu hoch gegriffen. Ich weiß das ist nicht das was du jetzt erhofft hast, aber ich bin auch kein reiner Crack und müsste mich da selbst erstmal reinsteigern, das ist aber eben auch nix was man eben mal an nem Tag programmiert.
 

BAXL

Admin
Mitarbeiter
Man kann die Sachen aber auch Stück für Stück aufbauen und erweitern. Also nicht auf einen Schalg alles, sondern wachsen lassen, dafür muß man dann auch kein Experte sein.
 

hugentobler

Mitglied
Das war wirklich nicht die erhoffte Antwort, aber wenigstens ehrlich und nachvollziehbar. Und ich werde nicht so Stumpf wie in den anderen Foren abgespiesen.

Ich meine, mir war klar, dass dies eine Hürde sein wird. Ich ging davon aus, dass für meine gewünschten zusätzlichen Steuerungsfunktionen der Code nur noch vervollständigt hätte werden müssen, zumal ein Motor und Servos bereits angesprochen werden konnte. Aber dass dann doch noch so ein Riesenaufwand dahinter steckt, hätte ich ehrlich gesagt nicht gedacht.

Aber nach wie vor bleibt das mein Ziel, über einen eigenen Transmitter und Receiver. Dann muss der gute Panzer leider noch etwas zuwarten, denn ich muss mich zuerst in das Codieren vertiefen.

Wer Interesse hat, habe ich ein kleines Bild parat.

(Ein grosser Teil der 3D Daten habe ich übernommen und einiges habe ich mit Fusion 360 noch umgemodelt oder überhaupt neu gemacht, ausser Motor und Servos, und das innere vom Lauf, ist alles 3d gedruckt. Selbst das Getriebe.)

B6AB9BDC-6A12-4D4E-BE97-D29CE72F4BB2.jpeg
 

DFENCE

Mitglied
Ein Tiger, der konnte soweit ich was im Stand wirklich auf der Stelle drehen nur in der Fahrt nicht weil da nur eine Kettenseite gebremst wird, wie war das noch mit der Drehung um die Hochachse beim Tiger, Panter und co ? :unsure: :ROFLMAO: isn Anderes Thema, auf jeden fall ein Hübsches Teil und für ausm Drucker noch 10 mal Hübscher.

Ich empfehle dir aber auch wie Baxl schon schrieb schritt für schritt. Ich würde mich im Code erstmal auf das Vorwärts und Rückwärts beschränken, und dann nach und nach Funktion für Funktion, da bekommst dann auch eher hilfe bei einzelnen Schritten, so wirkt das erstmal erschlagend viel Arbeit.
 

BAXL

Admin
Mitarbeiter
Der Punkt istja, dass die Hilfe so aussehen müsste, dass jemand von uns sich in den Code einarbeiten und vervollständigen müsste. Du würdest den hochladen und feststellen das hier und da und dort irgendwas nicht klappt. Spätestens dann wird es kompliziert, weil der "Programmierer" keinen Zugriff auf die Hardware hat, um mal was auszuprobieren. Wenn ich Programme schreibe, dann funktionieren die auch nicht aus dem Stand und ich bessere sehr sehr oft an vielen Stellen nach, und wenn es nur kleine Parameter sind.

Das beste wird sein, Du startest mit den Grundfunktionen und bringst die zum Laufen, wenn Hilfe brauchst, fragst Du. Danach knöpfen wir uns stück für Stück die anderen Funktionen vor. Genau so arbeite ich auch bei meinen Programmen.

Ich zitiere mich mal selbst:
Tipp:
Meine Taktik dabei ist immer schreitweise vorzugehen, d.h. ich probiere in ganz einfachen Programmen die Sensoren oder Ausgabegeräte einzeln, der Reihe nach aus. Damit behalte ich die Übersicht und finde Programmfehler schneller. Wenn jeder einzelne Sensor und jedes einzelne Ausgabegerät sicher funktioniert, beginne ich die einzelnen Komponenten nach und nach zu einem Gesamtsystem zusammenzufügen. Erst wenn jede neu entstandene Kombination zufriedenstellend funktioniert, kommt das nächste Teil, oder Funktion hinzu. Dabei nicht vergessen, jeden (funktionierenden) Zwischenstand einer Programmversion mit einem Sinnvollen Namen zu speichern und im Quelltext des Programms kurz mit einem Kommentar zu beschreiben. Wenn man nämlich bei der 5. Zwischenversion angekommen ist, weiß man oft nicht mehr, was man sich bei Version 2 gedacht hat. Das erleichtert auch einen Schritt zurück (Fall Back), wenn man sich vergallopiert hat und zu einer funktionierenden Version zurückkehren möchte.

Angenehmer Nebeneffekt, die Zwischenversionen können als Basis für eine neue abgewandelte Anwendung direkt verwendet werden, weil man ja genau weiß, dass sie funktionieren.
Entnommen aus: Arduino - Wie setze ich ein Projekt um
 

hugentobler

Mitglied
Ja so war mein zweiter Gedanke, wenns mit ein paar Codezeilen wie erhofft nicht klappt (verständlicherweise natürlich), dass ich mir das Schritt für Schritt aufbaue.
 

hugentobler

Mitglied
Hallo TheDIYGuy999

Von dir habe ich unabhängig von diesem Forum bereits ein paar Videos gesehen. Hast du zufällig auch eins mit Raupenfahrzeug? Ein solches habe ich nicht gesehen.
 
Top Bottom