Pages: [1] 2   Go Down
Author Topic: Problem mit Servo steuerung  (Read 1886 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Moin,
ich habe eine Servosteuerung geschrieben, in der man einen Wert von 1-180 im Terminal eigeben können soll, und der Servo soll dann diese Position einnehmen. Aber wenn ich die Zahlen 1 - 9 eingebe, (nach stellen des Servos gibt er den Wert mit Serial.prinln zurück) bekomme ich statt 1, 10 zurück und bei 9, 90. Das ist auch bei allen anderen Zahlen so. Wenn ich jetzt 2 Zahlen gleichzeitig eingebe, z.B. 85, stellt er den Servo erst auf 80° und dann auf 50° und gibt erst 80 und dann 50 zurück. Was mache ich falsch?
Code:
#include <Servo.h>

Servo se;
char inByte[32];
int i = 0;
int pos;

void setup(){
  se.attach(4);
  se.write(90);
  Serial.begin(9600);
  Serial.println("Ready...");
}

void loop(){
  if(Serial.available()){
    while(Serial.available()>0){
      inByte[i] = Serial.read();
      inByte[i+1] = '/0'; // Das habe ich hier im Forum gesenhen, damit ich das Teil zum int machen kann
      i++;
    }
    i = 0;
    pos = atof(inByte);
    se.write(pos);
    Serial.println(pos);
  }
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oha, da sind mehrere Macken drin:

* Das Zeichen '\0' (als Nullbyte) wird mit Backslash ("\") geschrieben, nicht mit Slash ("/").

* atof() wandelt einen ASCII-String in eine Fließkommazahl, nicht in einen int. Verwende dafür besser atoi(), sonst wird die Fließkommabibliothek mit in deinen Sketch gebaut. Das kostet Platz und ist langsam.

* du hast die serielle Schnittstelle auf 9600 BPS gestellt, das sind etwa 1000 Zeichen pro Sekunde. Deine while-Schleife läuft aber sehr viel schneller als 1000x pro Sekunde. Nach dem ersten gelesenen Zeichen
wird also die Abbruchbedingung (Serial.available()>0) zuschlagen, einfach weil das nächste Zeichen noch nicht da ist.
Besser ist es da, gar keine Schleife zu verwenden:

Code:
#include <Servo.h>

Servo se;
char inByte[12];
int i = 0;
char zeichen;
char fertig = 0;
int pos;

void setup(){
  se.attach(4);
  se.write(90);
  Serial.begin(9600);
  Serial.println("Ready...");
}

void loop()
{
  // erst mal serielle Schnittstelle auswerten
  if(Serial.available()){
    zeichen = Serial.read();
    if((zeichen >='0') && (zeichen <='9') && (i < 10)) // numerisches Zeichen?
    {
      fertig = 0;
      inByte[i] = zeichen; // zeichen in den String uebernehmen
      inByte[i+1] = '\0'; // Ein String hat in C/C++ ein Nullbyte am Ende
      i++;
    }
    else fertig = 1; // ein nicht-numerisches Zeichen beendet die Zahl
  }
  
  // Wenn von der Seriellen was kam kann das jetzt umgewandelt werden
  if((fertig == 1) && (i > 0)) // nur bei i>0 haben wir was gelesen!
  {
    pos = atoi(inByte);
    se.write(pos);
    Serial.println(pos);
    i = 0;
  }
  
  // hier kann evtl. weitere Verarbeitung erfolgen
}
loop() schaut in jedem Durchgang, ob ein Zeichen angekommen ist. Falls ja, wird überprüft ob es eine Ziffer ist. Ist es eine, wird sie an den String angehängt (bis zu 10 Ziffern), andernfalls wird die Zahleneingabe beendet ("fertig = 1;").
Dann schaut loop() ob eine Zahl komplett angekommen ist (also i > 0 und fertig == 1). Wenn ja, wird der Servo gestellt und i wieder genullt.

Dadurch wird loop() nicht durch eine while()-Schleife blockiert und kann mehrere tausend Durchläufe pro Sekunde schaffen, so dass man "nebenbei" jetzt noch Luft für andere Aufgaben hat (Sensoren auslesen, LEDs blinken lassen etc...).
Wichtig ist halt nur, dass keine dieser Aktionen den Arduino blockiert.

Gruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ok danke erstmal.
aber dein Code geht nicht.
Wenn ich ihn übertrage und den Arduino resette, stellt sich der Servo in Mittel position. Auf Zahlen eingaben reagiert er nicht. Aber wenn ich sinnlos irgentwelche buchstaben eintippe, bekomm ich immer Zahlen wie
-243242 und +234234 zurück ausgegeben. Im code direkt konnte ich jetzt keinen Fehler finden. Woran liegts?

Philipp
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Fangen wir mal beim Servo an: Der sweep-Sketch funktioniert?

Welche Werte musst du denn für 0°, 90° und 180° übergeben?

Dann: Ist der String korrekt wenn du ihn in eine Zahl wandelst? Einfach mal auf der Konsole ausgeben.

Gruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

servo geht.
Würde meine Methode gehen, wenn ich eine größere Baudrate nähme?
Welche ist denn die Maximal mögliche mit dem Duemilanove?

Philipp
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ehm - nein.

Die Baudrate der Seriellen hat da wohl nichts mit zu tun, die muss halt nur auf beiden Seiten gleich eingestellt sein. Bei 9600 auf dem Arduino brauchst du auch 9600 auf der Gegenstelle. Ist bei 11520 aber genauso.

Kommen denn die Zahlen so zurück wie du sie eingegeben hast?

Gruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Also ich habe jetztmal das Serial.println(inByte) in die if abfrage gepackt, in der abgefragt wird, ob es ein numerisches Zeichen oder ein Buchstabe ist. Da bekomm ich bei der Eingabe 123 zu erst eine 1, dann eine 12 und dann eine 123 zurück. Soweit ist dass dann ja gut, aber wenn ich dann noochmal 123 eigeben wird es zu dem bestehenden dazuaddiert. Also kommt dann zuerst 1231 dann 12312 und dann 123123 zurück. Das deutet darauf, dass die abfrage if((fertig == 1...
garnicht aufgerufen wird. Den Fehler habe ich gefunden. Du hattest die Esle fertig = 1; als else für die if((zeichen>='0'.. eingesetzt, aber so wird die else nie aufgerufen. Dann habe ich die else für if(Serial.availa.... eingestzt. Jetzt geht es. Aber das problem ist jetzt noch, dass der Block, in dem der Servo positioniert wird if((fertig ==1
immer wieder durchlaufen wird. Dadrin habe ich auch noch eine Serial.prinln und in der console wird die ganze Zeit über der Wert ausgegeben, der Aktuell ist. zumindest geht es jetzt mit eingabe der Zahlen, dass die pos Variable auch den richtigen Wert bekommt.

Philipp
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Du denkst aber daran, dass du zum Beenden einer Zahl ein nicht-numerisches Zeichen eingeben musst?

Weil der Arduino ja nicht ekennen kann, ob nach "12" noch eine "3" kommt oder nicht, musst du ihm da schon helfen, eben durch ein Zeichen das keine Ziffer ist.
Genau in diesem Fall wird dann mein "else fertig = 1" aufgerufen.

Gruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

das ist schon klar aber so wie du das Programmiert hast, wird die else fertig =1; nie ausgerufen, da dieser Aufruf in der if srial.available verschachtelt ist, und wenn keine Zahl mehr im Buffer ist, wird dieser Aufruf garnichtmehr gemacht, so wird die else fertig = 1 nie ausgeführt.
So wie ich das jetzt umgeschrieben habe, wird erstgeschaut ob noch Zahlen im Buffer sind, und wenn das nicht der Fall ist, wird else fertig = 1 aufgerufen.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wenn du die Zahlen von der Arduino-IDE aus dem seriellen Terminal heraus verschickst, dann ist immer nach den Ziffern ein Zeilenvorschub (0x13 oder 0x10) mit dabei, so dass mein "else"-Kriterium greift.

Womit verschickst du denn die Zahlen dass nach der letzten Ziffer gar nichts mehr kommt?

Die Abfrage auf "serial.available" halte ich für bedenklich, besonders bei deiner niedrigen Transferrate. Da wird das auch mit Sicherheit mitten in der Zahl vorkommen.

Gruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich verschicke die Zahlen mit dem Arduino Terminal
Aber bei mir kommt am ende kein weiteres Zeichen
Philipp
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK, habs mal ausprobiert, du hast recht: es gibt kein Zeilenendezeichen.

Aber wenn man selbt ein solches Zeichen bereitstellt, dann funktioniert es. Ein beliebiges Zeichen tut es schon, such dir eins aus  8-).

Das kann man sich sogar zunutze machen:
Wenn du z.B. zwei Servos ansteuern möchtest kann das zusätzliche Zeichen für den Servo stehen: "A" ist der eine, "B" der andere.

Sende "90A45B" um beide zu stellen, oder "180B" um nur einen zu stellen.

Bruß
pi
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ich möchte später auch 2 Servos ansteuern, aber ich hatte vor 6 bytes zusenden. Also z.b. Servo 1 = 180 und Servo 2 ist 45.
Dann wollte ich 180045 senden. Und dann char servo_1[] = {inByte[0],inByte[1],inByte[2],};
und mit servo 2 dann genauso.
Aber es ist ja so, dass ich in das inByte immer noch ans ende '\0' setzten muss. Muss ich das dann auch ans ende von den servo_1 /2 chars machen? Also: char servo_1[] = {inByte[0],inByte[1],inByte[2],'\0'};

Philipp
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nein, das Nullbyte dient dazu, das String-Ende anzuzeigen. Ein solches gibt es aber definitionsgemäß nur einmal ("... nur die Wurst hat zwei!").

Mit den festen Stringlängen kannst du dir das Einlesen natürlich einfacher machen. Ich würde allerdings trotzdem Zeichen zur Separation der Dreiergruppen einfügen, damit die Synchronisation besser klappt.

Sollte sich der Arduino irgendwann mal verzählen (z.B. weil eine ISR ihn für eine Sekunde ausgebremst hat und er ein paar Zeichen verloren hat) kann er wieder aufsetzen:

018-049-160-086-119-16-012-048-157-167-049-202

01804916008611916012048157167049202

Ein Zeichen fehlt: die 6. Dreiergruppe besteht nur aus zwei Ziffern.
Durch die Bindestriche lässt sich das aber auch für den Arduino sehr viel leichter feststellen als im reinen Zeichenstrom. Da würde er dann statt "16-012-048-157" ein "160-120-481-" lesen und somit drei ziemlich heftige Fehleinstellungen machen. An der 481 kann man evtl. erkennen, dass hier ein Fehler vorliegt, aber es gibt keinen Hinweis, wie das korrigiert werden kann.

Da die Servos üblicherweise mit 50 Hz angesteuert werden hast du also bei 9600 BPS etwa 20 Zeichen für eine Übertragung. Das reicht für 5 Servos mit 4 Zeichen (3 Ziffern und 1 Separator).

Gruß
pi
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 11
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So, ich konnte den Code ebenfalls gebrauchen, vielen Dank für die gute Vorarbeit!
Ich hab es jetzt so umgesetzt, wie pi es vorgeschlagen hatte:
Quote
Sende "90A45B" um beide zu stellen, oder "180B" um nur einen zu stellen.
Funktioniert bestens, und war wirklich leicht seinen code anzupassen.
Mein Teil des codes ist wahrscheinlich etwas unsauber, sollte aber zu lesen sein.
Ich bin selbst neu hier und versuch grade, mir auf Basis eines RC-Monstertrucks einen Roboter als lernplattform zu bauen.
Und da kam es mir ganz gelegen, das Ding auch ohne intelligentes Verhalten einfach vom PC aus fern zu steuern.
Code:
//05/06/10
//sketch zur Ansteuerung eines Modellautos mit zwei Kanälen per Arduino
//sketch for controlling a model car with two channels with an arduino

#include <Servo.h>

Servo servo;  //Servo für die Lenkung /Servo for steering
Servo fahrt;  //Fahrtregler / speed controller
char inByte[12];
int i = 0;
char zeichen;
char fertig = 0;
int pos;

void setup(){
  servo.attach(4);  //Pin für den Servo / Pin for the servo
  servo.write(90);  
  fahrt.attach(3);  //Pin für den Fahrtregler / Pin for the speed controller
  fahrt.write(90);
  Serial.begin(9600);
  Serial.println("vehicle ready and waiting for instructions...");
}

void loop()
{
  // erst mal serielle Schnittstelle auswerten
  if(Serial.available()){
    zeichen = Serial.read();
    if((zeichen >='0') && (zeichen <='9') && (i < 10)) // numerisches Zeichen?
    {
      fertig = 0;
      inByte[i] = zeichen; // zeichen in den String uebernehmen
      inByte[i+1] = '\0'; // Ein String hat in C/C++ ein Nullbyte am Ende
      i++;
    }
    else fertig = 1; // ein nicht-numerisches Zeichen beendet die Zahl
}
  
  // Wenn von der Seriellen was kam kann das jetzt umgewandelt werden
  if((fertig == 1) && (i > 0)) // nur bei i>0 haben wir was gelesen!
  {
    pos = atoi(inByte);
    if(zeichen == 'A')
    fahrt.write(pos);
    Serial.println("Fahrtregler auf ");
    Serial.println(pos);
  }
    if(zeichen == 'B'){
    servo.write(pos);
    Serial.println("Servo auf ");
    Serial.println(pos);
  }
    i = 0;
  }
  
  // hier kann evtl. weitere Verarbeitung erfolgen



Gruß,
Philipp
« Last Edit: May 06, 2010, 12:04:33 pm by bluegnome » Logged

Pages: [1] 2   Go Up
Jump to: