kramann.info
© Guido Kramann

Login: Passwort:










COACH2
1 Planung
2 Architektur
3 Anzeige
4 EEPROM
5 I2C
..5.1 MasterSendByte
..5.2 MasterSend2Bytes
..5.3 MasterReceiveByte
..5.4 MasterReceive2Bytes
6 UART
7 DFT
8 FFT
9 Planung2
10 Klassen
..10.1 AnzeigeTaster
..10.2 RS232
..10.3 MotorServo
..10.4 Drehgeber
..10.5 Sensor
..10.6 Funk
11 Adaption
..11.1 Programmiertechnik
..11.2 Evoopt
12 Fuzzy
..12.1 Uebungsaufgabe
..12.2 Fuzzygroesse
..12.3 Fuzzyset
..12.4 Lookuptable
13 Skript
..13.1 Funkkorrektur
..13.2 Skriptsprachen
..13.3 Anforderungen
..13.4 Agentensysteme
..13.5 Implementierung
..13.6 Experimente
14 Gesamtkonzept
..14.1 Skripterweiterung
..14.2 Makroverhalten
67 Echtzeitsysteme
..67.1 Einfuehrung
....67.1.1 Echtzeit
....67.1.2 Korrektheit
....67.1.3 Hardware
....67.1.4 Ziele
....67.1.5 Synchronprogramm
..67.2 Threads
....67.2.1 Java
....67.2.2 Synchronisierung
..67.3 COACH
....67.3.1 Kaskadenregler
....67.3.2 Zeitebene1
....67.3.3 Zeitebene2
....67.3.4 Zeitebene3
....67.3.5 Puck
....67.3.6 Puckschwarm
..67.4 RTAIlab
....67.4.1 Slax
....67.4.1 USB_Stick
....67.4.2 Sinus
..67.5 Semaphor
....67.5.1 Laufkatze
....67.5.2 Java
....67.5.3 Semaphor
..67.6 Audio
....67.6.1 wav
....67.6.2 Linux
..67.7 Lookup
....67.7.1 Fuzzy
....67.7.2 PWM
..67.8 NeuronaleNetze
....67.8.1 Neuron
....67.8.2 Backpropagation
....67.8.3 Umsetzung
....67.8.4 Winkelerkennung
..67.9 Internetprogrammierung
....67.9.1 Codegenerierung
....67.9.2 PHP_Programmierung
....67.9.3 PHP_OOP
....67.9.4 Java
....67.9.5 UDP
..67.10 DFT
..67.11 FFT
..67.12 Zustandsmaschine
..67.13 Fuzzy
....67.13.1 Fuzzylogik
....67.13.2 FuzzyRegler
....67.13.3 Uebung9
....67.13.5 Softwareentwicklung
......67.13.5.1 AgileSoftwareentwicklung
......67.13.5.2 FuzzyRegler
......67.13.5.3 Uebung
....67.13.6 Umsetzung
......67.13.6.1 FuzzyRegler
......67.13.6.2 Simulation
......67.13.6.3 Optimierung
......67.13.6.4 Uebung
....67.13.7 Haengependel
......67.13.7.1 Haengependel
......67.13.7.2 Simulation
......67.13.7.3 FuzzyRegler
......67.13.7.4 Optimierer
......67.13.7.5 Genetisch
....67.13.8 Information
....67.13.9 Energie

11.1 Programmiertechnik im Zusammenhang mit der Implementierung von Optimierern und Fuzzy-Reglern auf einem Mikrocontroller.

Registrierung

registrieren.zip - Download der nachfolgenden Programmbeispiele.
  • Schon häufiger wurde von der Technik Gebrauch gemacht, Objekt a in anderen Objekt b zu registrieren.
  • Dies diente immer dem Zweck, dass bestimmte Methoden und Attribute von a in b verfügbar gemacht werden sollten.
  • Technisch besagt es nicht weiter, als dass ein Zeiger auf a in b gespeichert wird (C++), bzw. eine Referenz (Java).
  • In elementarster Gestalt sieht das Registrieren folgendermaßen aus:
...in C++:
class A
{
    public:
        int holeKennung()
        {
            return 15678;
        }
};

Code 11.1-1: Quellcode zu A.h

