Erkennung von Abstand zwischen zwei bewegten Teilen mittels IR-Sensor

Hallo an alle,

ich bin ein Arduino Starter und habe ein Programmierproblem.
Mein Projekt:
Es sollen über ein Fließband Teile bewegt werden und am Ende mit einem Mindestabstand von diesem herunterfallen.

Mein Ziel ist es zu bemerken, wenn der Abstand zwischen zwei Teilen auf meinem selbstgebauten Fließband zu gering ist und in dem Falle das Fließband für 2 Sekunden stoppen zu lassen.
Anschließend soll es wieder anfahren und wieder aufmerksam sein.

Bei meinem Code, arbeite ich bisher nur mit If -Befehlen um dies umzusetzen.
Leider Stoppt der Motor sobald der Sensor etwas erkennt.
Könnt ihr mir Helfen einen richtigen Weg zu finden?

Meine Hardware ist folgende:

  • ein Arduino Uno, genauer: AZDelivery Mikrocontroller Board ATmega328
  • ein AZDelivery CNC Shield V3 mit A4988 Motortreibern (nur ein Motor in Benutzung) (inkl. externer Spannungsversorgung)
  • ein Nema17 Schrittmotor 1,8° ( 42Ncm 1.5A)
  • KY-032 IR Infrot -Sensoren

DerCode ist folgender:

// Stepper Motor X

const int stepPin = 2;          //X.STEP
const int dirPin = 5;           // X.DIR
int sen = 9;
int detect;
long zeit3;
long zeit2;
long dauer;
long wiederholung;
long wartezeit;
long warten;

void setup() {
  Serial.begin(9600);
  pinMode(stepPin, OUTPUT);   // Step Signal fuer Geschw.
  pinMode(dirPin, OUTPUT);    //Dir -> direction
  pinMode(sen, INPUT);        //Sensor eingang

}
void loop() {

  detect = digitalRead(sen);
  if (detect == 1) {
    //digitalWrite(dirPin, HIGH); // Enables the motor to move in a particular direction
    digitalWrite(dirPin, LOW);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(471);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(471);
    Serial.println("Laeuft");
  }

  else if (detect == 0)                        // Sensor detektiert etwas
  {
    if (detect == 1)                           // Sensor erkennt wieder nichts
    {
      zeit2 = millis();                        // Zeit starten
      digitalWrite(dirPin, LOW);
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(471);
      digitalWrite(stepPin, LOW);
      delayMicroseconds(471);

      if (detect == 0)                         // Sensor detektiert erneut etwas
      {
        wiederholung = millis();               // Zeit erkennen und messen der Dauer
        dauer = wiederholung - zeit2;
        Serial.println(dauer);
        if (dauer > 2000)                      // wenn der Abstand groesser als 2 Sekunden -> nichts
        {
        }
        else {
          zeit3 = millis();                    // wenn Dauer kleiner als 2Sekunden dann warte bis die Zeit rum ist und starte Loop neu
          wartezeit = 2000 - (zeit3 - zeit2);
          Serial.println(wartezeit);
          //digitalWrite(stepPin, 0);
          delay(wartezeit);                    // zwei Sekunden warten
        }
      }
    }
  }
};

Vielen Dank für Eure Unterstützung!
PrinzHermann

Hallo,
ich bin jetzt nicht in Deine Logik eingestiegen, aber mir fällt auf das Du die Zeitverabeitung mit millis() , ich sage mal etwas unüblich machst. Du willst den Abstand erkennen und eine Zeitverzögerung bauen ?
alle Variablen die irgendwie etwas mit milli() zu tun haben sollten vom Typ usigned long oder uint32_t sein. Damit es zu keinen negativen Werten kommen kann macht man die Abfrage immer in der Form
if (millis() - startwert >= timerwert) // Zeit ist abgelaufen

startwert setzt Du dann zu dem Zeitpunkt mit startwert=millis() auf die aktuelle zeit wenn der Timer gestartet werden soll. Das hast Du ja mit wiederholung und zeit3 schon so gemacht.

Heinz

Ich hab das mal etwas anders gemacht.
Du willst vermutlich nur eine Vereinzelung, sodas ein Teil immer mit einem Mindestabstand zum Vorhergehenden fällt.

