Moin.
Ich würde gerne mehr mit nem Arduino machen, hab aber bisher kaum Ahnung davon.
Wie wäre es hier mit Hilfen und Programmen rund um unsere Autos?
Wie kann ich z.B. den Arduino nutzen um mit einem Kanal mehrere Servos zu Steuern?
Moin.
Ich würde gerne mehr mit nem Arduino machen, hab aber bisher kaum Ahnung davon.
Wie wäre es hier mit Hilfen und Programmen rund um unsere Autos?
Wie kann ich z.B. den Arduino nutzen um mit einem Kanal mehrere Servos zu Steuern?
Einfacher TriState Switch ( 1 - 0 - 1) mit zwei Steuerungsausgängen
/***********************************
*
* simple servo signal detection
* three state switch with two outs
* Hardware: DigiSpark
*
***********************************/
// pin assignment
const int servoin = 2; // pin 2 servo signal in
const int switch1 = 3; // pin 4 output to relais 1
const int switch2 = 4; // pin 3 output to relais 2
const unsigned long neutral = 1500; // neutral pulse lenght in µS
const unsigned long deadzone = 125; // pulse lenght difference in µS between neutral and point to switch on
const unsigned long hysteresis = 25; // pulse lenght hysteresis (between on and off)
unsigned long servopulse;
void setup() {
// initiales ports
pinMode(servoin, INPUT);
pinMode(switch1, OUTPUT);
pinMode(switch2, OUTPUT);
// default port output
digitalWrite(switch1, LOW);
digitalWrite(switch2, LOW);
}
void loop() {
// read servo pulse length
servopulse = pulseIn(servoin, HIGH);
// switch
if ( servopulse > neutral + deadzone)
{
digitalWrite(switch1, HIGH);
digitalWrite(switch2, LOW);
}
if ( servopulse < neutral - deadzone)
{
digitalWrite(switch1, LOW);
digitalWrite(switch2, HIGH);
}
if ( abs (servopulse - neutral) < deadzone - hysteresis )
{
digitalWrite(switch1, LOW);
digitalWrite(switch2, LOW);
}
}
Alles anzeigen
Programmerläuterung in Deutsch
Dieses Programm wertet einen Kanal aus und schaltet entsprechend zwei Ausgänge, z.B. um eine Winde anzusteuern.
Neutralpunkt, Todeszone (wie heißt das auf Deutsch?) und Hysterese sind einstellbar mittels Konstanten.
Erläuterungen zum Code selbst ...
// pin assignment
const int servoin = 2; // pin 2 servo signal in
const int switch1 = 3; // pin 4 output to relais 1
const int switch2 = 4; // pin 3 output to relais 2
Die Zuweisung der konkreten Pin Nummern zu den jeweiligen Aus- und Eingängen mittels Konstanten. Erhöht die Lesbarkeit und Verständlichkeit des Programmcodes und erleichtert spätere Änderung.
const unsigned long neutral = 1500; // neutral pulse lenght in µS
const unsigned long deadzone = 125; // pulse lenght difference in µS between neutral and point to switch on
const unsigned long hysteresis = 25; // pulse lenght hysteresis (between on and off)
Definition weiterer Konstanten für Neutralstellung usw.
Die globale Definition der einzigen verwendeten Variable garantiert, dass sie überall innerhalb des Programms Geltung hat und genutzt werden kann. Bei dem geringen Umfang des Programms ist das noch nicht relevant.
void setup() {
// initiales ports
pinMode(servoin, INPUT);
pinMode(switch1, OUTPUT);
pinMode(switch2, OUTPUT);
// default port output
digitalWrite(switch1, LOW);
digitalWrite(switch2, LOW);
}
Der "Setup" Teil wird nur ein einziges mal beim Start, also nach dem Einschalten oder einem Reset ausgeführt. Hier ist der richtige Ort, um Ein- und Ausgänge zu konfigurieren. Dies geschieht mit der "pinMode()" Funktion. Hier sieht man auch gleich den Vorteil des Umweges über Konstanten. Statt der nichtssagenden Pin-Nr. eine aussagefähige Konstantenbezeichnung. Bei der Gelegenheit sollte man diese auch in einen definierten Zustand bringen. Deshalb die beiden Schreibbefehle.
void loop() {
// read servo pulse length
servopulse = pulseIn(servoin, HIGH);
// switch
if ( servopulse > neutral + deadzone)
{
digitalWrite(switch1, HIGH);
digitalWrite(switch2, LOW);
}
if ( servopulse < neutral - deadzone)
{
digitalWrite(switch1, LOW);
digitalWrite(switch2, HIGH);
}
if ( abs (servopulse - neutral) < deadzone - hysteresis )
{
digitalWrite(switch1, LOW);
digitalWrite(switch2, LOW);
}
}
Alles anzeigen
Die Haupt Programm Schleife wird unendlich immer wieder wiederholt ausgeführt. Wir beginnen mit der Erfassung der Länge des Servo-Impulses vom Empfänger. (persönliche Anmerkung des Autors: Ich stehe für gewöhnlich mit der" pulseIn()"-Funktion auf den Kriegsfuß, weil sie prinzipbedingt die Ausführung des Programms für eine gewisse Zeit blockiert. Soetwas ist gefährlich und kann zu Fehlfunktionen führen. Bei diesem lächerlich kleinen Programm ist dies aber unkritisch und deshalb lasse ich es ausnahmsweise mal durchgehen.) Es folgt die deutlich umfangreichere Auswertung mittels "if"-Anweisung. Das Einbeziehen der "deadzone" soll sicherstellen, das in Neutralstellung die Ausgänge nicht zu "flattern" anfangen, weil das Signal vielleicht mal eine Microsekunde drüber oder drunter liegt. Den jeweils anderen Ausgang, den wir eigentlich gar nicht schalten wollen, setzen wir trotzdem auf "Low". Auch das dient der Funktionssicherheit, denn es gibt durchaus Konstellationen oder Funkanlagen, wo das Signal des Kanals von einem Extrem ins andere springt. In einem solchen Fall, wo die neutrale Zone übersprungen wird, würde sonst der jeweils andere Ausgang unberechtigterweise aktiv bleiben. Die dritte "if"-Anweisung dient der Rückkehr in die Neutralstellung. Hier wird auch die Hysterese ausgewertet. Die Hysterese bewirkt, das die Schaltschwellen für's Ein- und Ausschalten des Schaltausganges entsprechend auseinander liegen. So ist sicher gestellt, das bei der Verwendung eines Proportional-Kanales beim Überschreiten der jeweiligen Schwelle die Ausgänge nicht "flattern".
"Totbereich" war was du suchst, würd ich sagen.
Danke für's teilen!
Genau DAS ist es was ich mit diesem Thread erreichen wollte
Danke Joerg1972
Allerdings kann man damit jetzt nur ein Servo Steuern, oder?
Wie sieht es denn aus wenn ich z.B. mit meiner Spectrum DX4s den 4. Kanal verwenden möchte um an meinem TRX beide Schaltservos getrennt voneinander Schalten möchte. Dazu sollten alle Endpunkte einstellbar sein.
Geht das?
Das Programm macht aus einem Servosignal zwei Schaltausgänge, quasi ein "PS2"
Es ist für einen Digispark geschrieben, das ist ein Winzig-Arduino, keinen Plan ob der 2 Servos ansteuern kann.
Ja, das Beispiel ist für den Digispark, ist aber auf jeden anderen Arduino übertragbar. Servos müsste der auch ansteuern können, hoffe ich. Probiere ich gerne aus. Mir schwebte schon länger im Kopf, mal quasi einen Grundlagen/Einsteiger Kurs für den Arduino zu machen. Deshalb dachte ich mir, das passt hier gut rein. Eigentlich wollte ich jetzt mit Blinker und als nächstes Brems- und Rückfahrleuchte folgen lassen. Zu der Ansteuerung von Servo wäre ich dann später noch gekommen. Wenn es aber bei den TRXen brennt kann ich natürlich eine Beispiellösung vorziehen.
Wäre echt cool weil ich dann meine DX4S weiter verwenden könnte
Ich kann den Kanal auf einen x-beliebigen Knopf legen und diesen dann als Taster Programmieren.
Am liebsten wäre mir.
Taster nach oben: Diff vorne gesperrt.
Taster noch mal nach oben: Diff vorne entsperrt.
Das gleiche nach unten für das hintere Diff.
Hey BigMac, ich hab dir doch schon mal den Sketch für den TRX geschickt. Den kannst du auch für deine DX4s verwenden. Die kann soweit ich weiß auch 3 verschiedene Stellungen ausgeben. Dann nimmst du einfach nur die beiden Diffservos. So wie es die originale Traxxas Steuerung auch macht. Dazu musst du nur ein paar Zeilen auskommentieren. Die von der Steuerung übergebenen Werte kannst du über die Schnittstelle auslesen und dann halt in den entsprechenden Zeilen eingeben.
Dein Ansatz mit dem Taster und nach oben und unten "tasten" ist allerdings auch gut. Das wäre glaub ich sogar noch die bessere Lösung. Das dürfte eigentlich auch kein Thema sein.
Erhöhen wir den Schwierigkeitsgrad etwas und variieren unser Programm in zweierlei Hinsicht.
1. unsere bisherige Tast-Funktionalität, Funktion ist nur solange aktiv, wie der Kanal entsprechend ausgelenkt ist, wandeln wir in eine Toggle-Funktion, mit jeder erneuten Auslenkung des Kanals wechselt unsere Funktion zwischen aktiv und inaktiv.
2. statt eines Schaltausgangs erzeugen wir ein Servo Signal, welches zwischen zwei fixen Positionen wechselt.
Hier der Programm Code einmal komplett:
/***********************************
*
* simple servo signal detection
* toggle two servos between two positions
* switched by one channel
* Hardware: arduino uno or equal
*
***********************************/
#include <Servo.h>
// pin assignment
const int servoin = 2; // pin 2 servo signal in
const int servo1 = 7; // pin 7 output to servo 1
const int servo2 = 8; // pin 8 output to servo 2
const unsigned long neutral = 1500; // neutral pulse lenght in µS
const unsigned long deadzone = 125; // pulse lenght difference in µS between neutral and point to switch on
const unsigned long hysteresis = 25; // pulse lenght hysteresis (between on and off)
const int servo1activ = 1750; // servo1 position in mircoseconds when active
const int servo1idle = 1250; // servo1 position in mircoseconds when inactive
const int servo2activ = 1750; // servo1 position in mircoseconds when active
const int servo2idle = 1250; // servo1 position in mircoseconds when inactive
unsigned long servopulse = 1500; // servosignal length
boolean toggle1 = false; // flag for toggle switch function1
boolean toggle2 = false; // flag for toggle switch function2
boolean antitoggle = true; // flag to prevent from auto toggling
// Servo(s) / ESC outputs
Servo Servo1out;
Servo Servo2out;
void setup() {
// initiales ports
pinMode(servoin, INPUT);
// initial servo ports
Servo1out.attach(servo1);
Servo2out.attach(servo2);
Servo1out.writeMicroseconds(servo1idle);
Servo2out.writeMicroseconds(servo2idle);
}
void loop() {
// read servo pulse length
servopulse = pulseIn(servoin, HIGH);
// switch
if ( servopulse > neutral + deadzone && antitoggle == true)
{
antitoggle = false;
toggle1 = !toggle1; // toggle function 1
}
if ( servopulse < neutral - deadzone && antitoggle == true)
{
antitoggle = false;
toggle2 = !toggle2; // toggle function 2
}
if ( abs (servopulse - neutral) < deadzone - hysteresis )
{
antitoggle = true; // set anti autotoggle flag
}
// servo action!
if (toggle1 == true)
{
Servo1out.writeMicroseconds(servo1activ);
}
else
{
Servo1out.writeMicroseconds(servo1idle);
}
if (toggle2 == true)
{
Servo2out.writeMicroseconds(servo2activ);
}
else
{
Servo2out.writeMicroseconds(servo2idle);
}
}
Alles anzeigen
Die neuen Programmsegmente im einzelnen:
Als erstes binden wir die arduino hauseigene Servo Bibliothek ein, um auf die Servo Funktionen zugreifen zu können.
const int servo1activ = 1750; // servo1 position in mircoseconds when active
const int servo1idle = 1250; // servo1 position in mircoseconds when inactive
const int servo2activ = 1750; // servo1 position in mircoseconds when active
const int servo2idle = 1250; // servo1 position in mircoseconds when inactive
Ein neues Paket Konstanten, mit denen wir die fixen Servo Positionen einmal im aktiven und einmal im inaktiven Zustand jeweils definieren.
boolean toggle1 = false; // flag for toggle switch function1
boolean toggle2 = false; // flag for toggle switch function2
boolean antitoggle = true; // flag to prevent from auto toggling
Ein paar neue Variablen, sogenannte Flags, weil sie nur ein Bit speichern. Zwei davon dienen dazu, den Zustand der jeweiligen Schaltfunktion zu speichern. Die dritte hat etwas mit der Bedienbarkeit zu tun. Sie verhindert, das die Funktionen beim Auslösen nicht selbstständig ihren Zustand ständig ändern. Zum erneuten Umschalten muss also erst wieder einmal in die Neutrale Position zurück gekehrt werden. Dazu später mehr.
Hier kommt zum ersten mal eine Funktion aus der eingebundenen Servo-Bibliothek zum Einsatz. Wir definieren hier, dass wir zwei Servos haben.
// initial servo ports
Servo1out.attach(servo1);
Servo2out.attach(servo2);
Servo1out.writeMicroseconds(servo1idle);
Servo2out.writeMicroseconds(servo2idle);
Weiter geht es im Setup Teil, wo wir mit der "attach()" Funktion unseren Servo Funktionen reale Ausgänge zuweisen. Mir fällt dabei gerade auf, dass die Namesgebung etwas unglücklich gewählt ist. Im nächsten Schritt gegeben wir unseren beiden Servos mal eine Grundposition, die sich an den vorher definierten Fixpositionen orientiert. Ich habe hier die zweite Variante der Servo Positionierung gewählt, bei der die Servo Position durch Angabe der Pulslänge bestimmt wird. Alternativ dazu kann man auch mit Stellwinkeln arbeiten. Dazu muss aber bei der "attach()"-Funktion entsprechende Pulslängen für die jeweiligen Endwinkel angegeben werden. Würde für unser Beispielprogramm mehr Aufwand bedeuten.
if ( servopulse > neutral + deadzone && antitoggle == true)
{
antitoggle = false;
toggle1 = !toggle1; // toggle function 1
}
Beispielhaft mal eine der abgeänderten "if()"-Konstrukte. Es fällt auf, das in der Bedingung ein Element, nämlich "antitoggle == true" mittels und-Verknüpfung ("&&") hinzu gekommen ist. Es müssen also beide Bedingen erfüllt sein. Wie weiter oben schon erwähnt dient dies der Bedienbarkeit. Wir setzen nämlich dieses Flag innerhalb des Ausführungsteils zurück, damit dieser nur einmalig ausgeführt wird, bis das Flag wieder gesetzt wird, was nur bei Neutralstellung des Kanals passiert. (die dritte if()-Konstruktion) Das eigentliche Wechseln, Togglen ist denkbar einfach. Es wird einfach der negierte Zustand wieder rein geschrieben.
// servo action!
if (toggle1 == true)
{
Servo1out.writeMicroseconds(servo1activ);
}
else
{
Servo1out.writeMicroseconds(servo1idle);
}
Zum Schluss geben wir in Abhängigkeit der Schaltzustände die vordefinierten Positionen an die Servos.
Hallo Jörg,
Mir schwebte schon länger im Kopf, mal quasi einen Grundlagen/Einsteiger Kurs für den Arduino zu machen.
ja dann mach mal, BITTE!
Gruß,
Walter
Sehr schön, danke!
Aber: 2 Sachen:
- Die Verknüpfung der zwei Bedingungen ist &&, nicht ==
- Warum setzt du nicht sofort die Servowerte in den IFs mit den Eingangsabfragen? Erst das Toggle-Bit setzen und dann wieder abfragen und die Servos setzen ist irgendwie doppelt gemoppelt. Hab ich was übersehen?
Sehr schön, danke!
Aber: 2 Sachen:
- Die Verknüpfung der zwei Bedingungen ist &&, nicht ==
- Warum setzt du nicht sofort die Servowerte in den IFs mit den Eingangsabfragen? Erst das Toggle-Bit setzen und dann wieder abfragen und die Servos setzen ist irgendwie doppelt gemoppelt. Hab ich was übersehen?
1. Danke, korrigiert! Sehr gut, wenn einer gegencheckt!
2. Da ich bei jeder Erfüllung der Bedingung einen anderen Servowert setze, kann ich hier keinen fixen Wert einsetzen und ich muss zwischen diesen beiden Werten hin und her schalten. (Das inspiriert mich zu einen weiteren Beispiel.) Man kann auch irgendwie die aktuelle Servoposition auslesen, aber das ist afaik kompliziert und das müsste man dann wiederum auswerten. Wenn man statt eines Servos nur einen Schaltausgang verwendet, kann man sich in der Tat die Booleans Variablen schenken und den Ausgang direkt togglen.
Man könnte natürlich die if() mit den Servoanweisungen zu Eingangsabfragen packen, macht den Code aber nicht kleiner und hier habe ich wohl instinktiv wieder weiter gedacht. Denn wenn noch weitere Programmteile hinzu kommen, die ggf. auch die Servopositionen beeinflussen sollen, dann muss dies unabhängig von der Eingabe geschehen.
Beispiel, unsere beiden Diff-Sperren sollen nur im ersten Gang zur Verfügung stehen. Dann kann man beim Hochschalten in den zweiten die Sperren gleich automatisch mit lösen lassen, um beispielsweise den Antrieb vor Schäden durch Verspannungen zu schützen.
Alles anzeigenHallo Jörg,
Mir schwebte schon länger im Kopf, mal quasi einen Grundlagen/Einsteiger Kurs für den Arduino zu machen.
ja dann mach mal, BITTE!
Gruß,
Walter
Wir sind hier doch schon fleißig dabei. Wenn dir etwas unklar ist, bitte fragen! Dann können wir drauf eingehen.
Wir fahren fort mit einem weiteren Beispiel.
Statt zwei Servos zwischen zwei festen Positionen wechseln zu lassen, haben wir nur noch einen Servo, der dafür aber mehrere festgelegte Positionen durchschaltet. Die Menge der Positionen ist nach oben fast beliebig erweiterbar, konkret sind es im Beispiel aber nur drei, sollte z.B. für ein Dreiganggetriebe reichen.
/***********************************
*
* simple servo signal detection
* shift servo posititions/gear
* switched by one channel
* Hardware: arduino uno or equal
*
***********************************/
#include <Servo.h>
// pin assignment
const int servoin = 2; // pin 2 servo signal in
const int servoout = 7; // pin 7 output to servo 1
const unsigned long neutral = 1500; // neutral pulse lenght in µS
const unsigned long deadzone = 125; // pulse lenght difference in µS between neutral and point to switch on
const unsigned long hysteresis = 25; // pulse lenght hysteresis (between on and off)
const int gearmax = 2; // highest gear -1!
const int servopos[gearmax + 1] = {1200, 1500, 1800}; // gear servo positions
unsigned long servopulse = 1500; // servosignal length
int gear = 0; // start gear -1
boolean antitoggle = true; // flag to prevent from auto toggling
// Servo
Servo GearServo;
void setup() {
// initiales ports
pinMode(servoin, INPUT);
// initial servo ports
GearServo.attach(servoout);
GearServo.writeMicroseconds(servopos[gear]); // set gear at start
}
void loop() {
// read servo pulse length
servopulse = pulseIn(servoin, HIGH);
// switch
if ( servopulse > neutral + deadzone && antitoggle == true)
{
antitoggle = false;
++gear; // shift gear up
if ( gear > gearmax)
{
gear = gearmax; // limit to highest gear
}
}
if ( servopulse < neutral - deadzone && antitoggle == true)
{
antitoggle = false;
--gear; // shift gear down
if ( gear < 0 )
{
gear = 0; // limit to lowest gear
}
}
if ( abs (servopulse - neutral) < deadzone - hysteresis )
{
antitoggle = true; // set anti autotoggle flag
}
// servo action!
GearServo.writeMicroseconds(servopos[gear]);
}
Alles anzeigen
Gehen wir auf die Änderungen bzw. Neuheiten ein ...
Hier kommt erstmalig ein Array zum Einsatz. Mit einem Array bekommt man Variablen oder auch Konstanten am Fließband, im Verbund. Hier handelt es sich um ein einfaches eindimensionales Array. Die einzelnen Inhalte werden über den Index adressiert. Wichtig ist dabei zu beachten, dass der Index bei Null beginnt. Ein Array mit drei Variablen, arrayname[3] besteht aus arrayname[0], arrayname[1] und arrayname[2]. Bei der Definition kann man mehrere Werte auch gleich übergeben, dies geschieht in den geschweiften Klammern.
Inkrementieren einer Variable. Keine große Sache, entspricht "gear = 1 + gear". Man kann hier genauso gut auch "gear++" schreiben, hat den selben Effekt, entspricht "gear = gear + 1". Relevant wird dies erst, wenn man sowas macht: "neueVariable = gear++". Brauchen wir an dieser Stelle nicht weiter drauf eingehen.
Analog funktioniert dies auch mit dem Dekrementieren.
Ahja, macht Sinn, danke für die Erklärung.
Hier noch ein kleines, nützliches Werkzeug, ein kleiner Sketch zum Monitoring von Servo PWM Signalen.
Dazu muss der Arduino mit dem Rechner verbunden sein, via USB wie bei der Programmübertragung und der "Serielle Monitor" in der Entwicklungsumgebung unter "Werkzeuge" wird zur Visualisierung genutzt.
/***********************************
*
* simple servo signal detection
* prints servo PWM signal on serial
* Hardware: arduino uno or equal
*
***********************************/
// pin assignment
const int servoin = 2; // pin 2 servo signal in
unsigned long servopulse = 1500; // servosignal length
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
while (!Serial) {
; // wait for serial port to connect.
}
// initiales ports
pinMode(servoin, INPUT);
}
void loop() {
// read servo pulse length
servopulse = pulseIn(servoin, HIGH);
Serial.print("Servo PWM:");
Serial.print(servopulse);
Serial.print("µS\n");
}
Alles anzeigen
Hat jemand eine Idee für eine "stehende" Anzeige?
Einzig "\n" (und "\t") wird als Steuerzeichen korrekt umgesetzt.
Ich blieb mal dabei.