class B
{
    private:
        A* a_zeiger;
    public:
        void registriere(A* a_zeiger)
        {
            this->a_zeiger = a_zeiger;
        }
        void zeigeKennung()
        {
            cout<<a_zeiger->holeKennung()<<endl;
        }
};

Code 11.1-2: Quellcode zu B.h

#include<iostream>
using namespace std;
#include "A.h"
#include "B.h"
int main()
{
    A a;
    B b;
    b.registriere(&a);
    b.zeigeKennung();
}

Code 11.1-3: Quellcode zu Hauptprogramm.cpp

...in Java:
public class A
{
        public int holeKennung()
        {
            return 15678;
        }
}

Code 11.1-4: Quellcode zu A.java

public class B
{
    private A a_referenz;
    public void registriere(A a_referenz)
    {
        this.a_referenz = a_referenz;
    }
    public void zeigeKennung()
    {
        System.out.println(a_referenz.holeKennung());
    }
}

Code 11.1-5: Quellcode zu B.java

public class Hauptprogramm
{
    public static void main(String[] args)
    {
        A a = new A();
        B b = new B();
        b.registriere(a);
        b.zeigeKennung();
    }
}

Code 11.1-6: Quellcode zu Hauptprogramm.java

Repräsentation von Fließkommavariablen als Integer-Werte auf einem Mikrocontroller

param.zip - Download des nachfolgend beschriebenen Programms.
  • In dem genetischen Algorithmus zur Optimierung (evolutionärer Optimierer) der Regelparameter eines PID-Rgelers, wurden in Regelungssysteme die Parameter als Integerwerte repräsentiert und bei Bedarf wieder in double-Werte umgerechnet.
  • Dort war dies geschehen, um die Bits der Speicherrepräsentation der Parameter in der Integer-Darstellung als "Gene" zu betrachten und entsprechend zu manipulieren.
  • Durch die Möglichkeit, sie je nach Erfordernis einmal als Integer-Werte und ein anderes mal als double-Werte zu verarbeiten, eröffnet sich die Möglichkeit, manche Berechnungen Ressourcen-sparend mit der Integer-Zahlen-Repräsentation durchzuführen und andere, wenn es unvermeidbar ist, in der double-Wert-Repräsentation.
  • Das erwähnte Optimierer-Beispiel bildet die Fließkommazahlen zudem auf den ganzen Integer-Zahlenbereich ab, wodurch eine maximale Genauigkeit erreicht wird.
  • Die einheitliche Skalierung der Parameter auf den maximalen Umfang des verwendeten Integer-Datentyps erlaubt sowohl bei der Implementierung des evolutionären Optimierers, als auch bei der Implementierung des Fuzzy-Reglers Einsparungen und Vereinheitlichungen bei der Manipulation der Parameter.
  • Im folgenden soll nun eine spezielle C++-Klasse für den Mikrocontroller entwickelt werden, die nur dem Zweck dient, eine solche hybride (sowohl als Integer, als auch als double-Zahl) Repräsentation von Variablen und Parametern in kontrollierter Weise zu ermöglichen.
Vorüberlegungen
  • Durch die Verwendung der Darstellung uint16_t einer Variablen u kann auch unter C/C++ auf jeder Plattform ein gleicher Zahlenbereich garantiert werden:
  • umin=0, umax=65535
  • Soll eine double-Zahl x in optimaler Weise durch eine uint16_t - Zahl u repräsentiert werden, so kann dies folgendermaßen geschehen, wenn man den Minimalwert xmin und den Maximalwert xmax für x kennt:
  • Transformation: u = (uint16_t)( ((x-xmin)*65535.0)/(xmax-xmin) );
  • Rücktransformation: x = xmin + ((double)u*(xmax-xmin))/65535.0;
Skalierung von x nach u: Zunächst auf den Bereich 0..1 skalieren, dann um den Faktor 65535 strecken.

