EV3: C# - Farbsensor programmatisch anwenden

NXC, C/C++, Lejos, pbLua, RobotC...

Moderator: Moderatoren

BorisEhlers
Neuer Schreiber
Neuer Schreiber
Beiträge: 2
Registriert: 27. Feb 2014 16:32

EV3: C# - Farbsensor programmatisch anwenden

Beitragvon BorisEhlers » 24. Feb 2015 17:23

Hi,

derzeit hänge ich an dem Problem, dass die Farberkennung mit dem "EV3 Color Sensor"
a) unzuverlässig und ...
b) mit einem Delay verbunden

... arbeitet. So sollen die Farben an auf einem Förderband befindlichen Bausteinen erkannt
und ausgegeben werden. Über die grafische Entwickleroberfläche werden die Farben 1-6 (entsprechend für die Farben: schwarz*), blau, grün, gelb, rot und weiß) korrekt erkannt und in nahezu Echtzeit verarbeitet. Dadurch kann die Logik beim Förderband korrekt reagieren.

Wenn ich jedoch programmatisch den Farbsensor im Color-Modus (nicht ReflectiveRaw, ReflectiveRGB oder dergleichen) anspreche, sobald sich in der Methode "void _brick_BrickChanged(...)" etwas ändert, braucht es
gefühlte 200ms, bis die erkannten Farbwerte ausgegeben bzw. verarbeitet werden. Die Folge ist, dass nur ein Bruchteil der Farben korrekt erkannt werden.

Meine erste Frage: Die Abtastrate des Farbsensors wird laut folgender Website mit 1kHz angegeben:
http://www.intorobotics.com/sensors-leg ... omparison/

Wird die Callback-Methode "_brick_BrickChanged(object sender, BrickChangeEventArgs e){...}" womöglich nicht
schnell genug durch den Lego Mindstorms aufgerufen? Wie lässt sich das Ändern? Ich versuchte es bereits durch eine externe Methode, die alle 50ms in einer while Schleife die Sensorwerte abfragte. Dies half jedoch nicht.

Da es über die grafische Entwickleroberfläche von Lego keine Zeitverzögerung gibt, sollte in C# ebenfalls eine Echtzeitdarstellung der Sensorwerte existieren.

Um die Zuverlässigkeit zu erhöhen würde mich zudem interessieren, ob brauchbare Farberkenner bzw. Konverter existieren, die mit dem HSV-Farbraum arbeiten (z.B. RGB2HSV-Konverter). Passende Tutorials oder Code-Snippets konnte ich leider nicht finden.

Für Tipps und Informationsquellen wäre ich dankbar, zumal die API-Dokumentation von Lego eher
übersichtlich ist.

Boris

*) okay, Schwarz ist eigentlich keine Farbe, sondern charakterisiert sich durch Abwesenheit selbiger

Technicmaster0
Schreibt super viel
Schreibt super viel
Beiträge: 379
Registriert: 22. Dez 2010 12:36
Wohnort: In Berlin rechts abbiegen
Kontaktdaten:

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Technicmaster0 » 25. Feb 2015 15:45

Wenn du mehr als 6-7 Farben erkennen willst kannst du den RGB Modus vom Farbsensor verwenden.
Da MonoBrick JIT kompilliert solltest du vllt. alle Methoden/ Funktionen einmal durchlaufen lassen bevor du mit "ernsthaften Messungen" anfängst. Danach sollte alles etwas schneller laufen. Ansonsten kann man ohne Quellcode schwer was sagen.

Pedant
Mindstormsfreund
Mindstormsfreund
Beiträge: 4
Registriert: 2. Jan 2016 14:04

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Pedant » 3. Jan 2016 18:38

Hallo,

diesen alten Thread wärme ich auf, da ich jetzt dieselben Fragen habe und leider auch keine Antworten.

Seit ein paar Tagen habe ich auch ein EV3 und versuche damit Sensordaten mit einen C#-Programm am PC auszulesen.
Das Auslesen ist nicht das Problem, aber wie Boris bin ich mit der Performance sehr unzufrieden.

Technicmaster0 hat geschrieben:Da MonoBrick JIT kompilliert solltest du vllt. alle Methoden/ Funktionen einmal durchlaufen lassen...