Meine Idee ist folgende:

  • Ich detektiere das erste Teil
  • Merke mir den Zeitpunkt
  • merke mir, das ich was dectectiert habe
  • und las den Antrieb weiter laufen
    Wenn das Teil den detect-Bereich verlässt, lösche ich den Sperrmerker und bin wieder bereit etwas zu detektieren
  • Ich detektiere das zweite Teil
  • Ist die Mindestzeit abgelaufen, mache wie bei Teil 1
  • Ist die Zeit nicht abgelaufen bleibt der Antrieb stehen
    • aber nur solange, bis die Mindestzeit erreicht

ungetestet - ich habs kommentiert

// Stepper Motor X

const byte stepPin = 2;          //X.STEP
const byte dirPin = 5;           // X.DIR
const byte sensorPin = 9;
const uint32_t mindestZeit = 2000;
uint32_t detectTime;
bool lastDetect = false;
bool antrieb;

void setup()
{
  Serial.begin(9600);
  pinMode(stepPin, OUTPUT);   // Step Signal fuer Geschw.
  pinMode(dirPin, OUTPUT);    //Dir -> direction
  pinMode(sensorPin, INPUT);        //Sensor eingang
  antrieb = true;
}
void loop()
{
  if (digitalRead(sensorPin) == LOW &&         // Sensor detektiert etwas
      lastDetect == false)                     // Vorher war da nichts
  {
    if (millis() - detectTime >= mindestZeit)  // letztes detekt ist lange her
    {
      detectTime = millis();                   // aktuelle Zeit merken
      antrieb = true;                          // Antrieb ein
      lastDetect = true;                       // Sperrmerker setzen
    }
    else                                       // letzte detektZeit ist zu kurz?
    {
      antrieb = false;                         // Antrieb aus
    }
  }
  else                                         // Senor ist freigegeben
  {
    lastDetect = false;                        // Sperrmerker löschen
  }
  motor();
}

void motor()
{
  if (antrieb == true)
  {
    digitalWrite(dirPin, LOW);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(471);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(471);
    Serial.println("Laeuft");
  }
  else
  {
    digitalWrite(stepPin, LOW);
  }
}

Hallo prinzhermann,

willkommen im Arduino-Forum. Super dass du den Sketch als Code-Section gepostet und die hardware beschrieben hast.

xy das ist mal wieder typisch. Nicht den code vom TO analysieren sondern deine persönliche Lieblingsversion draufschmeißen. Wahrscheinlich wieder mit bugs gefüllt
dass es erst nach dem fünften mal abändern wirklich funktioniert.

xy ist schon ein urkomischer Typ nicht xy sondern eher woozel-goozel zzz-3000

Zu deinem Code

diese Zeile liest den Zustand des Sensors und speichert den Zustand in der Variablen "detect"

weiter unten im Code verwendest du dann die Variable detect in den If-bedingungen
Im Prinzip sinnvoll. An bestimmten Stellen muss man aber den Wert aktualisieren bzw.
Abfragen nicht "verschachteln".

wenn die Vorbedingung detect == 0 erfüllt ist nur dann prüfe auf
detect == 1

das kann niemals erfüllt sein. Deshalb funktioniert die Logig an der Stelle nicht.

Meine Empfehlung zuerst eine Beschreibung in normalen Worten und konsequenter Vermeidung von Programmierbegriffen.

Du schreibst

Mindestabstand "8"
T2-0-1-2-3-4-5-6-7-8-T1
alles iO.
..........T2-0-1-2-3-4-5-6-7-8-T1
....................T2-0-1-2-3-4-5-6-7-8-T1
..............................T2-0-1-2-3-4-5-6-7-8-T1
........................................T2-0-1-2-3-4-5-6-7-8-T1
..................................................T2-0-1-2-3-4-5-6-7-8-T1

Teile bewegen sich im passenden Abstand über das Band

zu kleiner Abstand
T2-0-1-2-3-T1

