Sonntag, 10. März 2013

Monzoom Pro Rückblick

ADLER-CLASS-06
Raumschiff U.S.S.Adler
 
Das Programm hieß vor seiner Umbenennung Reflection und war (glaube ich) ein beliebtes Werkzeug zum modulieren, texturieren und animieren in den 1990iger Jahren.
FUCHS LL-03A
Radpanzer
 
Während meiner Schulzeit habe ich viele Modelle entworfen oder andere Objekte nachgemacht. Das Programm begleitete mich bis 2002 und zwang meinen PC in die Knie, wenn ich eine Filmsequenz gerendert habe.
image
Angler am Strand 3D
 
Letztes Jahr habe ich in meiner Datensicherung meine gesammelte Arbeit wiedergefunden und ich dachte, dass rendere ich mal in High Definition. Wer bereits die Videos gesehen hat, die ich auf meinem Blog gepostet habe, dürfte sich an einen Pixelhaufen erinnern. Früher hatte ich nur einen Pentium 300MHz PC, mit einer Eraser III und 64MB Arbeitsspeicher (später jedoch auf 192MB aufgerüstet). Nun denn, genug Nostalgie.

Ein zusammenschnitt der 3D Animationen mit Monzoom Pro in HD Auflösung.

Sonntag, 3. März 2013

Daten vom Arduino verarbeiten und visualisieren

image
Arduino Nano 3.0 und der Magnet Sensor HMC5883L.
Gelb=SDA (Pin4), Orange=SCL (Pin5), Rot=+5V und Schwarz=GND

Wenn ich keine Lösung für dem Netduino habe, dann finde ich eine für Arduino. Eines meiner ersten Programme war eines zum Lesen der Sensordaten. Zunächst verwendete ich den Arduino Monitor, der allerdings nichts anderes tat, als eine serielle Verbindung herzustellen. Mit ein “wenig” C# und .NET Framework Kenntnis kann auch ein eigenes Programm geschrieben werden, das die Daten ebenfalls lesen kann. Mein neues Bespiel zeigt die magnetischen Einflüsse auf 3 Achsen an, was ich mit Hilfe von Windows Forms visualisiere.

image
Windows Forms mit analoger Anzeige

Warum Windows Forms? Die GUI ist schon etwas in die Jahre gekommen und bietet kaum Neuerungen, dennoch kann man grafisch immer noch etwas an Ergebnissen erreichen. Also wäre es doch schön, eine analoge Anzeige auf digitalem Boden zu schaffen. Aber erstmal eins nach dem anderen.

Auf der Seite Gihub stellt der Entwickler Jeff Roberg seine Bibliotheken zur Verfügung für Zahlreiche Sensoren die mit dem Arduino angesprochen werden können. Für mein Beispiel wird der Ordner HMC5883L und die I2Cdev Inhalt benötigt, die dann in den Order “libraries” vom Arduino Compiler kopiert werden. Anschließend kann das mitgelieferte Beispiel “HMC5883L_Example” geöffnet und auf dem Arduino hochgeladen werden. Die Ausgabe erfolgt über die serielle Verbindung, die auf dem Arduino Monitor betrachtet werden kann.

image
Ausgabe über den Monitor

Nun zur PC Anwendung, die die Daten auswertet und sie in einer visuellen Form darstellt. Als erstes stellen wir die Verbindung sicher, und hier hilft uns das .NET Framework mit der Klasse “SerialPort”. Damit wir die Einstellungen auch später in der Anwendung anpassen können, benötigen wir eine kleine Setup Möglichkeit. Hier reichen die Auswahl des Ports und der Baudrate.

image
Das kleine Setup mit einem Button zum “Aktualisieren” und einem für “Verbinden”.

In “Form1.cs” Code Seite kommt zunächst die Event Methode, die durch einen Doppelklick auf dem Button im Designer erstellt wird. Dort schreiben wir etwas Code, um die Auswahl über die ComboBoxen auszufüllen.