Bild 11.1-1: Skalierung von x nach u: Zunächst auf den Bereich 0..1 skalieren, dann um den Faktor 65535 strecken.

  • Bei einer Optimierung und auch bei anderen Anwendungen kommt es vor, dass der Definitionsbereich (xmin, xmax) eines Parameters x zur Programmlaufzeit geändert werden muß.
  • Dies kann z.B. dann der Fall sein, wenn die Rasterung eines Parameters um ein gefundenes Optimum herum weiter verfeinert werden soll, um das Optimum noch genauer zu bestimmen.
  • In diesem Fall ist es notwendig, den aktuellen Wert aus der Integer-Repräsentation heraus zunächst wieder mit Hilfe der alten Grenzen xmin,xmax in eine double-Zahl zu zurück zu transformieren und im Anschluß mit Hilfe der neuen Grenzen (xmin_neu, xmax_neu) eine neue Transformation durchzuführen.
  • Voraussetzung ist aber, dass auch für die neuen Grenzen gilt: xmin_neu<=x<=xmax_neu.
  • Die Abfolge der Berechnungen lautet dann:
  • x = xmin + ((double)u*(xmax-xmin))/65535.0;
  • u = (uint16_t)( ((x-xmin_neu)*65535.0)/(xmax_neu-xmin_neu) );
  • Im folgenden wird nun die Klasse Param entwickelt, die Parameter in obiger Weise als Integer-Repräsentation speichern kann.
  • Dabei werden die mit der jeweiligen Integer-Repräsentation verbundenen double-Werte als Zeiger in einem Array registriert.
  • Auf diese Weise wird eine redundante Datenhaltung und damit die Gefahr inkonsistenter Daten, zumindest für die double-Werte vermieden.
  • Das Testprogramm registriert zwei double-Werte und führt eine Transformation und eine Rücktransformation aus.
  • Aufgrund der Rasterung im Integer-Zahlenbereich entsteht dabei eine leichte Änderung der Parameter.
  • Auf dem PC wäre es naheliegend, die maximale Anzahl der Parameter dem Konstruktor zu übergeben und den benötigten Speicher dynamisch (mit new) zu allokieren.
  • Da aber für die Mikrocontroller-Programme keine dynamische Allokierung möglich ist, wird sie dort durch eine define-Anweisung festgelegt.
#define PANZ 10
class Param
{
    public:
        double*   p[PANZ];    //Array mit Zeigern auf die registrierten double-Parameter.
        double    pmin[PANZ]; //Minimaler Wert jedes Parameters.
        double    pmax[PANZ]; //Maximaler Wert jedes Parameters.
        uint16_t  u[PANZ];    //Integer-Repräsentation der einzelnen Parameter.
        void aktualisiereInt(int nr) //double-Repräsentation des ausgewählten Parameters auf die Integer-Rep. übertragen.
        {
            u[nr] = (uint16_t)(  ((*p[nr]-pmin[nr])*65535.0)/(pmax[nr]-pmin[nr])  );
        }
        void aktualisiereDouble(int nr) //int-Repräsentation des ausgewählten Parameters auf die double-Rep. übertragen.
        {
            *p[nr] = pmin[nr] + ((double)u[nr]*(pmax[nr]-pmin[nr]))/65535.0;
        }
        void registriere(int nr,double* x, double xmin, double xmax) //Neuen Parameter merken
        {
            p[nr]    = x;
            pmin[nr] = xmin;
            pmax[nr] = xmax;
            aktualisiereInt(nr);
        }        
        bool aendereDefinitionsbereich(int nr, double xmin_neu, double xmax_neu)
        {
            if(*p[nr]<xmin_neu || *p[nr]>xmax_neu)
                return false;
            pmin[nr] = xmin_neu;
            pmax[nr] = xmax_neu;
            aktualisiereInt(nr);
            return true;          
        }
};

Code 11.1-7: Quellcode der Klasse Param.h

#include<iostream>
#include<stdint.h>
using namespace std;
#include "Param.h"
Param param;
int main()
{
    double y=0.7;
    double t=310.45;
    cout<<"y="<<y<<endl;
    cout<<"t="<<t<<endl;
    param.registriere(0,&y,-1.0,1.0);
    param.registriere(1,&t,0.0,1000.0);
    param.aktualisiereInt(0);
    param.aktualisiereInt(1);
    param.aktualisiereDouble(0);
    param.aktualisiereDouble(1);
    cout<<"y="<<y<<endl;
    cout<<"t="<<t<<endl;
}

Code 11.1-8: Quellcode des Programms Test.cpp

y=0.7
t=310.45
y=0.699977
t=310.445

Code 11.1-9: Ausgabe des Programms Test.cpp