Das Band wird für zwei Sekunden angehalten
1 Sekunde
2 Sekunden
weiterfahren
T2-0-1-2-3-T1
..........T2-0-1-2-3-T1
....................T2-0-1-2-3-T1
..............................T2-0-1-2-3-T1
........................................T2-0-1-2-3-T1
..................................................T2-0-1-2-3-T1

Davon alleine ändert sich der Abstand der Teile nicht.
Also irgendwie fehlt da noch was in der Beschreibung.

Entweder es gibt ein zweites Band und dann macht anhalten von Band 1 Sinn
weil das Teil auf Band 2 dann ein Stücken weitertransportiert ist
oder wenn es nur ein Band gibt dann muss das zu dicht folgende Teil irgendwie
mechanisch festgehalten werden und das Band unter dem Teil "durchrutschen" bis der Abstand passt.

Gibt es dazu schon einen realen Aufbau?
Wenn ja Fotos posten. Die kann man einfach ganz direkt aus der Zwischenablage mit Strg-V hier einfügen.

vgs

Wolltest Du mir gerade was erzählen?
Möchtest Du noch mal nachlesen?
Dann sprechen wir über typisch.
Die Aufgabe ergibt sich aus dem Titel.
Ich warte jetzt nur bis die Zeit um ist.
Du darfst natürlich gerne hier drunter

antrieb = false;                         // Antrieb aus

noch eine Wartezeit mit delay(2000); einbauen.

Doch.

Wenn der IR-Sensor kurz vor dem Abgrund steht...

ist ein Wort aus der Geometrie.
Wenn damit Mindestzeitabstand gemeint ist, dann macht es Sinn.
Noch ein Beispiel dafür, dass man sich beim Programmieren und kommunizieren übers Programmieren präzise ausdrücken muss.
vgs

Ja und? Ist doch ok.
Schon mal gesehen, wie Vereinzel-Bänder funktionieren?
Teil 1 fällt runter, Teil 2 kommt zu früh, wartet und fällt dann runter.
Mindest-Abstand auf Band 2 ist immer gegeben.

Hallo und ein herzliches Dank, dafür dass Ihr Euch meinem Problem angenommen habt.

Richtig, so ist mein Vorhaben
Danke für den Hinweis bzgl. des Typs bei der Verwendung von millis(), ich werde es beherzigen und ändern.

Vielen Dank für die Mühe mit dem aktualisieren des Codes!
Diesen Ansatz finde ich gut, ich werde mal versuchen den Umzusetzen (Der Code hat nicht auf Anhieb funktioniert, aber ich werde mich da gleich auch nochmal genauer Einlesen)

@StefanL38
Vielen Dank für das nette willkommen heißen im Forum.
Danke, du hast recht, dass kann so nicht funtkionieren mit der Abfrage ohne den Wert zu aktualisieren.
Ich werde auch hier gleich mal schauen, ob ich den Code sinnvoll anpassen kann.

Diese Verwendungsart klappt leider nicht, da die Teile keine Bekannte länge haben.
Deswegen wollte ich es mit dem IR Sensor erkennen, sobald das Teil A vorbei ist und merken ob Teil B mind. 2 Sekunden Abstand hat.

Das stimmt, da war ich nicht präzise, es soll der Mindestzeitabstand sein.
Die Teile sollen laut Plan auf ein zweites Förderband fallen und dadurch wird durch das Stoppen des Bands1 ein Abstand der Teile erzeugt.

Nein, leider gibt es noch keinen realen Aufbau, aber die Teile sind bestellt. :slight_smile:

Genau das ist mein Ziel. :slight_smile:

Ersetze das loop(). Hinweis: Die Variable heisst jetzt isDetect :slight_smile:

void loop()
{
  if (digitalRead(sensorPin) == LOW)         // Sensor detektiert etwas
  {
    isDetect = true;                         // Merken
  }
  if (millis() - detectTime <= mindestZeit)  // letzte detektZeit ist zu kurz? ...
  {
    if (isDetect == true)                    // ... und was detectiert?
      antrieb = false;                       // Antrieb aus
  }
  else                                       // letztes detekt ist lange her
  {
    antrieb = true;                          // Antrieb ein
    if (digitalRead(sensorPin) == HIGH)      // Wenn nichts mehr vor dem Sensor steht
    {
      if (isDetect == true)                  // Merker ist gesetzt? ...
      {
        detectTime = millis();               // ... dann Zeit merken
        isDetect = false;                    // Merker immer löschen
      }
    }
  }
  motor();
}