Ich verwende nicht MonoBrick, sondern die Lego.Ev3.Desktop.dll aus dem LEGO MINDSTORMS EV3 API for .NET von BrianPeek, die ich in mein C#-Projekt eingebunden habe.
Mein Programm läuft also auf dem PC und holt sich die Daten vom EV3, wahlweise per USB oder per Bluetooth.
Das Programm ist zu komplex, um es hier zu posten, aber ich habe die Grundfunktion der Kommunikation weiter unten als Quellcode gepostet.
Der gepostete Code macht Folgendes:
Ändern sich die Sensordaten, wird angezeigt die wievielte Änderung es ist,
wie lange (in Millisekunden) die vorhergehende Änderung zurück liegt
und welchen Wert der Sensor gerade ausgab.

Boris hat geschrieben:sobald sich in der Methode "void _brick_BrickChanged(...)" etwas ändert, braucht es gefühlte 200ms, bis die erkannten Farbwerte ausgegeben bzw. verarbeitet werden.

Bei meinem Testprogramm (siehe Code) komme ich mit Bluetooth nicht unter 150 ms Eventabstand und mit USB sogar nicht unter 1200 ms.
Das ist Beides weit entfernt von 1 kHz und jenseits dessen was Spaß macht.
Es ist dabei egal, ob ich den Farb- oder den Infrarotsensor auslese.

Boris hat geschrieben:Wird die Callback-Methode "_brick_BrickChanged(object sender, BrickChangeEventArgs e){...}" womöglich nicht schnell genug durch den Lego Mindstorms aufgerufen?
Wie lässt sich das Ändern?

Ich vermute, dass das BrickChanged-Event nicht wirklich ist, was der Name impliziert.
Man könnte anhand des Namens denken, dass der EV3 dieses Event auslösen würde, eher glaube ich, dass die API das Event selbst generiert, indem sie regelmäßig alle Sensordaten vom EV3 abfragt, vergleicht, ob eine oder mehrere Daten sich geändert haben und bei Änderung ein Event auslöst.
Man könnte sich mal durch den Quellcode des API quälen, um die Funktionsweise zu prüfen.
Ich hatte irgendwo gelesen, dass die API dafür eine Taktung von 100 ms hätte und dass die irgendwie auch zu ändern sei.
Leider hab ich die Quelle dafür vergessen und noch nicht wieder gefunden.
Stimmt meine Vermutung, so wird das "_brick_BrickChanged(object sender, BrickChangeEventArgs e){...}" nicht durch den EV3 aufgerufen.
Der EV3 muss lediglich dem PC die Abfrage der Sensordaten beantworten und diese Daten dabei per USB oder Bluetooth an den PC übermitteln.

Boris hat geschrieben:Ich versuchte es bereits durch eine externe Methode, die alle 50ms in einer while Schleife die Sensorwerte abfragte. Dies half jedoch nicht.

Hab ich auch versucht, also einen Task geschrieben, der in einem festen Takt genau einen Sensor abfragt und dessen Wert mit dem Vorhergehenden vergleicht und bei Unterschied irgendwas tut.
Wie schon geschrieben, vermute ich, dass das "_brick_BrickChanged"-Event auch nichts Anderes macht.
Bei meinen Task habe ich keinen Performanceunterschied feststellen können, egal ob ich ihn auf 100, 10 oder nur 1 ms takte.
Die Performance liegt nicht besser und nicht schlechter, als wenn ich mich des vorgekauten Events bediene.

Boris hat geschrieben:...zumal die API-Dokumentation von Lego eher übersichtlich ist.

Ein API-Dokumentation von Lego kenne ich nicht.
Hat Lego eine eigene API oder hat Lego die von BrianPeek dokumentiert
oder meintest Du die Dokumentation von BrianPeek für die API von BrianPeek?
Zur Letzteren habe ich Folgendes gefunden:
Den Getting Started Guide, ein Einführungsvideo und den Quellcode.

Getting Started Guide hat geschrieben:USB communication with the Desktop library is a bit laggy.

"A bit laggy" ist bei 1200 ms pro Wert noch sehr geschmeichelt.
Ich frage mich daher, ob ich irgendwas falsch mache oder ob "a bit laggy" auf deusch krötenlangsam heißt.

Was auch angeboten wird ist das hier: UsbCommunication.cs
Dazu heißt es: "Better desktop USB support (ahilevich and dsharlet)"
Ich werde mir das mal ansehen und vielleicht nutzt es mir etwas.

Hier noch der Code für das simple Testprogramm:

Code: Alles auswählen

using System;
using System.Windows.Forms;
using Lego.Ev3.Core;
using Lego.Ev3.Desktop;

namespace Ev3_Test
{
   public partial class Form1 : Form
   {
      Brick _brick;
      // Für Bluetooth "usb" auf false setzen
      bool usb = true;
      int step = 0;
      DateTime zeit = DateTime.Now;

      public Form1()
      {
      InitializeComponent();
      }

      private async void programm_start(object sender, System.EventArgs e)
      {
      // Falls per USB verbunden werden soll...
      if (usb) {_brick = new Brick(new UsbCommunication());};
      // Falls nicht per USB (also Bluetooth) verbunden werden soll...
      // "COM3" gegebenenfalls anpassen
      if (!usb) {_brick = new Brick(new BluetoothCommunication("COM3"));};
      // Eventhandler hinzufügen
      _brick.BrickChanged += _brick_BrickChanged;
      // Verbindung aufnehmen
      await _brick.ConnectAsync();
      }

      private void _brick_BrickChanged(object sender, BrickChangedEventArgs e)
      {
      step++; // Eventzähler erhöhen
      // Zeitdauer seit des letzten Events berechnen
      TimeSpan dauer = DateTime.Now - zeit;
      // "zeit" für die nächste Berechnung auf jetzt setzen
      zeit = DateTime.Now;
      // Anzeigen das wievielte Event erkannt wurde
      label1.Text = "Eventnummer: " + step.ToString();
      // Anzeigen wie lange das vorhergehende Event zurück liegt
      label2.Text = "Eventabstand: " + (dauer.Seconds * 1000 + dauer.Milliseconds).ToString() + " ms";
      // Anzeigen welchen Wert der Sensor übermittelte
      // InputPort.One gegebenenfalls anpassen
      label3.Text = "Sensorwert: " + e.Ports[InputPort.One].SIValue.ToString();
      }
   }
}

Zum Ausprobieren des Quellcodes sind noch vier Dinge zu erledigen:
1. Lego.Ev3.Desktop.dll als Referenz zum Projekt hinzufügen.
2. Im Designer für "Form1" als load-Event "programm_start" auswählen
3. Im Designer drei Label anlegen und sie "label1", "label2" und "label3" benennen.
4. Eventuelle Anpassungen gemäß der Kommentare im Code


Gruß Frank
EV3 mit Firmware v1.08H

Technicmaster0
Schreibt super viel
Schreibt super viel
Beiträge: 379
Registriert: 22. Dez 2010 12:36
Wohnort: In Berlin rechts abbiegen
Kontaktdaten:

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Technicmaster0 » 7. Jan 2016 19:18

Ich habe mit der EV3 API eher schlechte Erfahrungen gemacht (das Programm reagierte nicht mehr und man konnte es auf keinem Weg beenden bis mein Antivirenprogramm dazwischengegangen ist). Daher empfehle ich die MonoBrick Communication Library. Ob die schneller reagiert kann ich aber nicht sagen.

Pedant
Mindstormsfreund
Mindstormsfreund
Beiträge: 4
Registriert: 2. Jan 2016 14:04

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Pedant » 8. Jan 2016 09:37

Hallo Technicmaster0

...bis mein Antivirenprogramm dazwischengegangen ist

Vielleicht ist dabei die Lösung auch die Ursache?
Egal, darum geht's ja nicht.

Monobrick erfordert ein Linux auf dem EV3.
Wäre das reversibel, also das Originalbetriebssystem wieder etablierbar, falls mir Monobrick nicht zusagen würde?
Falls ja, probiere ich es gerne aus und "messe" damit den "Eventtakt".
Ich fang mal an zu Lesen was zum Monobrick-Installieren geschrieben wird.

Gruß Frank


Nachtrag

Meine Nachfrage ist schnell beantwortet:
Monobrick Firmware
Zitat: "The firmware fits on a single SD card you simply boot the MonoBrick firmware directly from the SD card without replacing the standard firmware."
MonoBrick Communication Library
Zitat: "The MonoBrick communication library is a LEGO Mindstorms communication library written in C# that works with the standard firmware on both the EV3 and NXT."

Man muss also das Originalbetriebssystem gar nicht entfernen/ersetzen, sondern bootet einfach die MonoBrick-Firmware von einer SDCard
und was mir bisher entgangen war: Es gibt auch eine MonoBrick-Library, die mit der Original-Firmware funktioniert.
Jetzt habe ich also zwei Dinge, die ich ausprobieren kann.
EV3 mit Firmware v1.08H

