"Diffential Thrust" Pro mini

hallö liebe helfenden ;D

ich habe mir einen sog. Nurflügler gebaut, ihn mit einem Pixhawk (Flugcontroller) bestückt und bin damit ziemlich zufrieden.
mein Problem ist nur das o.g. Flugmodell kein ruder, welches für die “links - rechts Lenkung” in der luft zuständig ist, hat.
nun dachte ich mir Arduino, jawoll…, kann doch nicht so schwer sein.
nur habe ich einen code gefunden der als “V-Tail mixer” erstellt wurde und als solcher auch wunderbar funktioniert.
ich möchte diesen umschreiben, hab auch ziemlich viel davon in erfahrung bringen können, was was bedeutet trotzdem sind einige fragen doch ungeklärt geblieben.

ich weiß wie das mit der “interrupt” funktion funktioniert und wie/wo welche signale ausgegeben werden

nun stellt sich mir die frage was genau diese zeilen im einzelnen zu bedeuten haben:

volatile double Chan1_val, Chan2_val = 1500;
volatile double Chan1_val_last, Chan2_val_last = 1500;

warum sind die chan_1 und chan_2 value auf 1500 festgelegt? und welchen zweck hat dies?

double reverse(double val) {
 return (val-1500) * -1 + 1500;
}

double add(double val1, double val2) {
 double out = (val1 -1500 + val2 -1500) + 1500;
 out = (out < 1000) ? 1000 : out;
 out = (out > 2000) ? 2000 : out;
 return out;
}