Du bekommst jetzt als Messzeit immer die zwischen den Teilen.
Start, wenn nichts mehr vor dem Sensor ist; stop beim nächsten Teil.
Weiter läufts, wenn die Zeit vorbei ist.

1 Like

OK jetzt ist der Ablauf klar:

Teile unterschiedlicher Länge kommen auf dem Band angefahren.
Sensor schaltet auf "Teil unter mir" wenn das Teil zu Ende ist schaltet Sensor auf "kein Teil"
Zu diesem Zeitpunkt einen Zeitstempel speichern.
Das Band läuft (erst einmal) weiter.
Wenn der Sensor das nächste mal auf "Teil unter mir" schaltet wird berechnet wie viel Zeit ist seit dem letzten Umschalten "kein Teil" vergangen?
Wenn mehr als 2 Sekunden dann ist sowieso alles iO. Band weiterlaufen lassen
Wenn weniger als 2 Sekunden dann Band stoppen bis 2 Sekunden vergangen sind
dann Band weiterfahren lassen.

Es kommt also darauf an das Umschalten von "Teil unter mir" auf "kein Teil"
und das Umschalten "kein Teil" auf "Teil unter mir"
zu erfassen.

Das nennt man Flankenerkennung.
Vorschlag für die Variablennamen:
boolean TeilAnfangErkannt
unsigned long TeilEndeZeitStempel;

Wenn man sich die Zeit nimmt die Variablennamen sorgfältig so festzulegen,
das die Variablennamen selbsterklärend sind erleichtert das das Programmieren sehr.

Die Zeitdifferenz seit TeilEnde erkannt wurde berechnet sich dann mit
millis() - TeilEndeZeitStempel

Wenn jetzt das nächste Teil detektiert wird aber diese Zeitdifferenz kleiner als 2000 Millisekunden ist dann ===> das Band STOP!

bis zwei Sekunden vergangen sind. Dann das Band weiterlaufen lassen.

Eine grobe Programm-Skizze mit selbsterklärenden Namen und weiteren erklärenden Kommentaren. Programmskizze bedeutet compiliert noch nicht. Da müssen noch weitere Zeilen dazu.

Der eine oder andere "Profi" wird jetzt wegen der vielen Kommentare möglicherweise die Nase rümpfen.

Hallo!

Dieses Programm dient dazu zu erklären wie man es programmiert

byte letztesMalTeilUnterMir;
byte TeilUnterMir;
boolean TeilAnfangErkannt;
unsigned long TeilEndeZeitStempel;


TeilUnterMir = digitalRead(TeileSensor);

// Flankenerkennung: nur wenn sich Sensor-Signal ÄNDERT
if (letztesMalTeilUnterMir == LOW && TeilUnterMir == HIGH) { // Flanke von kein Teil auf Teil = Teilanfang
  TeilAnfangErkannt = true;

  // letztesMalTeilUnterMir aktualisieren damit 
  // Flankenmerker TeilAnfangErkannt beim Durchlaufen 
  // des Teils NICHT immer wieder neu gesetzt wird 
  letztesMalTeilUnterMir = TeilUnterMir; 
}



// Flankenerkennung: nur wenn sich Sensor-Signal ÄNDERT
if (letztesMalTeilUnterMir == HIGH && TeilUnterMir == LOW) { // Flanke von  Teil auf kein Teil = Teilende
  TeilEndeZeitStempel = millis(); 
  
  // letztesMalTeilUnterMir aktualisieren damit Zeitstempel 
  // beim weiterlaufen des Teils NICHT  immer wieder neu gespeichert wird 
  letztesMalTeilUnterMir = TeilUnterMir; 
}


if(TeilAnfangErkannt && millis() - TeilEndeZeitStempel < 2000 ) {
  // wenn der nächster Teilanfang erkannt wurde
  // UND noch KEINE 2000 Millisekunden seit dem Ende des vorherigen Teils vergangen sind
  BandEingeschaltet = false; // <==== Merker umschalten auf BAND STOP!
}
else { // else-Fall:  genug Zeit vergangen oder noch kein weiterer Teilanfang detektiert    
  if (TeilAnfangErkannt) {
    // wenn neues Teil "ansteht"
    BandEingeschaltet = true;    // <== Merker umschalten auf Band laufen lassen
    TeilAnfangErkannt = false;   // Flankenmerker zurücksetzen
  }  
}


if (BandEingeschaltet) {
  MotorEin();
}
else {
  MotorAus();
}

letztesMalTeilUnterMir = TeilUnterMir; 

Die Redundanz der Zeile

letztesMalTeilUnterMir = TeilUnterMir; 

Habe ich mit voller Absicht des besseren Verstehens wegen so reingesetzt

vgs

1 Like

Das Programm von xy funktioniert im Prinzip gleich.
Er hat einige Zeilen die ich Programmiert habe eingespart.
Mein Kommentar dazu:

Realisiert genau die geforderte Funktionialität Transportband-Vereinzeler.
Erklärt aber weniger Grundprinzipien.
vgs

Hallo,
ich Danke Euch beiden sehr, nicht nur für die Qualität der Antworten sondern auch für die Geschwindigkeit!

Das ist sehr gut Beschrieben und hilft mir beim Verstehen und nachvollziehen.

Dein Code ist ebenfalls sehr verständlich, vielen Dank!
Er macht genau das was er soll.

Nun habe ich aber ein neues Problem mit den Codes:
Der Motor schafft nun nicht mehr die Geschwindigkeit und ruckelt eher als dass es eine fließende Bewegung ist.
(Die Motor Bewegung habe ich vorher alleine in einem Programm getestet und die Dreh-Geschwindigkeit hatte als fließende Bewegung gepasst)

Mein Versuch einer Erklärung:
Das Programm wird durch den Loop-Durchlauf immer Neugestartet und bekommt so keine flüßigen Bewegungsablauf hin.

Bin ich da auf dem Holzweg?
Habt Ihr vielleicht dazu eine Erklärung?

Ich wünsche Euch einen schönen Sonntag!

viele Grüße

Dazu musst du schon dein ganzes Programm posten
You can post code by using this method that adds the code-tags
There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

Hast Du zu dem Motor ein Datenblatt?
Und dann verkürz die delayMicros() mal auf was 2stelliges im ms-Bereich.
Nach dem LOW ggfls. sogar kürzer als nach dem HIGH auf dem stepPin.

na da kann ja prinzhermann gleich zwei Anfängerfehler kennenlernen
baudrate oldschool 9600
und dann nach jedem Schritt etwas seriell ausgeben wollen

Warst Du gestern nicht grad der Meinung das es doch ganz doll Scheisse ist, wenn man den Code umschreibt?
Nu mache ich das nicht und las den Originalcode stehen und das passt Dir auch nicht?

Alles in Ordnung?

Mikrosekunden 2stelling im ms das heißt also 5-stellige Anzahl Mikrosekunden
delayMicros(30000);
warum denn nicht einfach
delay(30);

Wenn Du es nicht verstehst, was und warum das da steht, dann frage.

  • Ist dies ein professionelles Forum: Nein.
  • Kennen wir die Fragesteller: Nein.
  • Was ist die Stärke von so einem Forum: Vielfalt!

Wenn Du eine gute Idee zu haben scheinst, wie Du dem Fragesteller helfen kannst, dann mach es einfach. Bitte laß aber den anderen die Freiheit, ihren eigenen Stil zur Hilfe zu nutzen. Der Fragesteller kann dann das nehmen, was ihm hilfreich erscheint, was er versteht und auf ihn paßt.

Das Programm in #1 habe ich nicht verstanden, weshalb ich auch keine Idee hätte, Verbesserungen vorzuschlagen. Es hilft mir dann, die Aufgabenstellung ganz neu in ein Programm umzusetzen, um mögliche Problemstellen zu erkennen. Wäre dann aber doch blöd, so ein Programm in der Schublade verschwinden zu lassen, finde ich.