Technicmaster0
Schreibt super viel
Schreibt super viel
Beiträge: 379
Registriert: 22. Dez 2010 12:36
Wohnort: In Berlin rechts abbiegen
Kontaktdaten:

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Technicmaster0 » 8. Jan 2016 18:37

Und als kleiner Bonus unterstützt die Communication Library von Monobrick auch noch gleichzeitig NXT und EV3.

Pedant
Mindstormsfreund
Mindstormsfreund
Beiträge: 4
Registriert: 2. Jan 2016 14:04

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Pedant » 9. Jan 2016 01:44

Hallo,

Technicmaster0 hat geschrieben:Daher empfehle ich die MonoBrick Communication Library.

danke für den Hinweis.
Technicmaster0 hat geschrieben:Ob die schneller reagiert kann ich aber nicht sagen.

...ich jetzt schon:
Mit einem allerersten, dahingepfuschten Testprogramm erhalte ich über USB neue Werte im zirka 25-ms-Takt.
Das ist gegenüber zirka 1200 ms eine ganz andere Liga.


Apropos Testprogramm, hier ein reduzierter Ausschnitt:

Code: Alles auswählen

private void brick_auslesen(object sender, System.EventArgs e)
   {
   var meinbrick = new Brick<Sensor, Sensor, Sensor, Sensor>("usb");
   meinbrick.Connection.Open();
   label1.Text = meinbrick.Sensor1.ReadAsString();
   meinbrick.Connection.Close();
   }

Lässt sich meinbrick eigentlich nur als var deklarieren und bleibt damit lokal.

Beispielsweise das hier compiliert nicht.

Code: Alles auswählen

MonoBrick.EV3.Brick meinbrick = new Brick<Sensor, Sensor, Sensor, Sensor>("usb");

Mir fehlt im Moment ein Ansatz in einem Windows-Forms-Programm meinbrick genau einmal zuzuweisen und dann nur einmal die Verbindung zu öffnen.
Im Moment mache ich es bei jedem Aufruf von brick_auslesen() und den Aufruf in einer schnellen Wiederholung per Timer.
Das kann's irgendwie nicht sein, aber Beispiele habe ich leider nirgends gefunden.

Gruß Frank
EV3 mit Firmware v1.08H

Pedant
Mindstormsfreund
Mindstormsfreund
Beiträge: 4
Registriert: 2. Jan 2016 14:04

Re: EV3: C# - Farbsensor programmatisch anwenden

Beitragvon Pedant » 10. Jan 2016 00:04

Hallo,

auf meine letzte Frage habe ich heute eine Antwort gefunden und die Kommunikation per USB und BT läuft richtig flott.
Das Topic wäre für mich daher gelöst und für den TopicOwner "BorisEhlers" vermutlich auch.

Zum Abschluss:

Ich habe heute das simple Testprogramm, dass ich für die Lego.Ev3.Desktop.dll geschrieben und hier als Quellcode gepostet hatte, umgeschrieben für die MonoBrick.dll.
Dort gibt es kein "BrickChangedEvent", daher habe ich das Auslesen als TimerEvent gestaltet, als Alternative zu einer simplen Endlosschleife (while (true) {tuwas;};)
Der Code kann natürlich auch als Ausgangsbasis dienen für andere Programme, die den Brick steuern sollen.

Zu erwähnen sein noch, dass es sich um die DLL handelt, die mit der Originalfirmware spricht und nicht um die Variante für die MonoBrick EV3 Firmware.

Der Code sieht jetzt so aus:

Code: Alles auswählen

using System;
using System.Windows.Forms;
using MonoBrick.EV3;

namespace Ev3_Test
{
   public partial class Form1 : Form
   {
      // Hier wird die Variable "meinbrick" angelegt
      MonoBrick.EV3.Brick<Sensor, Sensor, Sensor, Sensor> meinbrick;
      // Für Bluetooth muss, anselle von "com3", der tatsächliche serielle Bluetooth-Port verwendet werden
      string verbindungsart = "usb"; // "wifi" // "com3" (serieller Bluetooth-Port)
      // Hier wird vorbelegt, wie schnell der Timer sich wiederholen soll (in Millisekunden)
      int timertaktung = 1;
      // Nur eine Zählvariable
      int step = 0;
      // Nur eine Variable zum Speichern des Sensorwertes
      string sensorwert = "";
      // Nur eine Variable zum Speichern der Zeit
      DateTime zeit = DateTime.Now;
      // Hier wird der Timer "timer_auslesen" angelegt
      System.Windows.Forms.Timer timer_auslesen = new System.Windows.Forms.Timer();

      public Form1()
      {
      InitializeComponent();
      }

      private void programm_start(object sender, System.EventArgs e)
      {
      // Konfiguration des Timers (Wiederholungsintervall)
      timer_auslesen.Interval = timertaktung;
      // Konfiguration des Timers (Was soll aufgerufen werden, wenn der Timer ablief, hier: "void auslesen()")
      timer_auslesen.Tick += new EventHandler(auslesen);
      // "meinbrick" wird belegt, variabel ist hier nur die Verbindungsart, die oben schon vorbelegt wurde
      meinbrick = new Brick<Sensor, Sensor, Sensor, Sensor>(verbindungsart);
       // Verbindung zum Brick wird geöffnet
       meinbrick.Connection.Open();
      // Falls der Modus eines Farbsensors festgelegt werden soll
      // meinbrick.Sensor1 = new ColorSensor(ColorMode.Color);
      // Der Timer wird gestartet
      timer_auslesen.Start();
      }

      // Wird aufgerufen, wenn der Timer abläuft
      private void auslesen(object sender, System.EventArgs e)
      {
      // Der Timer wird gestoppt, damit "auslesen" abgearbeitet werden kann, bevor der Timer erneut abläuft
      timer_auslesen.Stop();
      // Merken was der Sensor beim letzten mal meldete
      string sensorwertvorher = sensorwert;
      // Auslesen und merken was der Sensor jetzt meldet
      sensorwert = meinbrick.Sensor1.ReadAsString();
      //Prüfen, ob der Wert sich geändert hat und falls ja...
      if (sensorwert != sensorwertvorher)
         {
         // Die Zählvariable um 1 erhöhen
         step++;
         // Die Zeitspanne berechnen und in "dauer" speichern
         TimeSpan dauer = DateTime.Now - zeit;
         // Die Jetzt-Zeit in "zeit" speichern
         zeit = DateTime.Now;
         // Die Zählvariable anzeigen lassen
         label1.Text = "Wertänderungen: " + step.ToString();
         // Die Dauer anzeigen lassen
         label2.Text = "Erkennungsabstand: " + (dauer.Seconds * 1000 + dauer.Milliseconds).ToString() + " ms";
         // Den neuesten Sensorwert anzeigen lassen
         label3.Text = "Sensorwert: " + sensorwert;
         }
      // Alle Aufgaben beendet, daher den Timer wieder starten, damit er den nächsten Durchlauf auslösen kann
      timer_auslesen.Start();
      }

      // Wird aufgerufen, wenn der Button "Beenden" angeklickt wurde
      private void beenden_button(object sender, System.EventArgs e)
      {
      // Den Timer anhalten
      timer_auslesen.Stop();
      // Die Verbindung zum Brick schließen
      meinbrick.Connection.Close();
      // Das Programm schließen
      this.Close();
      }
   }
}


Zum Ausprobieren des Quellcodes sind noch ein paar Dinge zu erledigen:
1. MonoBrick.dll als Referenz zum Projekt hinzufügen.
2. hidapi.dll und LibUsbDotNet.dll von Hand in den Ordner kopieren, in dem sich die aus dem Code komplierte Exe befindet (befinden wird)
3. Im Designer für "Form1" als load-Event "programm_start" auswählen
4. Im Designer drei Label anlegen und sie "label1", "label2" und "label3" benennen
5. Im Designer eine Button anlegen und "Beenden" benennen
6. Im Designer dem Button "Beenden" als Click-Aktion "beenden_button" zuweisen
7. Eventuelle Anpassungen gemäß der Kommentare im Code

Ich habe im Code Alles kommentiert, um Nachfragen möglichst unnötig zu machen.
Die DLL-Dateien sind im Library DLL Download zu finden.

Gruß Frank
EV3 mit Firmware v1.08H


Zurück zu „textbasierte Programmiersoftware“

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 14 Gäste

Lego Mindstorms EV3, NXT und RCX Forum : Haftungsauschluss