private void buttonUpdate_Click(object sender, EventArgs e)
{
    comboBoxChooseCom.Items.Clear();
    foreach (string item in SerialPort.GetPortNames())
    {
       comboBoxChooseCom.Items.Add(item);
    }

    comboBoxChooseBaud.Items.Clear();
    comboBoxChooseBaud.Items.Add(9600);
    comboBoxChooseBaud.Items.Add(38400);
    comboBoxChooseBaud.Items.Add(115200);
}

Füllt die ComboBox mit den Einstellungen.

Die SerielPort Klasse wird nun als Member in der “Form1.cs” angelegt. Einen weiteren Button, der den Text “Verbinden” erhält, erstellen wir ebenfalls durch Doppelklick auf die Event Methode. Hier initialisieren wir die Klasse mit den Einstellungen aus der ComboBox. Damit die Anwendung fehlerfrei läuft, sollte vorher geprüft werden, ob eine Auswahl besteht. Nach erfolgreicher Verbindung muss noch ein neuer Event erzeugt werden, der immer ausgeführt wird, sobald am seriellen Port neue Daten verfügbar sind.

private void buttonConnect_Click(object sender, EventArgs e)
{
     if (!string.IsNullOrEmpty(comboBoxChooseCom.Text) &
          !string.IsNullOrEmpty(comboBoxChooseBaud.Text) &

          _SerialPort == null)
     {
         _SerialPort = new SerialPort(
                 comboBoxChooseCom.Text,
                 (int)comboBoxChooseBaud.SelectedItem,
                 Parity.None, 8, StopBits.One);
         _SerialPort.DataReceived +=
                 new SerialDataReceivedEventHandler(
                 _SerialPort_DataReceived);

         _SerialPort.Open();
     }
}

private void _SerialPort_DataReceived(
     object sender, SerialDataReceivedEventArgs e)
{

}

Mit den Einstellungen die Verbindung herstellen.

Jetzt kommen wir zum visuellen Teil der Anwendung, wofür wir ein neues UserControl anlegen, indem wir in unserem Projekt mit Rechtsklick das Kontextmenü öffnen und dann über Hinzufügen >  Neues Element.. das Benutzersteuerelement auswählen. Zuvor sollte ein entsprechender Name verwendet werden wie “AnalogView.cs” oder ähnliches. In meinem Bespiel hat das UserControl eine Größe von 200x100 Punkten, was über die Layout Eigenschaften festgelegt werden kann. Anschließend geht es auf der Code Seite weiter.

public partial class UserControlAnalogView : UserControl
{
   private Point _PointerValue = new Point(100, 20);
   private double _DisplayRawValue = 0.0;
    private double _MaximumValue = 90;

   public double Maxi_MinimumValue
    {
        set
        {
            _MaximumValue = value;
            _MinimumValue = 0 - _MaximumValue;
        }
       get { return _MaximumValue; }
    }

    public UserControlAnalogView()
    {
        InitializeComponent();
    }

    public void RenderAnalogDisplay()
    {
        SuspendLayout();

       Graphics g = CreateGraphics();
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

        Pen p = new Pen(new SolidBrush(Color.Black), 2);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;

        g.FillPie(Brushes.WhiteSmoke, 5, 5, 190, 190, 0, -180);

        g.DrawPie(p, 5, 5, 190, 190, 0, -180);

        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
        p.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
        g.DrawLine(p, new Point(100, 100), _PointerValue);

        ResumeLayout(false);
    }

    public double DisplayValue
    {
        set
        {
            _DisplayRawValue = value;

            double alpha = _DisplayRawValue / _MaximumValue;

            double w1 = Math.Cos(alpha) * 90;
            double w2 = Math.Sin(alpha) * 90;

            _PointerValue = new Point((100 + Convert.ToInt32(w2)), 100 - Convert.ToInt32(w1));

            RenderAnalogDisplay();
        }
        get { return _DisplayRawValue; }
    }
}

Der Codeabschnitt für das Erstellen der Analoganzeige. Die Methode “RenderAnalogDisplay()” zeichnet die Anzeige, und über “DisplayValue” wird die Zeigerposition neu ausgerichtet.

Das Benutzersteuerelement ist fertig und nach dem Drücken der F6 Taste steht es auch schon in unser Toolbox, wo wir es in der Designeransicht auf die Form1 ziehen können. Für jede Achse wird eine analoge Anzeige verwendet; man stelle in den Eigenschaften das Maximum auf 4000 ein.

image
Die Analoge Anzeige wird erst gerendert, wenn die Methode im UserControl “RenderAnalogDisplay()” ausgeführt wird.

Als nächstes kommt das Auslesen der empfangen Werte. Der Inhalt sind Konstanten und können durch eine Methode einfach ausgewertet werden.

Raw: 455  39  668  Scaled:  418.60  35.88  614.56  Heading:  0.13 Radians  7.52 Degrees 
Die Zeichenkette, die uns der Arduino nach dem lesen des Sensor übermittelt.

Im Beispiel beschränken wir uns auf die Raw Daten und programmieren entsprechend in die Event Methode vom SerialPort den Lesevorgang und schreiben das Ergebnis an die analogen Anzeigen.

private void _SerialPort_DataReceived(
      object sender, SerialDataReceivedEventArgs e)
{
        string content = _SerialPort.ReadLine();

       string[] sa = content.Split('\t');
       string[] sa2 = sa[1].Split(' ');

        int[] ia = new int[3];
        int axis = 0;
        for (int i = 0; i < sa2.Length; i++)
        {
            if (!string.IsNullOrEmpty(sa2[i]))
            {
               int.TryParse(sa2[i], out ia[axis]);
                axis++;
            }
        }

        userControlAnalogView1.DisplayValue = ia[0];
        userControlAnalogView2.DisplayValue = ia[1];
        userControlAnalogView3.DisplayValue = ia[2];
    }
}

Methode für das Einlesen der Raw Daten.

An dieser Stelle sind wir mit dem Beispiel fertig und die Anwendung kann nun über F5 gestartet werden. Mit Aktualisieren erhalten wir die verfügbaren COM Verbindungen und wählen natürlich die vom Arduino.

Nähert man sich auf einige Zentimeter an den Sensor bewegen sich die Zeiger.

Mit einem Magneten können wir das Magnetfeld um den Sensor manipulieren und sehen, wie die Zeiger ausschlagen. Mit ein paar weiteren Codezeilen lässt sich auch der Rest der Daten anzeigen, allerdings verbleibe ich dabei, mich möglichst kurz zu halten.

Zuletzt darf die bereits fertiggestellte und kommentierte Solution des kleinen Tools nicht fehlen:

Arduino Nano 3.0 und MPU6050 in Quellcode Kurzfassung

image
Verkabelungsbeschreibung ist am ende dieses Blogs.

Bereits Anfang des Jahres habe ich eine Lösung in C#  und .NET Micro Framework geschrieben. Ursprünglich habe ich die Lösung aus der in C++ geschriebenen Library portiert (Library für Arduino). Zuvor suchte ich nach einem kürzeren und funktionalem Beispiel, aber ohne Erfolg. Da ich nun selbst relativ gut mit dem Sensor vertraut bin, konnte ich ein kürzeres Beispiel für den Arduino schreiben.

#include <Wire.h>

#define sensor_address 0x68

void setup()
{
  // 9600 Baut reichen zum Betrachten aus.

  Serial.begin(9600);
  Wire.begin();

  delay(1000);
 
  // Powermanagement aufrufen
  // Sensor schlafen und Reset, Clock wird zunächst von Gyro-Achse Z verwendet
 
  Serial.println("Powermanagement aufrufen - Reset");
  SetConfiguration(0x6B, 0x80);
 
  // Kurz warten
  delay(500);
 
  // Powermanagement aufrufen
  // Sleep beenden und Clock von Gyroskopeachse X verwenden

  Serial.println("Powermanagement aufrufen - Clock festlegen");
  SetConfiguration(0x6B, 0x03);
 
  delay(500);
 
  // Konfigruation  aufrufen
  // Default => Acc=260Hz, Delay=0ms, Gyro=256Hz, Delay=0.98ms, Fs=8kHz

  Serial.println("Konfiguration aufrufen - Default Acc = 260Hz, Delay = 0ms");
  SetConfiguration(0x1A, 0x00);
 
  // Leerzeichen
  Serial.println("");
}

void loop()
{
  byte result[14];
  // Anfangs Adresse von Beschleunigungssensorachse X
  result[0] = 0x3B;
 
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Anfangsadresse verwenden.
  Wire.write(result[0]);
  Wire.endTransmission();
  // 14 Bytes kommen als Antwort
  Wire.requestFrom(sensor_address, 14);
  // Bytes im Array ablegen
  for(int i = 0; i < 14; i++)
  {
    result[i] = Wire.read();
  }
 
  // Zwei Bytes ergeben eine Achsen Wert und könenn

// per Bit Shifting  zusammengelegt werden.
 
  // Beschleunigungssensor
  int acc_X = (((int)result[0]) << 8) | result[1];
  int acc_Y = (((int)result[2]) << 8) | result[3];
  int acc_Z = (((int)result[4]) << 8) | result[5];
 
  // Temperatur sensor
  int temp = (((int)result[6]) << 8) | result[7];
 
  // Gyroskopesensor
  int gyr_X = (((int)result[8]) << 8) | result[9];
  int gyr_Y = (((int)result[10]) << 8) | result[11];
  int gyr_Z = (((int)result[12]) << 8) | result[13];
 
  // Ausgabe
  Serial.print("ACC X:\t");
  Serial.print(acc_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(acc_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(acc_Z); Serial.print("\t");
  Serial.print("Temperatur:\t");
  Serial.print(temp); Serial.print("\t");
  Serial.print("Gyroskope X:\t");
  Serial.print(gyr_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(gyr_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(gyr_Z); Serial.println("\t");
}

void SetConfiguration(byte reg, byte setting)
{
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Register Aufruf
  Wire.write(reg);
  // Einstellungsbyte für das Register senden
  Wire.write(setting);
  Wire.endTransmission();
}

Kurzfassung für den Sensor Aufruf, ohne weitere Library.

Mit dem Quellcode wird der Sensor mit den Standardwerten initialisiert, so dass die Empfindlichkeit des Gyroskop bei 250°/s und der Beschleunigungssensor bei 2g liegt. Steht der Sensor still auf dem Tisch sollten die Werte für das Gyroskop und den Beschleunigungssensor für X und Y Achse nahe bei 65536 oder 0 liegen. Beschleunigung auf der Z-Achse bei ca. 16000.

image
Ausgabe über dem Arduino Monitor

Ich hoffe, ich konnte somit dem einen oder anderen mit diesem kleinen Bespiel weiterhelfen. Bei Fragen kann gerne die Kommentarfunktion verwendet werden.

Anschluss Beschreibung:

  • Arduino Nano 3.0 – Pin4 –> MPU6050 Pin SDA
  • Arduino Nano 3.0 – Pin5 –> MPU6050 Pin SCL
  • Arduino Nano 3.0 – Pin5V –> MPU6050 Pin VCC
  • Arduino Nano 3.0 – PinGND –> MPU6050 Pin GND

 

Zuletzt geändert 20.11.2014

Ameisen Simulation und andere Dinge

Wer lange sich schon mit C# beschäftig, hat sicherlich schon mal was von AntMe gelesen oder gehört. Diese Idee hatte ich aufgegriffen und mi...