void serviceServos(long pFrameCounter) {
 
 // do the v-tail mixing 
 double left  = add(Chan2_val, Chan1_val);
 double right = add(reverse(Chan2_val), Chan1_val);
 
 ServoArray[0].writeMicroseconds(left);
 ServoArray[1].writeMicroseconds(right);

das soll dieses v-tail mixing berechnen. aber was ist davon was? kann mir jemand erklären was davon welche funktion hat ich habe viel gelesen aber ich komm auch mittlerweile durch das arduino Learning nicht weiter.

hier nochmal der ganze code:

/**
PPM V-Tail/elevon mixer

Input pins:
 2: aileron/rudder
 3: elevator

Output pins:
 9: servo left
10: servo right
*/


#include <math.h>
#include <Servo.h>

//#define DEBUG
#define START_OUTPUT_PIN 9

int Chan1Interrupt = 0; // pin 2, aileron
int Chan2Interrupt = 1; // pin 3, elevator

unsigned long Chan1_startPulse, Chan2_startPulse;
volatile double Chan1_val, Chan2_val = 1500;
volatile double Chan1_val_last, Chan2_val_last = 1500;
long StartMillis=0;
long FrameCounter=0;

Servo ServoArray[2];

double reverse(double val) {
 return (val-1500) * -1 + 1500;
}

double add(double val1, double val2) {
 double out = (val1 -1500 + val2 -1500) + 1500;
 out = (out < 1000) ? 1000 : out;
 out = (out > 2000) ? 2000 : out;
 return out;
}

void serviceServos(long pFrameCounter) {
 
 // do the v-tail mixing 
 double left  = add(Chan2_val, Chan1_val);
 double right = add(reverse(Chan2_val), Chan1_val);
 
 ServoArray[0].writeMicroseconds(left);
 ServoArray[1].writeMicroseconds(right);
}

void setup() {
 attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
 attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
 
 ServoArray[0].attach(START_OUTPUT_PIN+0, 900, 2100); // aileron
 ServoArray[1].attach(START_OUTPUT_PIN+1, 900, 2100); // elevator

 StartMillis = millis();
#ifdef DEBUG
 Serial.begin(115200);
#endif
}

void loop() {
 long LocalMillis;
 long LocalFrameCounter;
 LocalMillis = millis();
 LocalFrameCounter = (LocalMillis - StartMillis) / 20;

 if (LocalFrameCounter > FrameCounter) {
  FrameCounter = LocalFrameCounter;
  serviceServos(FrameCounter);
 }
}

void Chan1_begin() {
 Chan1_startPulse = micros();
 detachInterrupt(Chan1Interrupt);
 attachInterrupt(Chan1Interrupt, Chan1_end, FALLING);
}

void Chan1_end() {
 Chan1_val = micros() - Chan1_startPulse;
 detachInterrupt(Chan1Interrupt);
 attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
 if (Chan1_val < 1000 || Chan1_val > 2000) {
  Chan1_val = Chan1_val_last;
 } else {
  Chan1_val_last = Chan1_val;
 }
}

void Chan2_begin() {
 Chan2_startPulse = micros();
 detachInterrupt(Chan2Interrupt);
 attachInterrupt(Chan2Interrupt, Chan2_end, FALLING);
}

void Chan2_end() {
 Chan2_val = micros() - Chan2_startPulse;
 detachInterrupt(Chan2Interrupt);
 attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
 if (Chan2_val < 1000 || Chan2_val > 2000) { 
  Chan2_val = Chan2_val_last;
 } else {
  Chan2_val_last = Chan2_val;
 }
}

als anhang ist ein bild was der rc stick tut und was die servos tun

  1. Spalte: Knüppelstellung vom sender
  2. Spalte: Stellung linkes servo
  3. Spalte: Stellung rechtes servo
  4. Spalte: ausgangspuls in Millisekunden linkes servo
  5. Spalte: ausgangspuls in Millisekunden rechtes servo
  6. Spalte: Eingangspuls in Millisekunden arduino pin 3
  7. Spalte: Eingangspuls in Millisekunden arduino pin 2
  8. Spalte gewollter Ausgangspuls arduino pin 9
  9. Spalte gewollter Ausgangspuls arduino pin 10

Nun, in der RC-Servo-Welt ist 1500 der Nullpunkt und der Wertebereich geht von 1000 bis 2000.

In diesem Bereich arbeiten die Funktionen add und reverse und solche Werte werden per writeMicroseconds an die Servos ausgegeben.

michael_x:
Nun, in der RC-Servo-Welt ist 1500 der Nullpunkt und der Wertebereich geht von 1000 bis 2000.

In diesem Bereich arbeiten die Funktionen add und reverse und solche Werte werden per writeMicroseconds an die Servos ausgegeben.

das ist mir bekannt. Danke trotzdem.

ich bräuchte eine erklärung zu o.g. Funktionen da ich nicht erkennen kann wie die Ausgangssignale berechnet werde weil ich die Formel nicht verstehe.

sie ist für mich nicht logisch nachvollziehbar.

da ich z.b. nicht weiß wie “val1” erkennbar ist. wozu sie aus welchem “irgendweas” entsteht bzw zugeordnet ist.

ich hatte schonmal eine Formel bei der alle berechneten funktionen logisch nachvollziehbar waren jedoch war diese auf der “Pulsein” funktion aufgebaut welche auf dem rechten kanal nicht zu kontrollierende gasstöße hervor brachte.
Hier der code von dem ich rede.

#include <Servo.h>

/* Begin configuration settings */
const int min_command = 1000; // The minimum signal to be sent to the ESC
const int max_command = 2000; // The maximum signal to be sent to the ESC

const byte throttle_pin = A2; // The pin that the throttle input signal is plugged into
const byte yaw_pin = A1; // yaw input pin
const byte l_motor_pin = 6; // Left motor output pin
const byte r_motor_pin = 9; // Right motor output pin

const int reverse_yaw = 1; // 1 or -1 to reverse the yaw channel. 
const int yaw_deadband = 20; // ignore this much jitter on yaw channel
const int mix_strength = .75; // value from 0 (no mix) to 1 (100%) for the mix strength. May eventually set this value from another switch/channel on the receiver

/* End configuration */


int throttle, yaw, modifyer = 1000;
float yaw_pct;

Servo l_motor, r_motor;

void setup() {
  // put your setup code here, to run once:

  pinMode(throttle_pin, INPUT);
  pinMode(yaw_pin, INPUT);

  l_motor.attach(l_motor_pin);
  r_motor.attach(r_motor_pin);

  
  Serial.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:

  // read input pwm from throttle and yaw
  throttle = pulseIn(throttle_pin, HIGH);
  yaw = pulseIn(yaw_pin,HIGH);

  
  if(yaw > 1500 + yaw_deadband || yaw < 1500 - yaw_deadband){
    yaw_pct = ((yaw-1500)/(float)500) * reverse_yaw;
  }else{
    yaw_pct = 0;
  }
  
  // calculate transformation
  modifyer = abs(yaw_pct) * (throttle - 1000) * mix_strength;
  
  // calculate & write left and right motor mixed throttle/yaw values
  if(yaw_pct < 0){
    l_motor.writeMicroseconds((throttle - modifyer) < min_command ? min_command : (throttle - modifyer));
    r_motor.writeMicroseconds((throttle + modifyer) > max_command ? max_command : (throttle + modifyer));
  }else{
    l_motor.writeMicroseconds((throttle + modifyer) > max_command ? max_command : (throttle + modifyer));
    r_motor.writeMicroseconds((throttle - modifyer) < min_command ? min_command : (throttle - modifyer));
  }
 
}

nun stellt sich mir die frage was genau diese [2] zeilen im einzelnen zu bedeuten haben:

warum sind die chan_1 und chan_2 value auf 1500 festgelegt? und welchen zweck hat dies?

das ist mir bekannt. Danke trotzdem.

Da hast du mich aber gut reingelegt. :wink:

Was z.B. double reverse(double val) macht, ist aber doch auch trivial, oder?
( Macht 1400 <–> 1600 usw. )

Und das Beispiel mit dem pulseIn ist dir auch klar. Schön.

Statt pulseIn wird hier immer abwechselnd entweder die Chan_end oder Chan_begin als Interrupt-Routine aktiviert und bei _end die zugehörige Chan_val gesetzt.
(Wird eben so gemacht, warum: kann man so machen, würde auch anders gehen)

Wenn das auch an deiner Frage vorbeigeht: Frag weiter, damit du was lernst :wink:

nach ein bisschen nachdenken und hin und her gerechne sind mir die funktionen

double reverse(double val) {
 return (val-1500) * -1 + 1500;
}

double add(double val1, double val2) {
 double out = (val1 -1500 + val2 -1500) + 1500;
 out = (out < 1000) ? 1000 : out;
 out = (out > 2000) ? 2000 : out;
 return out;
}

void serviceServos(long pFrameCounter) {
 
 // do the v-tail mixing 
 double left  = add(Chan2_val, Chan1_val);
 double right = add(reverse(Chan2_val), Chan1_val);

jetzt verständlich was sie tun.

nun möchte ich das diese funktion:

  if(yaw > 1500 + yaw_deadband || yaw < 1500 - yaw_deadband){
    yaw_pct = ((yaw-1500)/(float)500) * reverse_yaw;
  }else{
    yaw_pct = 0;
  }
  
  // calculate transformation
  modifyer = abs(yaw_pct) * (throttle - 1000) * mix_strength;
  
  // calculate & write left and right motor mixed throttle/yaw values
  if(yaw_pct < 0){
    l_motor.writeMicroseconds((throttle - modifyer) < min_command ? min_command : (throttle - modifyer));
    r_motor.writeMicroseconds((throttle + modifyer) > max_command ? max_command : (throttle + modifyer));
  }else{
    l_motor.writeMicroseconds((throttle + modifyer) > max_command ? max_command : (throttle + modifyer));
    r_motor.writeMicroseconds((throttle - modifyer) < min_command ? min_command : (throttle - modifyer));
  }

irgendwie so integriert/umgewandelt wird das sie mit der interrupt Funktion kompatibel ist.

In deinem "Interrupt" - Sketch hast du C1 und C2 und steuerst Motor_l = C1 + C2 Motor_r = C1 - C2

Im "pulseIn" Sketch wertest du stattdessen throttle und yaw aus und hast eine etwas komplizierteres Verfahren, daraus deinen modifier zu ermitteln.

Dann machst du Motor_l = throttle + modifier Motor_r = throttle - modifier ( je nach yaw_pct wird modifier hier nochmal invertiert )

wenn du deine Variablen umbennst, Chan1_val -> throttle, Chan2_val -> yaw, solltest du den Rest deines "pulseIn" Sketches verwenden können. Bis auf die pulseIn Funktionen selber, natürlich.

( Auch keine Ahnung, ob dein Flieger das überlebt ;) )