Die Bibliothek MobaTools (über die IDE zu installieren) arbeitet mit Interrupts für die Schritte und hat einen Sanftanlauf (Geschwindigkeitsrampe) eingebaut. Bei Interesse gerne mehr Infos :slightly_smiling_face:

Die Bibliothek wurde für Modellbahnen entwickelt, daher der Name, ist aber für Deine Anwendung bestens geeignet.

1 Like

Hallo an Alle

herzlichen Dank für die Anworten!

// Stepper Motor X

const byte stepPin = 2;          //X.STEP
const byte dirPin = 5;           // X.DIR
const byte sensorPin = 9;
const uint32_t mindestZeit = 2000;
uint32_t detectTime;
bool antrieb;
bool isDetect = false;

void setup()
{
  Serial.begin(9600);
  pinMode(stepPin, OUTPUT);   // Step Signal fuer Geschw.
  pinMode(dirPin, OUTPUT);    //Dir -> direction
  pinMode(sensorPin, INPUT);        //Sensor eingang
  antrieb = true;
}
void loop()
{
  if (digitalRead(sensorPin) == LOW)         // Sensor detektiert etwas
  {
    isDetect = true;                         // Merken
  }
  if (millis() - detectTime <= mindestZeit)  // letzte detektZeit ist zu kurz? ...
  {
    if (isDetect == true)                    // ... und was detectiert?
      antrieb = false;                       // Antrieb aus
  }
  else                                       // letztes detekt ist lange her
  {
    antrieb = true;                          // Antrieb ein
    if (digitalRead(sensorPin) == HIGH)      // Wenn nichts mehr vor dem Sensor steht
    {
      if (isDetect == true)                  // Merker ist gesetzt? ...
      {
        detectTime = millis();               // ... dann Zeit merken
        isDetect = false;                    // Merker immer löschen
      }
    }
  }
  motor();
}

void motor()
{
  if (antrieb == true)
  {
    digitalWrite(dirPin, LOW);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(7);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(7);
    Serial.println("Laeuft");
  }
  else
  {
    //digitalWrite(stepPin, LOW);
  }
}

Ja, das liegt mir vor:
es handelt sich im einen Bipolar Stepmotor von omc-Stepperonline:
*HerstellerNummer: 17HS15-1504S

  • Motorentyp: Bipolar Stepper
  • Schrittwinkel: 1.8 deg
  • Haltemoment: 42Ncm(59.49oz.in)
  • Strom/Phase: 1.50A
  • Widerstand/Phase: 2.0ohms
  • Induktivität: 3.9mH ± 20%(1KHz)

Das habe ich sogar auf eine einstellige Zahl verkürzt und denoch ist es eine rucklige Umdrehung in ca. 2 Sekunden und währenddessen vibriert der Motor ordetnlich.

Ja, davon gehe ich aus, dass ich noch einige Anfängerfehler mache und wahrscheinlich auch noch machen werde :slight_smile:
Desswegen bin ich umso glücklicher, dass es dieses Forum gibt und nette Leute, die auch Sonntags uneigennützig unterstützen!
Was meinst du mit oldschool 9600, was ist denn heute üblich?
(Das war die Zahl die ich beim durchstöbern des Netzes fand.
Serielle Ausgaben nicht nach jedem Schritt ausgeben, weil man das kaum verfolgen kann, richtig?
Ist es üblich bei einer Detektion eine serielle Ausgabe sich geben zu lassen (habe ich noch nicht implementiert) oder verwende ich diesen Ausgang gänzlich falsch?

War auch noch sehr verknotet in meinem Kopf, dass haben die zwei sehr gut umformuliert und klarer & verständlicher dargestellt.

Ich werde es mit der Moba Bibliothek versuchen. Danke für den Hinweis.

Aber wieso der Motor super rund läuft, wenn nur die Motorsteuerung in einem eigenen Programm läuft

      digitalWrite(dirPin, LOW);
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(471);
      digitalWrite(stepPin, LOW);
      delayMicroseconds(471);

aber fast nur vibriert, wenn es in dem kompletten Programm liegt, dass bereitet mir Kopfzerbrechen.

vielen Dank Euch allen!
Ich hoffe Ihr habt Alle einen tollen Sonntag Abend.
Viele Grüße
PrHermann