NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Modelle zum Nachbauen oder wo gibt es etwas interessantes oder Projekte?

Moderator: Moderatoren

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 3. Apr 2010 18:43

(Kopie der 1. englisch sprachigen Veröffentlichung)

hi,
my neural net for NXC is ready!

first, a short description about how such a neuron looks like:
download/file.php?id=149 (original link broken)

my following code example is designed for 4 of those neurons, each one connected to all 3 inputs (all inputs to all neurons)
(imagine you have 4 of them, not 2, and so you got 4 outputs, not 2 - but I was too lazy to draw a new grafic:
download/file.php?id=50 (original link broken)

These single layer neurons are called "perceptrons" as well, and the way they are trained is called "Delta Rule" or "Perceptron Learning".
The Learning Modul for the perceptron net matrix has been implemented as

Code: Alles auswählen

void LearnMatrixEvolution() // featuring the new Evolution Auto Training Engine)


Here comes the code.

Code: Alles auswählen

/***************************************************************************/
// Feed-Forward Net
// (outputs shown on display)
// (c) H. W. 2008-2010, FP (at) Sirius Cybernetics Corporation :)
// NXC version
/***************************************************************************/

/***************************************************************************/
// Neuron Net Internals
/***************************************************************************/

const string IRobot="Hal 2010";
const string version="015";
const int ni=3;      // number of inputs (max. displayed: 24)
const int nz=4;      // number of neurons = outputs (max. displayed: 24)

/***************************************************************************/
// excerpts from:
// #include "nxcio.h"
// #include "math.h"
/***************************************************************************/

/***************************************************************************/
// graphic display output
/***************************************************************************/

string __gFTFontName;
inline void SetFont(string name) { __gFTFontName = name; }

#define printf1g( _x, _y, _format1, _value1) { \
  string sval1 = FormatNum(_format1, _value1); \
  FontTextOut(_x, _y, __gFTFontName, sval1); \
}

#define printfsg( _x, _y, _s) { FontTextOut(_x, _y, __gFTFontName, _s);}

/***************************************************************************/
// Button handling
/***************************************************************************/

inline bool keypressed(){
   char test;
   test=( ButtonPressed(BTN1, false) || ButtonPressed(BTN2, false)
       || ButtonPressed(BTN3, false) || ButtonPressed(BTN4, false));
   return test;
}

inline int readkey() {
  int result = -1;

    if (ButtonPressed(BTN1, false))
      result = BTN1;
    else if (ButtonPressed(BTN2, false))
      result = BTN2;
    else if (ButtonPressed(BTN3, false))
      result = BTN3;
    else if (ButtonPressed(BTN4, false))
      result = BTN4;

    if (result <> -1)  while(ButtonPressed(result, false));

    return result;
}

/***************************************************************************/
// Sound: PlayTones
/***************************************************************************/

struct Note
{
  unsigned int Frequency;
  unsigned int Duration;
};

//*****************************************

Note ChordUp[] = {TONE_C4, 50, TONE_E4, 50, TONE_G4, 50,
  TONE_C5, 50, TONE_E5, 50, TONE_G5, 50, TONE_C6, 200};

Note ChordDn[] = {TONE_C6, 50, TONE_G5, 50, TONE_E5, 50,
  TONE_C5, 50, TONE_G4, 50, TONE_E4, 50,  TONE_C4, 200};
 
Note Beep[] = {TONE_C5, 200};

Note BeepBeep[] = {TONE_C5, 200 , 0, 100, TONE_C5, 200};

Note Blip[] = {TONE_C7, 50 };

Note BlipBlip[] = {TONE_C7, 50, 0, 10, TONE_C7, 50 };

Note Buzz[] = {220, 200 };

Note sdError[] = {TONE_C4, 50, 0, 50, TONE_C4, 50, 0, 50, TONE_C4, 50, 0, 50} ;

//*****************************************


void PlayTones(Note data[])
{
  for (int i = 0; i <ArrayLen>=0) {return (f + 0.5);}
  else
  {return (f - 0.5);}
}

/***************************************************************************/
// Init  program
/***************************************************************************/

void InitHalBios() {
  SetFont("Tiny_6.ric");
  #download "Tiny_6.ric";
  SetLongAbort(true);
}

/***************************************************************************/
// Neuron Structure
/***************************************************************************/

float Ni[nz][ni];   // inputs
float Nw[nz][ni];   // weights
float Nnet[nz];     // netto input (weighted sum)
float Nthr[nz];     // threshold
float Nout[nz];     // output


/***************************************************************************/
// File I/O
/***************************************************************************/

const string sFileName = "hal_mem.dat";

void MindUpload() {

  unsigned int nFileSize = 4*((ni+3)*(nz+1)); // estimated file zize
  byte fHandle, i, j;
  int IOresult, counter=0;
  float fbuf;

  TextOut(0,56-8, IRobot+"-"+version);
  DeleteFile(sFileName);
  IOresult = CreateFile(sFileName, nFileSize, fHandle);
  if (IOresult == LDR_SUCCESS) {
    ClearScreen();

    for (j=0;j<nz;j++) {
      for (i=0; i<ni;i++)   {
        counter+=1;
        fbuf= Nw[j][i];

        ClearScreen();
        TextOut(0,56-8, IRobot+"-"+version);
        NumOut ( 0,24, counter);
        NumOut ( 0,16, fbuf);
       
        WriteLn (fHandle, fbuf);
        }

      fbuf= Nthr[j];
      counter+=1;
      TextOut(0,56-8, IRobot+"-"+version);
      NumOut ( 0,24, counter);
      NumOut ( 0,16, fbuf);
     
      WriteLn (fHandle, fbuf); }

    CloseFile(fHandle);
    PlayTones(ChordUp);
  }
  else
  PlayTones(sdError);
  Wait(1000);
}

//*****************************************

void TotalRecall() {

  unsigned int nFileSize;
  byte fHandle, i, j;
  int IOresult, counter=0;;
  float fbuf;
 
  ClearScreen();
  TextOut(0,56-8, IRobot+"-"+version);
  IOresult = OpenFileRead(sFileName, nFileSize, fHandle);
  if (IOresult == LDR_SUCCESS) {
    for (j=0;j<nz;j++) {
      for (i=0; i<ni> netto input (net)
/***************************************************************************/


inline void PropagateNeuron(int z){   // Propagierungsfunktion für Neuron nz
  int i=0;                          // kalkuliert den Gesamt-Input (net)
  float s=0;                        // abzueglich Schwellwert

  for(i=0;i<ni> output
/***************************************************************************/


inline void ActivateNeuron(int z){         // Aktivierungsfunktion 1 T: x -> [0; +1]

   if (Nnet[z]>0)                   // 0-1-Schwellwertfunktion
      {Nout[z]=1;}                  // Fkt.-Wert: 0 oder 1
   else {Nout[z]=0;}
}


/***************************************************************************/
// Net: Init, Set
/***************************************************************************/

inline void ResetNeuron(int z){

   int i;

   for (i=0; i<ni; i++) {
     Ni[z][i]=0;       // Inputs  (Dendrit)
     Nw[z][i]=0.0;     // Weights (Dendrit)
   }
   Nnet[z]=0.0;        // total netto Input (weighted sum)
   Nthr[z]=0.4;        // threshold
   Nout[z]=0;          // activation level = output
}


//*****************************************

inline void ReloadMatrix(){
   int z;

   for (z=0; z<nz; z++) {
        ResetNeuron(z);}
}


/***************************************************************************/
// Inputs: set and watch
/***************************************************************************/


void InitAllSenses() {
    SetSensorTouch(0);
    SetSensorTouch(1);
    SetSensorTouch(2);
}

//*****************************************

inline void RefreshAllSenses() {  // get sensor inputs + store to neurons
   int i, z, US1;

   US1=SensorUS(3);
   for (z=0; z<nz>30)&&(US1<100>= 100; (0,1): < 30;
       Ni[z][4]=(US1<40);              // (1,0): 40-100; (1,1): 30-40;
*/
   }
}


/***************************************************************************/
// Forward Propagation
/***************************************************************************/

inline void MatrixReproduction() {
  int z;

  for(z=0; z<nz; z++) {
     PropagateNeuron(z);
     ActivateNeuron(z);
  }
}


/***************************************************************************/
// Display
/***************************************************************************/

string MenuText; // bottom line menu text

inline void displayInput(int _i, int _val){  // all inputs to all neurons
  byte x, y, im;
 
  im= _i %6;
  x= (_i /6)*6;
  y= (48-(8*im));

  printf1g( x, y,"%1d ",_val);
}

//*****************************************


inline void displayOutput(int _i, float _val){   // all outputs
  byte x, y, im, offset;

  if (nz<=6) offset=81;
  else offset=30;
 
  im= _i %6;
  x= offset + (_i /6)*6*3;
  y= (48-(8*im));

  printf1g( x, y,"%1.0f ",_val);
}

//*****************************************


inline void displayTOut(int _i, int _val){     //  target outputs
  byte x, y, im, offset;

  if (nz<=6) offset=86;
  else offset=35;
 
  im= _i %6;
  x= offset + (_i /6)*6*3;
  y= (48-(8*im));

  printf1g( x, y,">%1d ",_val);
}

//*****************************************

 
task DisplayValues(){
  int i;  // inputs = sensors
  int z;  // neuron number = outputs
  int ibuf;
  float fbuf;
  string TopLine;

  while(true) {
 
    if((ni<=3)&&(nz<=6)) {
      TopLine= "in..w1....w2....w3...thr..out.To";

      for(i=0; i<ni; i++) {
         ibuf= Ni[0][i];                     // neuron inputs
         displayInput(i, ibuf);              // all inputs to all neurons
      }

      for(z=0; z<nz; z++) {

        for (i=0; i<ni; i++) {
           fbuf=Nw[z][i];
           printf1g( 9+i*6*3 ,48-(8*z),"%2.1f", fbuf);   // neuron weights
        }

        fbuf=Nthr[z];
        printf1g( 9+3*6*3 ,48-(8*z),"%2.1f", fbuf);      // neuron threshold

        fbuf=(Nout[z]);

        displayOutput(z, fbuf);
      }
    }
    else

    if((ni<=24)&&(nz<=24)) {
      TopLine= "in............o..T..o..T..o..T..o..T";
      for(i=0; i<ni; i++) {
         ibuf= Ni[0][i];                           // neuron inputs
         displayInput(i, ibuf);
      }
      for(z=0; z<nz; z++) {
         fbuf=Nout[z];
         displayOutput(z, fbuf);
      }
    }
    printfsg(0,56, TopLine);
    printfsg(0, 0, MenuText);
  }
}


/***************************************************************************/
// Teach and Learn
/***************************************************************************/

void LearnMatrixEvolution() { // Perceptron-Lern-Modus
  int ErrorCount;             // new: featuring the Evolution Auto-Teach Agent

  int   i; // Anzahl Inputs
  int   j; // Anzahl Ausgabe-Neurons
  float targOut;
  int   ibuf;
  float fbuf;
  float tOutCribSheet[nz];  // crib sheet for the auto teach agent
  bool  autoTeach=false;
  char key;

  float lf=0.3, lfthr;  // learning factor
  lfthr=0.1;          // smaller learning factor for thrshold learning

  PlayTones(ChordUp);

  do
  {
    ErrorCount=0;

    MenuText="- <<OK>> ++"; // Btn left, center/esc, right

    for (j=0; j<nz> do nothing
      {
          if (!autoTeach) PlayTones(BlipBlip);
          Wait(1);
      }
      //....................................................

      if (targOut!=Nout[j])             // Out!=teachOut (wrong)  => start learning
      {
         if (!autoTeach) PlayTones(Beep);
         Wait(1);
         ErrorCount+=1;
                                               // LEARNING:  Delta-Rule
          for (i=0; i<ni>0)) do   //  manual or automatic target Output correction?
    {
          key=-1;
          if (keypressed()) {PlayTones(Blip); key=readkey();}

          if (key==BTNLEFT)  { autoTeach=false; } // manual Teach Mode
          else
          if (key==BTNRIGHT) { autoTeach=true; }  // switch to Auto Teach Mode
          Wait(1);
         
     }    while ((key==-1));

    if (key==BTNEXIT) {               // EXIT Learn program
       key=-1;
       PlayTones(Buzz);
       return;
    }
    key=-1;

  } while (ErrorCount>0);

  Wait(10);
  PlayTones(Beep);
}


/***************************************************************************/
// task main
/***************************************************************************/

task main() {

   char key;

   ClearScreen();
   TextOut(0,56-8, IRobot+"-"+version);
   
   InitHalBios();
   InitAllSenses();
   ReloadMatrix();
   Wait(500);
   ClearScreen();
   start DisplayValues;
   
   while(true) {
      MenuText="<save>";
      RefreshAllSenses();
      MatrixReproduction();
      key=-1;
      if (keypressed()) key=readkey();
     
      if (key==BTNCENTER) {LearnMatrixEvolution(); ClearScreen();}
      if (key==BTNLEFT  ) {stop DisplayValues; MindUpload();   ClearScreen(); start DisplayValues;}
      if (key==BTNRIGHT ) {stop DisplayValues; TotalRecall(); ClearScreen(); start DisplayValues;}
      if (key==BTNEXIT )  {ReloadMatrix(); ClearScreen(); PlayTones(ChordDn);}
     
      Wait(1);
   }
}




How to use it:
from execution state: MenuText= "[ save Learn / clear load ]"

present different input patterns -
that means: press the related touch sensors and keep them pressed. Look at the outputs (the last column) and watch what's happening (well, not much from the start, I guess)

if the ouput pattern is like the one you want: everything is ok - in this case the NN does what it should.
if not - press BtnCenter, so that the teaching modul starts.

A new MenuText appears: "- - <<OK>> ++"

at this point the presented input pattern has been stored to the Learn modul -
therefore you may release the touch sensors, they keep stored.

now the input pattern can be trained to the required target output patterns.
Each line represents a single neuron, and thus each neuron can be trained to a different reaction.

If all neuron outputs match the required target outputs, the learn modul stops automatically and the program returns to the execution level.

Here you may present more different input patterns that you wish to teach (as you will know, with 3 touch sensors you may have 2³=8 different input patterns, to which each neuron can give a different output reply).

Once you have trained the correct behaviour, you may save it to a file (left button), or restore former values (right button) or reset the NN to defaults (exit button).

you may easily redesign the net for more or less inputs or more or less output neurons by patching the "ni" and "nz" variable values (e.g., 4 inputs and 10 outputs or anything else) and so you may adjust it to your own requirements:

Code: Alles auswählen

/***************************************************************************/
// Neuron Net Internals
/***************************************************************************/
//...
const int ni=4;      // number of inputs (max. displayed: 24)
const int nz=10;      // number of neurons = outputs (max. displayed: 24)




I hope you enjoy it ! :)

PS:
you will have to download this ric-font for display write functions:
download/file.php?id=374

EDIT: (now advanced functionality:) the self-trainig net, featuring the new Evolution Auto Training Agent!
Once you have manually entered your patterns, you may let the robot do the boring part of this job, and you'll have all the fun!

So after the 1st round simply press BtnRight (automatic)!
Now Hal (yes, that indeed is his name!) reads your target output list and trains himself until he got the vocabulary!

Meanwhile lean back, relax and have fun! :P
Zuletzt geändert von HaWe am 6. Apr 2010 19:26, insgesamt 10-mal geändert.
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 4. Dez 2013 11:46

original post: 15. Mai 2011, 18:04
(update, File Upload war inzwischen vom Server gelöscht ):

Programmiersprache: NXC für den NXT.
Hier nochmal der Aufbau eines künstlichen Neurons:
neuron.jpg
Computer-Neuron
neuron.jpg (55.41 KiB) 8921 mal betrachtet


Hier der schematische Aufbau eines Feed-Forward-Netzes -
Alle Inputs (in_0, in_1, in_2) sind direkt mit allen Output-Neuronen (L0_0, L0_1) verbunden:
FFN 3+2.jpg
1-schichtiges Netz mit 3 Inputs und 2 Output-Neuronen (Feed-Forward-Netz)
FFN 3+2.jpg (11.23 KiB) 9930 mal betrachtet



Hier ein Font, den man braucht, um die Daten auf dem Display in kleiner Schrift anzuzeigen (tiny_6.ric):
Tiny_6.zip
kleine 6p Schriftart für Displayanzeige, ge-updated.
(1.04 KiB) 289-mal heruntergeladen


Und hier der NXC-Code für ein
Feed-Forward-Netz
mit 3 Inputs und 6 Ausgabeschicht-Neurons für somit 6 Outputs:

Anm.: J. Hansen hat in späteren NXC-Versionen die Sound-API verändert, es kann also sein, dass Töne nicht mehr korrekt gespielt werden - das tut der Funktionsweise aber keinen Abbruch, man muss höchstens die PlayTone-Zeilen auskommentieren oder umschreiben !

Code: Alles auswählen

/***************************************************************************/
// Feed-Forward Net
// (outputs shown on display)
// (c) H. W. 2008-2010 aka FP
// NXC version (Bricxcc 3.3.8.10) - Soundfiles ok?
/***************************************************************************/

// Autor: Helmut Wunder
// frei verwendbar für private Zwecke
// (für kommerzielle Zwecke nur nach bes. Gen.)

/***************************************************************************/
// Neuron Net Internals
/***************************************************************************/

const string IRobot="Hal 2010";
const string version="017";
const int ni=3;      // number of inputs (max. displayed: 24)
const int nz=6;      // number of neurons = outputs (max. displayed: 24)

/***************************************************************************/
// excerpts from:
// #include "nxcio.h"
// #include "math.h"
/***************************************************************************/

/***************************************************************************/
// graphic display output
/***************************************************************************/

string __gFTFontName;
inline void SetFont(string name) { __gFTFontName = name; }

#define printf1g( _x, _y, _format1, _value1) { \
  string sval1 = FormatNum(_format1, _value1); \
  FontTextOut(_x, _y, __gFTFontName, sval1); \
}

#define printfsg( _x, _y, _s) { FontTextOut(_x, _y, __gFTFontName, _s);}

/***************************************************************************/
// Button handling
/***************************************************************************/

inline bool kbdhit(){
   char test;
   test=( ButtonPressed(BTN1, false) || ButtonPressed(BTN2, false)
       || ButtonPressed(BTN3, false) || ButtonPressed(BTN4, false));
   return test;
}



/***************************************************************************/
// Sound: PlayTones
/***************************************************************************/

/*
void PlayTones(Note data[])
{
  for (int i = 0; i < ArrayLen(data); i++) {
    Note tmp = data[i];
    PlayTone(tmp.Frequency, tmp.Duration);
    Wait(tmp.Duration);
  }
}


struct Note
{
  unsigned int Frequency;
  unsigned int Duration;
};
*/
//*****************************************

Tone sndChordUp[] = {TONE_C4, 50, TONE_E4, 50, TONE_G4, 50,
  TONE_C5, 50, TONE_E5, 50, TONE_G5, 50, TONE_C6, 200};

Tone sndChordDn[] = {TONE_C6, 50, TONE_G5, 50, TONE_E5, 50,
  TONE_C5, 50, TONE_G4, 50, TONE_E4, 50,  TONE_C4, 200};

Tone sndBeep[] = {TONE_C5, 200};

Tone sndBeepBeep[] = {TONE_C5, 200 , 0, 100, TONE_C5, 200};

Tone sndBlip[] = {TONE_C7, 50 };

Tone sndBlipBlip[] = {TONE_C7, 50, 0, 10, TONE_C7, 50 };

Tone sndBuzz[] = {220, 200 };

Tone sndError[] = {TONE_C4, 50, 0, 50, TONE_C4, 50, 0, 50, TONE_C4, 50, 0, 50} ;

//*****************************************


/*void PlayTones(Note data[])
{
  for (int i = 0; i < ArrayLen(data); i++) {
    Note tmp = data[i];
    PlayTone(tmp.Frequency, tmp.Duration);
    Wait(tmp.Duration);
  }
}   */

/***************************************************************************/
// math functions
/***************************************************************************/

inline int round(float f)
{
  if (f>=0) {return (f + 0.5);}
  else
  {return (f - 0.5);}
}

/***************************************************************************/
// Init  program
/***************************************************************************/

void InitHalBios() {
  SetFont("Tiny_6.ric");
  #download "Tiny_6.ric";
  SetLongAbort(true);
}

/***************************************************************************/
// Neuron Structure
/***************************************************************************/

float Ni[nz][ni];   // inputs
float Nw[nz][ni];   // weights
float Nnet[nz];     // netto input (weighted sum)
float Nthr[nz];     // threshold
float Nout[nz];     // output


/***************************************************************************/
// File I/O
/***************************************************************************/

const string sFileName = "hal_mem.dat";

void MindUpload() {

  unsigned int nFileSize = 4*((ni+3)*(nz+1)); // estimated file zize
  byte fHandle, i, j;
  int IOresult, counter=0;
  float fbuf;

  TextOut(0,56-8, IRobot+"-"+version);
  DeleteFile(sFileName);
  IOresult = CreateFile(sFileName, nFileSize, fHandle);
  if (IOresult == LDR_SUCCESS) {
    ClearScreen();

    for (j=0;j<nz;j++) {
      for (i=0; i<ni;i++)   {
        counter+=1;
        fbuf= Nw[j][i];

        ClearScreen();
        TextOut(0,56-8, IRobot+"-"+version);
        NumOut ( 0,24, counter);
        NumOut ( 0,16, fbuf);

        WriteLn (fHandle, fbuf);
        }

      fbuf= Nthr[j];
      counter+=1;
      TextOut(0,56-8, IRobot+"-"+version);
      NumOut ( 0,24, counter);
      NumOut ( 0,16, fbuf);

      WriteLn (fHandle, fbuf); }

    CloseFile(fHandle);
    PlayTones(sndChordUp);
  }
  else
  PlayTones(sndError);
  Wait(1000);
}

//*****************************************

void TotalRecall() {

  unsigned int nFileSize;
  byte fHandle, i, j;
  int IOresult, counter=0;;
  float fbuf;

  ClearScreen();
  TextOut(0,56-8, IRobot+"-"+version);
  IOresult = OpenFileRead(sFileName, nFileSize, fHandle);
  if (IOresult == LDR_SUCCESS) {
    for (j=0;j<nz;j++) {
      for (i=0; i<ni;i++)   {

        counter+=1;
        ReadLn (fHandle, fbuf);
        Nw[j][i]=fbuf;

        ClearScreen();
        TextOut(0,56-8, IRobot+"-"+version);
        NumOut ( 0,24, counter);
        NumOut ( 0,16, fbuf);
        }

      counter+=1;
      ReadLn (fHandle, fbuf);
      Nw[j][i]=fbuf;

      ClearScreen();
      TextOut(0,56-8, IRobot+"-"+version);
      NumOut ( 0,24, counter);
      NumOut ( 0,16, fbuf);
    }

    CloseFile(fHandle);
    PlayTones(sndChordUp);
  }
  else
  PlayTones(sndError);
  Wait(1000);
}


/***************************************************************************/
// propagate -> netto input (net)
/***************************************************************************/


inline void PropagateNeuron(int z){   // Propagierungsfunktion für Neuron nz
  int i=0;                          // kalkuliert den Gesamt-Input (net)
  float s=0;                        // abzueglich Schwellwert

  for(i=0;i<ni;i++){
     s+= (Ni[z][i]*Nw[z][i]);     // gewichtete Summe
  }
  Nnet[z]=s-Nthr[z];
}


/***************************************************************************/
// activate -> output
/***************************************************************************/


inline void ActivateNeuron(int z){         // Aktivierungsfunktion 1 T: x -> [0; +1]

   if (Nnet[z]>0)                   // 0-1-Schwellwertfunktion
      {Nout[z]=1;}                  // Fkt.-Wert: 0 oder 1
   else {Nout[z]=0;}
}


/***************************************************************************/
// Net: Init, Set
/***************************************************************************/

inline void ResetNeuron(int z){

   int i;

   for (i=0; i<ni; i++) {
     Ni[z][i]=0;       // Inputs  (Dendrit)
     Nw[z][i]=0.0;     // Weights (Dendrit)
   }
   Nnet[z]=0.0;        // total netto Input (weighted sum)
   Nthr[z]=0.4;        // threshold
   Nout[z]=0;          // activation level = output
}


//*****************************************

inline void ReloadMatrix(){
   int z;

   for (z=0; z<nz; z++) {
        ResetNeuron(z);}
}


/***************************************************************************/
// Inputs: set and watch
/***************************************************************************/


void InitAllSenses() {
    SetSensorTouch(0);
    SetSensorTouch(1);
    SetSensorTouch(2);
    SetSensorLowspeed(3);
}

//*****************************************

inline void RefreshAllSenses() {  // get sensor inputs + store to neurons
   int i, z, US1;

   US1=SensorUS(3);
   for (z=0; z< nz; z++) {
       Ni[z][0]=SensorValue(0);
       Ni[z][1]=SensorValue(1);
       Ni[z][2]=SensorValue(2);
/*
       Ni[z][3]=(US1>30)&&(US1<100);   // (0,0): >= 100; (0,1): < 30;
       Ni[z][4]=(US1<40);              // (1,0): 40-100; (1,1): 30-40;
*/
   }
}


/***************************************************************************/
// Forward Propagation
/***************************************************************************/

inline void MatrixPropagation() {
  int z;

  for(z=0; z<nz; z++) {
     PropagateNeuron(z);
     ActivateNeuron(z);
  }
}


/***************************************************************************/
// Display
/***************************************************************************/

string MenuText; // bottom line menu text

inline void displayInput(int _i, int _val){  // all inputs to all neurons
  byte x, y, im;

  im= _i %6;
  x= (_i /6)*6;
  y= (48-(8*im));

  printf1g( x, y,"%1d ",_val);
}

//*****************************************


inline void displayOutput(int _i, float _val){   // all outputs
  byte x, y, im, offset;

  if (nz<=6) offset=81;
  else offset=30;

  im= _i %6;
  x= offset + (_i /6)*6*3;
  y= (48-(8*im));

  printf1g( x, y,"%1.0f ",_val);
}

//*****************************************


inline void displayTOut(int _i, int _val){     //  target outputs
  byte x, y, im, offset;

  if (nz<=6) offset=86;
  else offset=35;

  im= _i %6;
  x= offset + (_i /6)*6*3;
  y= (48-(8*im));

  printf1g( x, y,">%1d ",_val);
}

//*****************************************


task DisplayValues(){
  int i;  // inputs = sensors
  int z;  // neuron number = outputs
  int ibuf;
  float fbuf;
  string TopLine;

  while(true) {

    if((ni<=3)&&(nz<=6)) {
      TopLine= "in..w1....w2....w3...thr..out.To";

      for(i=0; i<ni; i++) {
         ibuf= Ni[0][i];                     // neuron inputs
         displayInput(i, ibuf);              // all inputs to all neurons
      }

      for(z=0; z<nz; z++) {

        for (i=0; i<ni; i++) {
           fbuf=Nw[z][i];
           printf1g( 9+i*6*3 ,48-(8*z),"%2.1f", fbuf);   // neuron weights
        }

        fbuf=Nthr[z];
        printf1g( 9+3*6*3 ,48-(8*z),"%2.1f", fbuf);      // neuron threshold

        fbuf=(Nout[z]);

        displayOutput(z, fbuf);
      }
    }
    else

    if((ni<=24)&&(nz<=24)) {
      TopLine= "in............o..T..o..T..o..T..o..T";
      for(i=0; i<ni; i++) {
         ibuf= Ni[0][i];                           // neuron inputs
         displayInput(i, ibuf);
      }
      for(z=0; z<nz; z++) {
         fbuf=Nout[z];
         displayOutput(z, fbuf);
      }
    }
    printfsg(0,56, TopLine);
    printfsg(0, 0, MenuText);
  }
}


/***************************************************************************/
// Teach and Learn
/***************************************************************************/

void LearnMatrixEvolution() { // Perceptron-Lern-Modus
  int ErrorCount;             // new: featuring the Evolution Auto-Teach Agent

  int   i; // Anzahl Inputs
  int   j; // Anzahl Ausgabe-Neurons
  float targOut;
  int   ibuf;
  float fbuf;
  float tOutCribSheet[nz];  // crib sheet for the auto teach agent
  bool  autoTeach=false;
  char key;

  float lf=0.3, lfthr;  // learning factor
  lfthr=0.1;          // smaller learning factor for thrshold learning

  PlayTones(sndChordUp);

  do
  {
    ErrorCount=0;

    MenuText="-- <<          OK / esc        >> ++"; // Btn left, center/esc, right

    for (j=0; j<nz; j++)    //   up to number of output Neurons
    {
      PropagateNeuron(j); ActivateNeuron(j); //  calculate current neuron output
      ClearScreen();

      if (!autoTeach) targOut=Nout[j] ;     //  target output == net output ?
      else
      if (autoTeach) targOut=tOutCribSheet[j] ;   // auto mode: recall former target values

      if (!autoTeach)
      MenuText="-- <<          OK / esc        >> ++"; // Btn left, center/esc, right

      ibuf=targOut;
      displayTOut(j, ibuf);

      if (!autoTeach) do                   //  manual target Output correction
      {
          key=-1;
          if (kbdhit()) {PlayTones(sndBlip); key=getchar();}

          if (key==BTNLEFT)  { targOut=0; }
          else
          if (key==BTNRIGHT) { targOut=1; }

          ibuf=targOut;
          displayTOut(j, ibuf);

          Wait(1);

          if (autoTeach) break;

      }   while ((key!=BTNCENTER)&&(key!=BTNEXIT));

      if (!autoTeach) tOutCribSheet[j]=targOut;  // store target output to crib sheet;

      //...................................................

      if (key==BTNEXIT) {               // EXIT Learn program
          PlayTones(sndBuzz);
          return;
      }
      //....................................................
                                        // LEARN-Mode START
      //....................................................

      if (targOut==Nout[j] )            // Out=teachOut (correct) => do nothing
      {
          if (!autoTeach) PlayTones(sndBlipBlip);
          Wait(1);
      }
      //....................................................

      if (targOut!=Nout[j])             // Out!=teachOut (wrong)  => start learning
      {
         if (!autoTeach) PlayTones(sndBeep);
         Wait(1);
         ErrorCount+=1;
                                               // LEARNING:  Delta-Rule
          for (i=0; i<ni; i++)                 // for all i (Inputs)
          {                                    // adjust all weights
             if ( targOut!=Nout[j]) {
               Nw[j][i] = Nw[j][i]+ (lf *(targOut-Nout[j]) *Ni[j][i]);
             }
          }

          PropagateNeuron(j); ActivateNeuron(j); //  again calculate neuron output

          if (targOut!=Nout[j]) // adjust threshold (Delta-Rule, expanded)
          {
              Nthr[j] = Nthr[j] - (lfthr*(targOut-Nout[j]));
          }

      } // if (targOut!=Neuron[j].out)
      //...................................................
      PlayTones(sndBlip);

    } // for j

    ClearScreen();
    MenuText="manual         esc     automatic"; //

    if ((!autoTeach) && (ErrorCount>0)) do   //  manual or automatic target Output correction?
    {
          key=-1;
          if (kbdhit()) {PlayTones(sndBlip); key=getchar();}

          if (key==BTNLEFT)  { autoTeach=false; } // manual Teach Mode
          else
          if (key==BTNRIGHT) { autoTeach=true; }  // switch to Auto Teach Mode
          Wait(1);

     }    while ((key==-1));

    if (key==BTNEXIT) {               // EXIT Learn program
       key=-1;
       PlayTones(sndBuzz);
       return;
    }
    key=-1;

  } while (ErrorCount>0);

  Wait(10);
  PlayTones(sndBeep);
}


/***************************************************************************/
// task main
/***************************************************************************/

task main() {

   char key;

   ClearScreen();
   TextOut(0,56-8, IRobot+"-"+version);

   InitHalBios();
   InitAllSenses();
   ReloadMatrix();
   Wait(500);
   ClearScreen();
   start DisplayValues;

   while(true) {
      MenuText="< save    Learn / clear   load >";
      RefreshAllSenses();
      MatrixPropagation();
      key=-1;
      if (kbdhit()) key=getchar();

      if (key==BTNCENTER) {LearnMatrixEvolution(); ClearScreen();}
      if (key==BTNLEFT  ) {stop DisplayValues; MindUpload();   ClearScreen(); start DisplayValues;}
      if (key==BTNRIGHT ) {stop DisplayValues; TotalRecall(); ClearScreen(); start DisplayValues;}
      if (key==BTNEXIT )  {ReloadMatrix(); ClearScreen(); PlayTones(sndChordDn);}

      Wait(1);
   }
}


ps
Fand meinen Original-Beitrag nicht mehr, alter link hat sich aber gefunden und jetzt beide Topics wurden zusammengeführt (danke, Sebi)
http://www.mindstormsforum.de/viewtopic.php?f=70&t=5601&p=47811#p47811
von HaWe » 3. Apr 2010, 18:43
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 1. Feb 2015 12:09

wegen einer aktuellen PM-Nachfrage hier ein Anwendungsbeispiel: ein während der Laufzeit trainierbarer Fahr-Roboter, der auf Anstoßen und Entfernung zu Hindernissen reagiert - gelerntes "Wissen" wird dabei im Flash dauerhaft gespeichert und ist nach Abschalten bei Neustart und auch sogar nach Flashen eines Programm-Updates abrufbar:

Als Inputs wählt man beliebige Sensoren, die 0/1-Signale abgeben (z.B. Taster, oder man definiert US-Entfernungsbereiche als einzelne "virtuelle" 1/0-Signale, das ist im Code bereits so vorgesehen!),
als Outputs verwendet man z.B. 4 Neuronen für Motoren A + B, jeweils für eine kurze Zeit vor und/oder zurück. Da die Motordrehung angelernt wird, kann man bereits im Lernmodus eine gleichzeitige Aktivierung von vor/zurück für denselben Motor vermeiden, wer will, kann natürlich noch ein zusätzliches Schaltneuron dazwischenbauen, das das grundsätzlich verhindert - - - muss aber nicht sein.
Eine Reihe weiterer Output-Neuronen könnte man mit Zeitdauer / Intervall trainieren, wie lange die entsprechende Motor-Aktion andauern soll , z.B. je 1 Neuron für 0.5 cek, 1 sek, 2 sek, 4 sek. (z.B. normal vorwärts in relativ kurzen Intervallen, um schnell reagieren zu können, und rückwärts nach Anstoßen an Hindernissen etwas länger, um schnell ein Stück Strecke rückwärts zu schaffen). Auch die Motor-pwm kann durch zusätzliche outputs trainiert werden (etwa für adjustierte Kurvenfahrt), es gibt aber natürlich noch viele andere, zusätzliche Trainingsmöglichkeiten.

Multiple Sensoren auch für eine Greifzange (um Annäherung und gezieltes Greifen zu lernen) und zur Umwelterkennung (z.B. diskrete Bereichs-Signale einer Cam, aber auch Distanz- oder Farbsensorsignale) in Verbindung mit einem nach Prioritäten geschichteten Behaviour-Programms (Subsumption Architektur) lassen das Training eines intelligent agierenden Roboters in einer echten lebenden Umwelt abseits von Labor-Parcours zu.

Ausblick / Diskussion:
Die Echtzeitfähigkeit bezügl. Geschwindigkeit und Größe für NNs ist für NXC sehr begrenzt - Netze sind speichermäßig auf insgesamt maximal 40 Neuronen begrenzt.
Weiterhin hat ein Feed-Forward-Netz nicht alle denkbaren Lernmöglichkeiten, ein Lernmuster wie z.B. bei einer XOR-Verknüpfung ist unmöglich. Hier müssen mehrschichtige Netze zur Anwendung kommen (Backpropagation-, Elman-, Jordan- oder ganz andere 3-dim. Architekturen wie z.B. Hopfield-oder ART-Netze).
Das Lerntraining dauert dann aber bei NXC (und RobotC) schon bei Backpropagation-Netzen wegen ihrer langsamen VMs nicht mehr nur Sekunden, sondern Stunden, was nicht mehr praktikabel ist. Möglich wird dies aber werden durch Programmierung von native Executables durch echte C-Compiler (NxOS, nxtOSEK, bei EV3 und Arduinos gpp C++ ( ab Arduino Due aufwärts, besser Raspberry Pi, BeagleBobe Black, oder der Kommende Arduino Tre).

Ich selber werde das Projekt wegen der einfachen Sketch-IDE für den Arduino Tre wieder neu aufnehmen.
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 30. Apr 2015 09:19

so, ich habe fertig - ein 2-schichtiges

Backpropagation Netz / Backpropagation net

Vorteil:
man kann jetzt hiermit alle mathematischen aussagenlogischen Verknüpfungen von Sensor-Inputs trainieren, auch z.B. XOR, was mit einfachen Feed-Forward Netzen (siehe obigen NXC-Code!) bewiesenermaßen nicht ging.

Bild
(Foto entnommen aus: https://msdn.microsoft.com/en-us/magazine/jj658979.aspx )

Nachteil: ein immenser Rechenaufwand per rückwärts gerichteter Fehler-Ableitungen.
Der NXT hat dazu für 1 einzigen Trainingslauf fast einen halben Tag gerechnet (teilw. für noch kleinere Netze weit über 8 Stunden)!
Mein Arduino Due schafft es jetzt in einigen Sekunden bis wenigen Minuten, da er keinen Interpreter verwendet (wie NXC) sondern native Executabels. Per nxtOSEK auf dem NXT müsste es übrigens fast ebenso schnell gehen, aus dem selben Grund.

Mit einigen Minuten Trainingszeit für Netze mit 10-20 Neuronen kann man nun zwar schon deutlich besser arbeiten, aber Netze mit sinnvoller Größe ab 100 Neuronen und mehr als 1000 Lernmustern aufwärts erfordern dann doch wieder sehr lange Trainingszeiten, die den Einsatz "in Echtzeit" in Frage stellen. Hier sind dann doch wieder deutlich schnellere Prozessoren mit deutlich mehr Speicher nötig (eventuell reicht schon der EV3? oder doch besser Sitara-Boards mit > 1GB RAM wie z.B. der lang ersehnte Arduino TRE? oder noch besser vlt sogar Multicore-Pozessoren mit >= 1GHz cpu Takt wie z.B. den neuen Raspberry 2B o.ä. ?).

Hier ist der Due-Code in "quite exactly ANSI C" - das dafür wichtige Speichern und Laden erlernter Muster von SD fehlt noch.

Für den Testbetrieb momentan für 12 (-100) Inputs (IO-Sensoren), 12 "hidden neurons", 12 "output neurons", > 110 verschiedene trainierbare Sensor-Kombinationsmuster (begrenzt durch verfügbares RAM aber i.P. frei skalierbar).

(edit, nun vergrößert: 100 Sensoren, 20 verdeckte Neuronen, 20 Output-Neuronen, 80 verschiedene Muster z.Zt. trainierbar)

Wichtig: alle "Leerstellen" müssen mit echten trainierten Werten aufgefüllt werden oder die Menge im Lernprozess eingegrenzt werden.

Als IO-Sensoren können Taster oder auch virtuelle analoge Sensorbereiche (z.B. Ultraschall-Entfernung 0-10, 11-20, 21-50,...) "verkabelt"werden:

Code: Alles auswählen

/*
Backpropagation Netz / net
optimiert für Arduino Due
Version BPNET 0060

// (C) UTFT Display Driver Lib by Henning Karlsen
// keine freie Verwendung für kommerzielle Zwecke,
// Code frei zur privaten Nutzung gemäss
// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// http://creativecommons.org/licenses/by-nc-sa/3.0/
// Urheberrechte von  H. Karlsen bleiben unberührt

to do:
- save memory to SD file
- plug real physical sensors
- pre-emptive Multitasking (currently not possible for Due)
*/



#include <SPI.h>
#include <SD.h>
#include <UTFT.h>
#include <ardustdio.h>
#include <malloc.h>
#include <DueTimer.h>



//-------------------------------------------------------------------------------------
// neural net size
#define  NMAXIN    108       // max number of inputs (sensors)
#define  NMAXHID    20       // max number hidden layer neurons
#define  NMAXOUT    20       // max number output layer neurons

#define  NMAXPAT    70       // <<< max number of possibly trained patterns;



//-------------------------------------------------------------------------------------
// neural net: neurons and patterns

float    WeightLIn[NMAXIN+1][NMAXHID+1];
float    WeightLOut[NMAXHID+1][NMAXOUT+1];

float    Input[NMAXPAT+1][NMAXIN+1];
float    Target[NMAXPAT+1][NMAXOUT+1];
float    Output[NMAXPAT+1][NMAXOUT+1];
//float    Contxt[NMAXOUT+1];    // Jordan-net: neuron-number == output-number

float    currIn[NMAXIN+1],     // currently polled inputs
         inbuf[NMAXIN+1];      // intermediate stored inputs for editing
float    currOut[NMAXOUT+1],   // currently computed net outputs
         outbuf[NMAXOUT+1];    // intermediate stored outputs

int16_t  NumInput = NMAXIN, NumHidden = NMAXHID, NumOutput = NMAXOUT;
int16_t  NumPattern = 0;       // <<< number of actually trained patterns;

int32_t  epoch;                // training measures
uint32_t BPTime;
float    Error;





//-------------------------------------------------------------------------------------
// TFT LCD
//UTFT   myGLCD(Model, SDA=MISO, SCL, CS, RESET, RS)
//UTFT   myGLCD(QD220A,   A2,    A1,  A5,  A4,   A3);   // adjust model parameter and pins !
  UTFT   myGLCD(QD220A,   50,    49,  52,   0,   51);   // A0->Vc (LED), A4->BoardReset

extern   uint8_t SmallFont[];

#define  LCDWhiteBlack()  {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(  0,   0,   0);}
#define  LCDNormal()      {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(  0,   0,   0);}
#define  LCDInvers()      {myGLCD.setColor(  0,   0,   0); myGLCD.setBackColor(255, 255, 255);}
#define  LCDWhiteRed()    {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(255,   0,   0);}
#define  LCDRedBlack()    {myGLCD.setColor(255,   0,   0); myGLCD.setBackColor(  0,   0,   0);}
#define  LCDYellowBlue()  {myGLCD.setColor(255, 255,   0); myGLCD.setBackColor( 64,  64,  64);}
uint8_t  fontwi= 8;
uint8_t  fonthi=10;
int16_t  LCDmaxX , LCDmaxY ;                // display size
int16_t  _curx_, _cury_,                    // last x,y cursor pos on TFT screen
         _maxx_, _maxy_;                    // max. x,y cursor pos on TFT screen
char     wspace[50];                        // line of white space

void lcdcls()  { myGLCD.clrScr();  _curx_ =0;  _cury_ =0; }
void curlf()   { _curx_=0; if( _cury_ <=(LCDmaxY-10) ) _cury_+=10; else _cury_=0; }

void lcdprintxy(int16_t x, int16_t y, char * str) {
   myGLCD.print(str, x, y);
   _curx_ = x + strlen(str)*fontwi;
   _cury_ = y;  // check for line overflow!
}

void curxy(int16_t x, int16_t y) {_curx_ = x;_cury_ = y;}

void lcdprint(char * str) {
   myGLCD.print(str, _curx_, _cury_);
   _curx_ = _curx_ + strlen(str)*fontwi;
   //_cury_ = _cury_;  // check for line overflow!
}

//-------------------------------------------------------------------------------------







//-------------------------------------------------------------------------------------
// SD Card
#define  SD_CSpin  53
File     myFile;
char     fname[64];
//-------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------
// misc
#define  TimerMS()    millis()
#define  LRAND_MAX    32767
#define  srand(seed)  randomSeed(seed)
#define  rand()       random(LRAND_MAX)
#define  rando()      ((float)rand()/(LRAND_MAX+1))
int32_t  RSeed;

#define  pswitchon(pin)   (!digitalRead(pin))    // btn press for pinMode(pin, INPUT_PULLUP)
#define  pswitchoff(pin)  ( digitalRead(pin))    // btn press for pinMode(pin, INPUT_PULLUP)
#define  pbtn(pin)        (!digitalRead(pin))    // alias
//-------------------------------------------------------------------------------------






//-------------------------------------------------------------------------------------
// user interface:  button pad control pins
#define  PIN_ESC    13
#define  PIN_UP     12
#define  PIN_OK     11
#define  PIN_DN      4 // instead opt.: 6
#define  PIN_LE      3 // instead opt.: 5
#define  PIN_RI      2

// pins 10,9,8,7 : motor
// pins 22 - 37 :  motor
// pins 49 - 51 :  LCD TFT
// pin  53:        SD CS

// available: 5+6, 38-48 for digital touch pins

//-------------------------------------------------------------------------------------
int16_t  btnpressed() {
   return ( pbtn(PIN_ESC)||pbtn(PIN_UP)||pbtn(PIN_OK)||pbtn(PIN_DN)||pbtn(PIN_LE)||pbtn(PIN_RI) );
}
//-------------------------------------------------------------------------------------
int16_t   getbtn() {
   int16_t  choice= -1;
   
   while (!  btnpressed() );  // wait until button pad pressed
   
   if( pbtn(PIN_ESC) ) choice = PIN_ESC;
   if( pbtn(PIN_UP) )  choice = PIN_UP;
   if( pbtn(PIN_OK) )  choice = PIN_OK;
   if( pbtn(PIN_DN) )  choice = PIN_DN;
   if( pbtn(PIN_LE) )  choice = PIN_LE;
   if( pbtn(PIN_RI) )  choice = PIN_RI;   
   
   while (  btnpressed() );   // wait until button pad released
   
   return choice;   
}


//-------------------------------------------------------------------------------------
// misc. tools

int16_t  toggleup(int16_t lo, int16_t hi, int16_t val ) {
  if ( val < hi )  val++;
  else val = lo;
  return val;   
}   

int16_t  toggledn(int16_t lo, int16_t hi, int16_t val ) {
  if ( val > lo )  val--;
  else val = hi;
  return val;   
}

   
   

//-------------------------------------------------------------------------------------
// motor control

#define MAXMOTORS   4  // max number of encoder motors at Arduino Uno=2 // Due=6 // Mega=8

// motor 0
#define pinenc0A   22  // enc0A yellow
#define pinenc0B   23  // enc0B blue
#define pinmot0d1  24  // dir0-1   <<
#define pinmot0d2  25  // dir0-2
#define pinmot0pwm 10  // pwm enable0   

// motor 1
#define pinenc1A   26  // enc1A yellow
#define pinenc1B   27  // enc1B blue
#define pinmot1d1  28  // dir1-1   <<
#define pinmot1d2  29  // dir1-2
#define pinmot1pwm  9  // pwm enable1   


// motor 2
#define pinenc2A   30  // enc2A yellow
#define pinenc2B   31  // enc2B blue
#define pinmot2d1  32  // dir2-1   <<
#define pinmot2d2  33  // dir2-2
#define pinmot2pwm  8  // pwm enable2   

// motor 3
#define pinenc3A   34  // enc3A yellow
#define pinenc3B   35  // enc3B blue
#define pinmot3d1  36  // dir3-1   <<
#define pinmot3d2  37  // dir3-2
#define pinmot3pwm  7  // pwm enable3   



   
//-------------------------------------------------------------------------------------
// motor bit patterns
const char COAST[]={0,0,0};
const char BREAK[]={0,0,1};
const char FWSLOW[]={0,1,0};
const char RVSLOW[]={0,1,1};
const char FWMED[]={1,0,0};
const char RVMED[]={1,0,1};
const char FWFAST[]={1,1,0};
const char RVFAST[]={1,1,1};

#define    fwslow   20
#define    rvslow  -20
#define    fwmed    60
#define    rvmed   -60
#define    fwfast  100
#define    rvfast -100


//=====================================================================================
// SETUP ()
//=====================================================================================
void setup() {
   char     sbuf[128];  // output string
   
   Serial.begin(115200);

   pinMode(PIN_ESC,INPUT_PULLUP);
   pinMode(PIN_UP, INPUT_PULLUP);
   pinMode(PIN_OK, INPUT_PULLUP);
   pinMode(PIN_DN, INPUT_PULLUP);
   pinMode(PIN_LE, INPUT_PULLUP);
   pinMode(PIN_RI, INPUT_PULLUP);


   myGLCD.InitLCD();
   LCDmaxX=myGLCD.getDisplayXSize();
   LCDmaxY=myGLCD.getDisplayYSize();
   myGLCD.setFont(SmallFont);
   _maxx_ = LCDmaxX / fontwi;
   _maxy_ = LCDmaxX / fonthi;
 
   memset(wspace, ' ', _maxx_);
   wspace[_maxx_]='\0';
   lcdcls();

   sprintf(sbuf, "Serial ok, GLCD=%dx%d",LCDmaxX,LCDmaxY);
   Serial.println(sbuf);
   myGLCD.print  (sbuf, 0, 0);
   
   ResetNet();

   RSeed = ( ((analogRead(A8)+1017)%100) * (TimerMS()% LRAND_MAX ) % LRAND_MAX );
   srand(RSeed);

   // # DEBUG
   sprintf(sbuf, "Seed= %ld", RSeed);
   myGLCD.print  (sbuf, 0,10);
   sprintf(sbuf, "Rand= %ld", rand() );
   myGLCD.print  (sbuf, 100,10);
   
   
   
   // # DEBUG
   memtest(20, "memtest: setup");
   
   SetNetDefaultPatterns();
   RefreshInputs();             // polls inputs and stores values to currIn[] array
     
     
      //***********************************
                  //*******************************************************************************
      // debug: test different input sets
       currIn[1]=1; //currIn[2]=0; currIn[3]=1; //currIn[4]=0; currIn[5]=0; currIn[6]=0; currIn[7]=0;
                  //*******************************************************************************   
      //***********************************               
           
     
    ComputeMatrix();         // applies inputs to net and computes outputs to currOut[] array
    TrainNet();              // basic training
   

}
//=====================================================================================
//=====================================================================================
   
   
   
   

//-------------------------------------------------------------------------------------
// analog range bit patterns
// returns 6 bits for ranges -32768...0...32767 or out of bounds (VOID==[32])
float Ana[64][8]={
     {0,0,0,0,0,0,0,0},  // 0
     {0,1,0,0,0,0,0,1},  // 0...1
     {1,2,0,0,0,0,1,0},  // 1...2
     {2,3,0,0,0,0,1,1},

     {3,4,0,0,0,1,0,0},
     {4,5,0,0,0,1,0,1},
     {5,6,0,0,0,1,1,0},
     {6,8,0,0,0,1,1,1},

     {8,10,0,0,1,0,0,0},
     {10,13,0,0,1,0,0,1},
     {13,16,0,0,1,0,1,0},
     {16,20,0,0,1,0,1,1},

     {20,26,0,0,1,1,0,0},
     {26,32,0,0,1,1,0,1},
     {32,40,0,0,1,1,1,0},
     {40,48,0,0,1,1,1,1},

     {48,56,0,1,0,0,0,0},
     {56,64,0,1,0,0,0,1},
     {64,74,0,1,0,0,1,0},
     {74,85,0,1,0,0,1,1},

     {85,97,0,1,0,1,0,0},
     {97,110,0,1,0,1,0,1},
     {110,128,0,1,0,1,1,0},
     {128,192,0,1,0,1,1,1},

     {192,256,0,1,1,0,0,0},
     {256,420,0,1,1,0,0,1},
     {420,512,0,1,1,0,1,0},
     {512,650,0,1,1,0,1,1},

     {650,800,0,1,1,1,0,0},
     {800,1024,0,1,1,1,0,1},
     {1024,2048,0,1,1,1,1,0},
     {2048,32767,0,1,1,1,1,1},   // <= SHORTMAX
     
     {0,0,1,0,0,0,0,0},     // #32: VOID !
     {0,-1,1,0,0,0,0,1},    // 0... -1
     {-1,-2,1,0,0,0,1,0},   // -1...-2
     {-2,-3,1,0,0,0,1,1},

     {-3,-4,1,0,0,1,0,0},
     {-4,-5,1,0,0,1,0,1},
     {-5,-6,1,0,0,1,1,0},
     {-6,-8,1,0,0,1,1,1},

     {-8,-10,1,0,1,0,0,0},
     {-10,-13,1,0,1,0,0,1},
     {-13,-16,1,0,1,0,1,0},
     {-16,-20,1,0,1,0,1,1},

     {-20,-26,1,0,1,1,0,0},
     {-26,-32,1,0,1,1,0,1},
     {-32,-40,1,0,1,1,1,0},
     {-40,-48,1,0,1,1,1,1},

     {-48,-56,1,1,0,0,0,0},
     {-56,-64,1,1,0,0,0,1},
     {-64,-74,1,1,0,0,1,0},
     {-74,-85,1,1,0,0,1,1},

     {-85,-97,1,1,0,1,0,0},
     {-97,-110,1,1,0,1,0,1},
     {-110,-128,1,1,0,1,1,0},
     {-128,-192,1,1,0,1,1,1},

     {-192,-256,1,1,1,0,0,0},
     {-256,-420,1,1,1,0,0,1},
     {-420,-512,1,1,1,0,1,0},
     {-512,-650,1,1,1,0,1,1},

     {-650,-800,1,1,1,1,0,0},
     {-800,-1024,1,1,1,1,0,1},
     {-1024,-2048,1,1,1,1,1,0},
     {-2048,-32768,1,1,1,1,1,1}    // >= -SHORTMAX
 
     };
     
//-------------------------------------------------------------------------------------     
     
int16_t  ana2bits(int16_t  aval) {         // return array index
   int16_t   i,j;
   if(aval == 0) return 0;
   else
   if( aval > 32767 ) return 32;   // VOID
   else
   if( aval < -32768 ) return 32;  // VOID
   else {
     for (i=1; i<32; ++i) {
        if ( (aval>Ana[0][i]) && (aval<=Ana[1][i]) ) return i;
     }   
     for (i=32; i<64; ++i) {
        if ( (aval<Ana[0][i]) && (aval>=Ana[1][i]) ) return i;
     }
   }
   return 32;
}   





//-------------------------------------------------------------------------------------
// mem test
extern  char _end;
extern  "C" char *sbrk(int i);
char    *ramstart=(char *)0x20070000;
char    *ramend=(char *)0x20088000;

//=====================================================================================
void memtest(int ypos, char * str) {
   char     sbuf[128];  // output string
   
   char *heapend=sbrk(0);
   register char * stack_ptr asm ("sp");
   struct mallinfo mi=mallinfo();

   sprintf(sbuf,str);
   Serial.println(); Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos);

   sprintf(sbuf, "Dyn.RAM used: %-10ld ", mi.uordblks);
   Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+10);

   sprintf(sbuf, "Prg.stat.RAM used %-10ld ", & _end - ramstart);
   Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+20);

   sprintf(sbuf, "Stack RAM used %-10ld ", ramend - stack_ptr);
   Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+30);

   sprintf(sbuf, "Free mem: %-10ld ", stack_ptr - heapend + mi.fordblks);
   Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+40);
   myGLCD.print(wspace, 0, ypos+50);

}















//=====================================================================================
// net: clear, set, reset, patch
//=====================================================================================

void clrnetbuf() {
  memset(inbuf,  0, sizeof(inbuf) );
  memset(currIn, 0, sizeof(currIn) );
  memset(outbuf, 0, sizeof(outbuf) );
  memset(currOut, 0, sizeof(outbuf) );
}

//=====================================================================================
 
void ResetNet(){
   
   memset(Input, 0, sizeof(Input) );   
   memset(Target, 0, sizeof(Target) );
   //memset(Contxt, 0, sizeof(Contxt) );

   clrnetbuf();
   NumPattern=0;
}









//=====================================================================================
// set input/target patterns manually
//=====================================================================================

int16_t   setIOpattern(int16_t  patt) {
   int16_t  result;
   char     sbuf[128];  // output string
   
   if(patt==-1) patt = CheckTestPattern(inbuf);        // pattern number if known, if not: -1
   
   if( patt > 0 ) sprintf(sbuf, "  PATCH target #%-3d", patt);
   else  sprintf(sbuf, "  NEW  target #%-3d", NumPattern+1);
   
   
   result=menu_setouttargets(sbuf);
   if (result == -1) return (result);     // break by escape btn   
   
   if (patt == -1) {                      // unknown => add new pattern!
      SetNewPattern( inbuf,  outbuf );
      result=NumPattern;
   }
   else {                                 // override old pattern
      PatchPattern( patt,  inbuf,  outbuf);
      result=patt;
   }

   return result;                        // return written pattern number
   
}













//=====================================================================================

//=====================================================================================
// display net inputs and target outputs
//=====================================================================================
void DisplayNetTrainingIOs(int16_t code) {
    int16_t   i, o, p;
    char      sbuf[128];  // output string
    char msg[20]="! SUCCESS !"  ;
    if (code==0) strcpy(msg,"calc.error");
    if (code==2) strcpy(msg,"user-break");

    /* print network outputs */
    Serial.println(); Serial.println();
    Serial.println(msg);

    sprintf(sbuf, "%-6ld:  Err= %9.7f", epoch, Error) ;
    Serial.print("Epoch "); Serial.println(sbuf);
    myGLCD.print(sbuf, 0, 20);
    myGLCD.print(msg, 0, 30);
       
    sprintf(sbuf, "NET TRAINING TIME = %ld sec \n", BPTime/1000);   Serial.println(sbuf);
   
    Serial.println();
    sprintf(sbuf, "Patt. ") ; Serial.print(sbuf); myGLCD.print(sbuf, 0, 40);
    for( i = 1 ; i <= NumInput ; i++ ) {
        sprintf(sbuf, "Inp%-3d ", i) ;  Serial.print(sbuf);
    }
    for( o = 1 ; o <= NumOutput ; o++ ) {
        sprintf(sbuf, "Targ%-3d Outp%-3d ", o, o); Serial.print(sbuf);
    }
    for(int p = 1 ; p <= NumPattern ; p++ ) {
       Serial.println();
       sprintf(sbuf, "%3d  ", p) ;  Serial.print(sbuf); myGLCD.print(sbuf, 40, 40);
       for( i = 1 ; i <= NumInput ; i++ ) {
            sprintf(sbuf, "%5.2f  ", Input[p][i]) ; Serial.print(sbuf);
        }
        for( o = 1 ; o <= NumOutput ; o++ ) {
            sprintf(sbuf, "%5.2f   %5.2f   ", Target[p][o], Output[p][o]) ;  Serial.print(sbuf);
        }
    }
    Serial.println(); Serial.println();
    sprintf(sbuf, "BPtrained, returning to main()...!") ;  Serial.print(sbuf);
    Serial.println(); Serial.println();

}


//=====================================================================================


void displayMatrix(int16_t patt) {
 
   int32_t  btn=-1;
   int16_t  i, l, o, ibuf, p, lval=1, hval=NumPattern;
   float    fbuf;
   char     msgline[30], sbuf[30];
   char     valline[30];     
   
   p=patt;
   if(p==0) p=1;
   if(p>NumPattern) p=NumPattern;
   
   do {   
      strcpy(msgline,"     12345678901234567890");
      sprintf(sbuf, "%-4d", p);
      strinsert(msgline, sbuf, 0);
      LCDWhiteRed();
      Serial.println(msgline); lcdprintxy(0,0,msgline); curlf();
      LCDNormal();
     
      for (i=1; i<=NMAXIN; ++i) {
            l=(i-1)/20;
            if (i%20==1) {             
               sprintf(sbuf, "%4d ", i-1);
               Serial.print(sbuf);  lcdprint(sbuf);
            }
            ibuf = round(Input[p][i]);
            sprintf(sbuf, "%1d", ibuf);
            Serial.print(sbuf);  lcdprint(sbuf);             
            if (i%20==0) {             
               Serial.println();
               curlf();
            }
      }   
      Serial.println();  curlf();     
      strcpy(msgline,"TARG 12345678901234567890");
      LCDRedBlack();
      Serial.println(msgline); lcdprint(msgline); curlf();
      LCDNormal();
     
      for (i=1; i<=NMAXOUT; ++i) {
            l=(i-1)/20;
            if (i%20==1) {             
               sprintf(sbuf, "%4d ", i-1);
               Serial.print(sbuf);  lcdprint(sbuf);
            }
            ibuf = round(Target[p][i]);
            sprintf(sbuf, "%1d", ibuf);
            Serial.print(sbuf);  lcdprint(sbuf);             
            if (i%20==0) {             
               Serial.println();
               curlf();
            }
      }
      Serial.println();
     
      LCDYellowBlue();
      sprintf(msgline, "toggle patt +-1: LEFT/RIGHT");
      Serial.println(msgline);  lcdprintxy(0, LCDmaxY-20, msgline);
      sprintf(msgline, "+-10:UP/DN edit:OK quit:ESC");
      Serial.println(msgline);  lcdprintxy(0, LCDmaxY-10, msgline);     
      LCDNormal();
   
      btn=getbtn();
      if ( (btn==PIN_RI) || (btn==PIN_LE) ) {             // browse displayed pattern
        if (btn==PIN_RI) p=toggleup(lval, hval, p);
          if (btn==PIN_LE) p=toggledn(lval, hval, p);         
      }
      if ( (btn==PIN_UP) || (btn==PIN_DN) ) {             // browse displayed pattern
        if (btn==PIN_UP) p=toggleup(lval, hval, p+9);
          if (btn==PIN_DN) p=toggledn(lval, hval, p-9);         
      }
      if ( (btn==PIN_OK) ) {                              // change displayed pattern         
        setIOpattern(p);       
      }
     
   } while ( (btn!=PIN_ESC ) );
}

//=====================================================================================

int16_t RefreshInputs() {        // polls inputs and stores values to currIn[] array

   // poll digital touch values and store directly (1 Dpin = 1 input)                16 DPins  == 16
   // poll analog sensor values and store bit pattern for ranges (1 Apin = 6 inputs)  8 A10bit == 48
   // poll motor speed and store bit pattern for speed ranges (1 Apin = 6 inputs)     4 motors == 24
   // Jordan/Elman neural context neurons == feedback inputs (1...NMAXCON)           20 inputs == 20
   //                                                                                           =108
   int16_t   i, cx;
   
   cx = NMAXIN - NMAXOUT +1;   // last NMAXCON inputs reserved for context neurons
   //for(i=cx; i<= NMAXIN; ++i) { currIn[i] = Contxt[i] ; }   //
   
}


//=====================================================================================

void  ComputeMatrix() {         // applies currIn[] inputs to net and computes outputs (outbuf[])
    int16_t  i, j, o;
    float    SumH[NMAXHID+1];
    float    SumO[NMAXOUT+1];
    float    HidOut[NMAXHID+1];
    float    SumDOW[NMAXHID+1];
    float    lambda= 0.5;      // context neuron self activation rate
   
    memset(SumH, 0, sizeof(SumH) );
    memset(SumO, 0, sizeof(SumO) );
    memset(HidOut, 0, sizeof(HidOut) );
    memset(SumDOW, 0, sizeof(SumDOW) );
   
    for( j = 1 ; j <= NumHidden ; j++ ) {                  // compute hidden unit activations 
          SumH[j] = WeightLIn[0][j] ;                      // bias neuron
          for( i = 1 ; i <= NumInput ; i++ ) {
              SumH[j] += currIn[i] * WeightLIn[i][j] ;
          }
          HidOut[j] = 1.0/(1.0 + exp(-SumH[j])) ;          // Sigmoidal Outputs
    }
           
    for( o = 1 ; o <= NumOutput ; o++ ) {                  // compute output unit activations and errors 
          SumO[o] = WeightLOut[0][o] ;                     // bias neuron
          for( j = 1 ; j <= NumHidden ; j++ ) {
              SumO[o] += HidOut[j] * WeightLOut[j][o] ;
          }
          currOut[o] = 1.0/(1.0 + exp(-SumO[o])) ;           // Sigmoidal Outputs       
    }
   
    //for( o = 1 ; o <= NumOutput ; o++ ) {                   // compute context neurons   
    //      Contxt[o] = lambda*Contxt[o] + (1-lambda)*currOut[o];  // assign outputs*weight to context neurons                       
    //}
 
}


//=====================================================================================
// check for known patterns
//=====================================================================================
int16_t  CheckTestPattern(float * test) {
   int16_t  i, p;
   
   for( p = 1; p <= NumPattern ; ++p) {
      for( i = 1; (i <= NumInput) && ( Input[p][i] == test[i] ); ++i);
      if (i > NumInput) {return p; }
   }   
   return -1;
}


//=====================================================================================
// patch old pattern
//=====================================================================================
void PatchPattern(int16_t  patt,  float * _ibuf,  float * _obuf ){
   int16_t i, o;
   
   if ( (NumPattern <= NMAXPAT) && ( NumPattern > 0) ) {
      // # DEBUG
      //Serial.print("NumPattern="); Serial.print(NumPattern);  Serial.print("  SetNetPattern="); Serial.println(patt);
      for(i=1; i<=NMAXIN;  ++i) { Input[patt][i]  = _ibuf[i]; }
      for(o=1; o<=NMAXOUT; ++o) { Target[patt][o] = _obuf[o]; }
   }
}


//=====================================================================================
// add new pattern
//=====================================================================================
void SetNewPattern(float * _ibuf,  float * _obuf ){
  int16_t i, o;
   
  if ( (NumPattern < NMAXPAT) && ( NumPattern >= 0) ) {
      NumPattern++;
      // # DEBUG
      //Serial.print("NumPattern="); Serial.print(NumPattern);  Serial.print("  SetNetPattern="); Serial.println(patt);
      for(i=1; i<=NMAXIN;  ++i) { Input[NumPattern][i]  = _ibuf[i]; }
      for(o=1; o<=NMAXOUT; ++o) { Target[NumPattern][o] = _obuf[o]; }
   }
}


//=====================================================================================
// set default net patterns
//=====================================================================================
void SetNetDefaultPatterns(){
    float _ibuf[NMAXIN+1], _obuf[NMAXOUT+1];   
       
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=0; _ibuf[2]=0; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=1;
    _obuf[1]=1; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=1; _ibuf[2]=0; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=1; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=0; _ibuf[2]=1; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=1; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
   _ibuf[1]=1; _ibuf[2]=1; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=1; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=0; _ibuf[2]=0; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=1; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=1; _ibuf[2]=0; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=1; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );
    _ibuf[1]=0; _ibuf[2]=1; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=1; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
 
    memset( _ibuf, 0, sizeof(_ibuf) );   memset( _obuf, 0, sizeof(_obuf) );   
    _ibuf[1]=1; _ibuf[2]=1; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
    _obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=1; _obuf[9]=0; _obuf[10]=0;
    SetNewPattern( _ibuf, _obuf);
   
}





//=====================================================================================
// Backpropagation Learning
//=====================================================================================
// Backpropagation-C-Implementierung:  nn.c   1.0 (C) JOHN BULLINARIA  2004
// verändert und portiert auf Arduino Sketch C: HELMUT WUNDER ("HaWe") 2015
//=====================================================================================
int16_t  TrainNet() {
    char     sbuf[128];  // output string
    int16_t  i, j, o, p, np, op, ranpat[NumPattern+1], result=0, ri, rj, ro, rp;
    int32_t  offset=0;
    const    float    minerr=(1E-4)*(NMAXHID+NMAXOUT);
   
    float    SumH[NMAXPAT+1][NMAXHID+1];
    float    SumO[NMAXPAT+1][NMAXOUT+1];
    float    HidOut[NMAXPAT+1][NMAXHID+1];
    float    SumDOW[NMAXHID+1];
    float    DeltaO[NMAXOUT+1], DeltaH[NMAXHID+1];
    float    DeltaWeightLIn[NMAXIN+1][NMAXHID+1], DeltaWeightLOut[NMAXHID+1][NMAXOUT+1];
   
    float    derror, oerror, oderror,
             eta = 0.6,         // gradient descent contribution
             alpha = 0.8,       // 'momentum' term which effectively keeps a moving average
                                // of the gradient descent weight change contributions...
             smallwt = 0.5;     // smallwt is the maximum absolute size of your initial weights
         
    /*  The weight changes DeltaWeightIH and DeltaWeightHO are each made up of two components.
    First, the eta component that is the gradient descent contribution.
    Second, the alpha component that is a 'momentum' term which effectively keeps a moving average
    of the gradient descent weight change contributions, and thus smoothes out the overall weight changes.
    Fixing good values of the learning parameters eta and alpha is usually a matter of trial and error.
    Certainly alpha must be in the range 0 to 1, and a non-zero value does usually speed up learning.
    Finding a good value for eta will depend on the problem, and also on the value chosen for alpha.
    If it is set too low, the training will be unnecessarily slow.
    Having it too large will cause the weight changes to oscillate wildly, and can slow down or
    even prevent learning altogether.
    (I generally start by trying eta = 0.1 and explore the effects of repeatedly doubling or halving it.
    */

    // # DEBUG
    memtest(70, "memtest: TrainBPNet");
    delay(1000);
   
    lcdcls();
    Serial.println(); Serial.println();   
    sprintf(sbuf, "NET TRAINING started") ;  Serial.println(sbuf);
    Serial.println();
    myGLCD.print(sbuf, 0, 10);
   
    uint32_t TimeStamp=TimerMS();

    for( j = 1 ; j <= NumHidden ; j++ ) {    /* initialize WeightLIn and DeltaWeightLIn */
        for( i = 0 ; i <= NumInput ; i++ ) {
            DeltaWeightLIn[i][j] = 0.0 ;
            WeightLIn[i][j] = 2.0 * ( rando() - 0.5 ) * smallwt ;
        }
    }
    for( o = 1 ; o <= NumOutput ; o ++ ) {    /* initialize WeightLOut and DeltaWeightLOut */
        for( j = 0 ; j <= NumHidden ; j++ ) {
            DeltaWeightLOut[j][o] = 0.0 ;
            WeightLOut[j][o] = 2.0 * ( rando() - 0.5 ) * smallwt ;
        }
    }

    for( epoch = 0 ; epoch <= 30000 ; epoch++) {    /* iterate weight updates */
        for( p = 1 ; p <= NumPattern ; p++ ) {    /* randomize order of individuals */
            ranpat[p] = p ;
        }
        for( p = 1 ; p <= NumPattern ; p++) {
            np = p + rando() * ( NumPattern + 1 - p ) ;
            op = ranpat[p] ; ranpat[p] = ranpat[np] ; ranpat[np] = op ;
        }
        Error = 0.0 ;
        for( np = 1 ; np <= NumPattern ; np++ ) {    /* repeat for all the training patterns */
            p = ranpat[np];
           
           
           
            for( j = 1 ; j <= NumHidden ; j++ ) {    /* compute hidden unit activations */
                SumH[p][j] = WeightLIn[0][j] ;
                for( i = 1 ; i <= NumInput ; i++ ) {
                    SumH[p][j] += Input[p][i] * WeightLIn[i][j] ;
                }
                HidOut[p][j] = 1.0/(1.0 + exp(-SumH[p][j])) ;               
               
            }
           
           
           
            for( o = 1 ; o <= NumOutput ; o++ ) {    /* compute output unit activations and errors */
                SumO[p][o] = WeightLOut[0][o] ;
                for( j = 1 ; j <= NumHidden ; j++ ) {
                    SumO[p][o] += HidOut[p][j] * WeightLOut[j][o] ;
                }
                Output[p][o] = 1.0/(1.0 + exp(-SumO[p][o])) ;   /* Sigmoidal Outputs */
               
               
               
                oerror = Error;                     
                Error += 0.5 * (Target[p][o] - Output[p][o]) * (Target[p][o] - Output[p][o]) ;   /* SSE */
               
               
               
               
                oderror = derror;                               // stalling ?
                derror  = Error-oerror;
                if ( (epoch <= 10000) && (Error>=0.5) && ((epoch-offset)>1000)
                     && (abs(derror-oderror)<(minerr/1000) )  ) {
                   offset=epoch;
                   for( o = 1 ; o <= NumOutput ; o ++ ) {    /* initialize WeightLOut and DeltaWeightLOut */
                      for( j = 0 ; j <= NumHidden ; j++ ) {
                        DeltaWeightLOut[j][o] = 0.0 ;
                        WeightLOut[j][o] = 2.0 * ( rando() - 0.5 ) * smallwt ;
                      }
                   }
                   
                   if( (epoch-offset)%3000==0) {
                     for( j = 1 ; j <= NumHidden ; j++ ) {   
                       for( i = 0 ; i <= NumInput ; i++ ) {
                          DeltaWeightLIn[i][j] = 0.0 ;
                          WeightLIn[i][j] = 2.0 * ( rando() - 0.5 ) * smallwt ;
                       }
                     } 
                   }
                   
                   
                   
                   
                   
                }   
                DeltaO[o] = (Target[p][o] - Output[p][o]) * Output[p][o] * (1.0 - Output[p][o]) ;   /* Sigmoidal Outputs, SSE */
            }
            for( j = 1 ; j <= NumHidden ; j++ ) {    /* 'back-propagate' errors to hidden layer */
                SumDOW[j] = 0.0 ;
                for( o = 1 ; o <= NumOutput ; o++ ) {
                    SumDOW[j] += WeightLOut[j][o] * DeltaO[o] ;
                }
                DeltaH[j] = SumDOW[j] * HidOut[p][j] * (1.0 - HidOut[p][j]) ;
            }
            for( j = 1 ; j <= NumHidden ; j++ ) {     /* update weights WeightLIn */
                DeltaWeightLIn[0][j] = eta * DeltaH[j] + alpha * DeltaWeightLIn[0][j] ;
                WeightLIn[0][j] += DeltaWeightLIn[0][j] ;
               
                for( i = 1 ; i <= NumInput ; i++ ) {
                    DeltaWeightLIn[i][j] = eta * Input[p][i] * DeltaH[j] + alpha * DeltaWeightLIn[i][j];
                    WeightLIn[i][j] += DeltaWeightLIn[i][j] ;
                }
            }
            for( o = 1 ; o <= NumOutput ; o ++ ) {    /* update weights WeightLOut */
                DeltaWeightLOut[0][o] = eta * DeltaO[o] + alpha * DeltaWeightLOut[0][o] ;
                WeightLOut[0][o] += DeltaWeightLOut[0][o] ;
                for( j = 1 ; j <= NumHidden ; j++ ) {
                    DeltaWeightLOut[j][o] = eta * HidOut[p][j] * DeltaO[o] + alpha * DeltaWeightLOut[j][o] ;
                    WeightLOut[j][o] += DeltaWeightLOut[j][o] ;
                }
            }
        }
        if( epoch % 10 == 0 )
        {
           sprintf(sbuf, "%-6ld:  Err= %9.7f", epoch, Error) ;
           Serial.print("Epoch "); Serial.println(sbuf);
           myGLCD.print(sbuf, 0, 20);
        }

        if(Error < minerr) {result=1; break;}                          // stop learning when 'near enough'
        if(pbtn(PIN_ESC)){delay(10); while(pbtn(PIN_ESC)); result=2; break;} // stop after Btn press-and-release
    }


       
    BPTime = TimerMS() - TimeStamp;
    return result ;
}












//=====================================================================================
// REPETITIVE LOOP ()
//=====================================================================================
void loop() {
   int16_t  result, quit=0;
   int32_t  choice, btn;
   char     sbuf[128], spatt[20];  // output string
   
   msg_userctrl();   
   Serial.println();

   while(!quit) {

      RefreshInputs();             // polls inputs and stores values to currIn[] array
     
     
      //***********************************
                  //*******************************************************************************
      // debug: test different input sets
       currIn[1]=1; //currIn[2]=0; currIn[3]=1; //currIn[4]=0; currIn[5]=0; currIn[6]=0; currIn[7]=0;
                  //*******************************************************************************   
      //***********************************               
           
     
      ComputeMatrix();         // applies inputs to net and computes outputs to currOut[] array
 
     
      curxy(0,  LCDmaxY - fonthi*(6+NMAXOUT/20) );
      LCDInvers();
      lcdprint("12345678901234567890");
      LCDNormal();
      curlf(); 
      for (int16_t  o=1; o<=20; ++o) {
            int16_t ibuf = round(currOut[o]);
            sprintf(sbuf, "%1d", ibuf);
            lcdprint(sbuf);             
            if (o%20==0) {             
               curlf(); 
            }
      }
     

     
      result=CheckTestPattern(currIn);
      sprintf(spatt,"det.inpatt=%3d", result);                 
      myGLCD.print( spatt, 0, (LCDmaxY)-(3*fonthi) );     
     
     
      choice=0;   
     
      // check for btn press
     
      if (btnpressed()) {
         btn=getbtn();
      }
      else btn=-1;           
           
      // action for button press
     
      if( btn==PIN_LE )   {                         // flash current inputs into buffer for processing
         memcpy ( inbuf, currIn, sizeof(currIn) );
         setIOpattern(-1);
      }       
      else   
      if( btn==PIN_OK )  {                          // menu: set net input->target output pattern           
         choice = menu_setinpattern("Menu: Set Inputs");  // define net input pattern
         if (choice != -1)  setIOpattern(-1);             // set related net target output pattern
      }           
      else
      if( btn==PIN_RI )   {                         // train net         
         result=TrainNet();
         DisplayNetTrainingIOs(result);
         msg_userctrl();
      }
      else
      if( btn==PIN_ESC )  clrnetbuf();              // clear buffers
     
      else
      if( btn==PIN_UP )  {                          // main menu
         choice = menu_0("menu (N/A)");         
      }
     
      else
      if( btn==PIN_DN )  {                          // # DEBUG
      for (int i=1; i <= NMAXIN; ++i) {
             sprintf(sbuf, " %003d", i); Serial.print(sbuf);
         }
         Serial.println();
         for (int i=1; i <= NMAXIN; ++i) {
             sprintf(sbuf, "%4.1f", currIn[i]); Serial.print(sbuf);
         }     
         Serial.println(); Serial.println();
         for (int o=1; o <= NMAXOUT; ++o) {
             sprintf(sbuf, "%4.1f", currOut[o]); Serial.print(sbuf);
         }     
         Serial.println();
         Serial.println(spatt);
         Serial.println(); Serial.println();
      }
     
      if( btn >=0 ) {
         Serial.print("choice="); Serial.println(choice);   // # DEBUG
         msg_userctrl();   
      }
   }

   sprintf(sbuf, "\n\nGoodbye!\n\n") ;  Serial.print(sbuf);
   while(true);
   
}


// OK: check pattern, add new pattern, patch old pattern
// OK: learn converging and escape stalling (local min)

//=====================================================================================
//=====================================================================================


/*


M           M  EEEEEEEEEEE  NN          N  U           U
MM         MM  E            N N         N  U           U
M M       M M  E            N  N        N  U           U
M  M     M  M  E            N   N       N  U           U
M   M   M   M  E            N    N      N  U           U
M    M M    M  EEEEEEE      N     N     N  U           U
M     M     M  E            N      N    N  U           U
M           M  E            N       N   N  U           U
M           M  E            N        N  N   U         U
M           M  E            N         N N    U       U
M           M  EEEEEEEEEEE  N          NN     UUUUUUU 


*/

//=====================================================================================
// bottom line user control menu
//=====================================================================================
void msg_userctrl() {
   char     sbuf[128];  // output string   
 
   Serial.println();
   LCDYellowBlue();
   sprintf(sbuf, "Menu:UP clr:ESC learn:RIGHT");
   Serial.println(sbuf);
   myGLCD.print(sbuf, 0, LCDmaxY-20);

   sprintf(sbuf, "inp.patt.set:OK  read:LEFT ");
   Serial.println(sbuf);
   myGLCD.print(sbuf, 0, LCDmaxY-10);   
   
   LCDNormal();   
}




//=====================================================================================
// LCD-TFT menu system
//=====================================================================================

int32_t  menu_0(char caption[] ) {          // main menu               
  const   int16_t MAXMSIZE = 8;                // number of maximum useable menu options
  const   char LSIZE=20;
 
  char    astr[MAXMSIZE][LSIZE+1],               // all options by all inscription string
          opbuf[LSIZE+1],                        // option buffer
          numbuf[LSIZE+1],                       // num buffer
          last[LSIZE+1] ,  // end of line buffer
          more[LSIZE+1] ,  // next line buffer
          space[LSIZE+1] , // empty line buffer         
          bobross=0;                             // if to paint a beautiful new menu by beautiful colors ;)

         
         
  static   int16_t  ch=0, maxframe=8, minframe=0; // static for re-entering the menu;
  int16_t  btn, i, j, lch, MAXVOPT=8;
  int16_t  val=0, lval=0, hval=3;
 
 
  memset(last, '-', LSIZE );  last[LSIZE]='\0';
  memset(more, '+', LSIZE );  more[LSIZE]='\0';
  memset(space, ' ', LSIZE ); space[LSIZE]='\0';
 
  for (i=0; i<MAXMSIZE; ++i) {
     strcpy(astr[i], space );   
     sprintf(opbuf, "%3d", i);
     strinsert(astr[i], opbuf, 0);
  }
 
 
  strinsert(astr[ 0], "File load  ->  0", 4);
  strinsert(astr[ 1], "File safe  ->  0", 4);
  strinsert(astr[ 2], "show patt. ->  0", 4);
  strinsert(astr[ 3], "erase patt.->  0", 4);
  strinsert(astr[ 4], "option  e ", 4);
  strinsert(astr[ 5], "option  f ", 4);
  strinsert(astr[ 6], "option  g ", 4);
  strinsert(astr[ 7], "option  h ", 4);

  strinsert(astr[MAXMSIZE-1], "option:last ", 4);

  if (ch<0) ch=0;
  if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }
 
  lcdcls();
  LCDWhiteRed();
  myGLCD.print(space, 0, 0);             
  myGLCD.print(caption, (20-strlen(caption))*8/2, 0);
 
  LCDNormal();
  for (i=minframe; i<maxframe; ++i) {
     myGLCD.print(astr[i], 0, (i+1-minframe)*10);
  }     
 
  LCDRedBlack();
  if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10);
  else myGLCD.print(last, 0, (MAXVOPT+1)*10);
 
  LCDInvers();
  myGLCD.print(astr[ch], 0, (ch+1-minframe)*10);
  LCDNormal();

  do {
    lch=ch;
    btn=getbtn();       
   
    if ( ch < 2 )    { lval=0; hval=3;}
    else if ( ch==2) { lval=0; hval=NumPattern;}  // 0 == show all
    else if ( ch==3) { lval=0; hval=NumPattern;}  // 0 == VOID (for safety unerase)
    else { lval=0; hval=0;}
       
       
    if ( (btn==PIN_DN ) ||  (btn==PIN_UP ) ) {   
      val=lval;   
      if (btn==PIN_DN )
      {   
         if(ch<MAXMSIZE-1)  ch++;
         else {
             ch=0;
             minframe=0;
             maxframe=MAXVOPT;
             if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
          }   
          if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;}       
       }   
       else
       if (btn==PIN_UP ) {
          if (ch > 0)  ch--;
          else {
             ch=MAXMSIZE-1;
             maxframe=MAXMSIZE;
             minframe=maxframe-MAXVOPT;
             if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
          }
          if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;}
       }
   newbob:   
       LCDNormal();
       if(bobross) {
           for (i=minframe; i<maxframe; ++i) {
              myGLCD.print(astr[ch], 0, (i+1-minframe)*10);
           }               
           bobross=0;
       }   
       else
       {
          myGLCD.print(astr[lch], 0, (lch+1-minframe)*10);
       }
       LCDRedBlack();
       if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10);
       else myGLCD.print(last, 0, (MAXVOPT+1)*10);       
       
       LCDInvers();
       myGLCD.print(astr[ch], 0, (ch+1-minframe)*10);
       LCDNormal();
    }   

    if ( (btn==PIN_RI) || (btn==PIN_LE) ) {                 // change input buffer value
       
       if (ch < 4) {                                        // toggle through sub menu options       
          if (btn==PIN_RI) val=toggleup(lval, hval, val);
          if (btn==PIN_LE) val=toggledn(lval, hval, val);
          sprintf(numbuf, "%3d", val);
          strcpy(opbuf, astr[ch]);
          strinsert(opbuf, numbuf, LSIZE-3);
          LCDInvers();
          myGLCD.print(opbuf, 0, (ch+1-minframe)*10);       // write line buffer = serial number + option
          LCDNormal();
       }
    }

  } while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;

  lcdcls();
  if (btn==PIN_ESC) { return -1; }

  if (ch==0) { ;}  // File load  ->  val
  else
  if (ch==1) { ;}  // File safe  ->  val
  else
  if (ch==2) { displayMatrix(val) ;}  // show patt. ->  val
  else
  if (ch==3) { ;}  // erase patt.->  val
 
  return val+(ch*10000);      // return number of suboption + 10000* option
 
}


//=====================================================================================
// menu: set target outputs manually
//=====================================================================================
int8_t  menu_setouttargets(char caption[]) {     // set targets
  const   int16_t MAXMSIZE = NMAXOUT;            // number of maximum useable menu options
  const   char LSIZE=17;                         // string length of line for option inscriptions

  char    astr[MAXMSIZE+1][LSIZE+1],             // all options by all inscription string
          opbuf[LSIZE+1],                        // option buffer
          buf[LSIZE+5] ="                     ", // line buffer = serial number + option inscription
          lbuf[LSIZE+5]="---------------------", // end of line buffer (-> last element)
          more[LSIZE+5]="+ + + + + + + + + + +", // next line buffer (-> more elements)
          bobross=0;                             // if to paint a beautiful new menu by beautiful colors ;)


  int8_t  btn, i, lch, MAXVOPT=10;
  int8_t  ch=0, maxframe=MAXVOPT, minframe=1;      // static for re-entering the menu;


  memcpy(outbuf, currOut, sizeof(outbuf) );
  for (i=1; i<=MAXMSIZE; ++i) {                   // initialize all lines in inscription array
     sprintf(opbuf, "Output%3d= %.0f    ", i, outbuf[i] );
     strcpy(astr[i], opbuf );                     // inscription = option + outbuf value
  }

  if (ch<1) ch=1;
  if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }

  lcdcls();
  LCDWhiteRed();
  myGLCD.print(buf, 0, 0);
  myGLCD.print(caption, (20-strlen(caption))*8/2, 0);

  LCDNormal();
  for (i=minframe; i<=maxframe; ++i) {
     sprintf(buf, "%3d %s", i, astr[i]);        // build line buffer = serial number + inscription
     myGLCD.print(buf, 0, (i+1-minframe)*10);
  }
  LCDRedBlack();
  if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (i+1-minframe)*10);
  else myGLCD.print(lbuf, 0, (i+1-minframe)*10);

  LCDInvers();
  sprintf(buf, "%3d %s", ch, astr[ch]);
  myGLCD.print(buf, 0, (ch+1-minframe)*10);
  LCDNormal();

  do {
    lch=ch;
    btn=getbtn();
    if ( (btn==PIN_DN ) ||  (btn==PIN_UP ) ) {                   // btn up/dn: dec/inc current option
      if (btn==PIN_DN ) {
          if(ch < MAXMSIZE)  ch++; 
          else {
             ch=1;
             minframe=1;
             maxframe=MAXVOPT;
             if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
          }
          if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;}   // adjust visible frame
       }
       else
       if (btn==PIN_UP ) {
          if (ch > 1)  ch--; 
          else {
             ch=MAXMSIZE;
             maxframe=MAXMSIZE;
             minframe=maxframe-MAXVOPT+1;
             if ( minframe != 1) { bobross=1; goto newbob; }
          }
          if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;}    // adjust visible frame
       }
   newbob:                                      // if frame has changed: move frame, paint anew
       LCDNormal();
       if(bobross) {
           for (i=minframe; i<=maxframe; ++i) {
              sprintf(buf, "%3d %s", i, astr[i]);
              myGLCD.print(buf, 0, (i+1-minframe)*10);  // write all visible complete line buffers
           }
           bobross=0;
       }
       else
       {
          sprintf(buf, "%3d %s", lch, astr[lch]);
          myGLCD.print(buf, 0, (lch+1-minframe)*10);    // write old line buffer (normal)
       }
       LCDRedBlack();
       if(maxframe<MAXMSIZE) {                          // write line for "more elements"
          myGLCD.print(more, 0, (i+1-minframe)*10);
       }
       else myGLCD.print(lbuf, 0, (i+1-minframe)*10);   // write line "last element"

       LCDInvers();
       sprintf(buf, "%3d %s", ch, astr[ch]);
       myGLCD.print(buf, 0, (ch+1-minframe)*10);        // write current line buffer (invers)
       LCDNormal();
    }
    if ( (btn==PIN_RI) || (btn==PIN_LE) ) {             // change input buffer value
       if (btn==PIN_RI) outbuf[ch]=1;
       if (btn==PIN_LE) outbuf[ch]=0;
       sprintf(opbuf, "Output%3d= %.0f    ", ch, outbuf[ch] );
       strcpy(astr[ch], opbuf );
       sprintf(buf, "%3d %s", ch, astr[ch]);            // update inscription: option + outbuf value
       LCDInvers();
       myGLCD.print(buf, 0, (ch+1-minframe)*10);        // write line buffer = serial number + inscription
       LCDNormal();
    }

  } while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;


  if (btn==PIN_ESC) { lcdcls();  return -1; }
 

  lcdcls();
  return ch;
}

     


//=====================================================================================
// menu: set inputs manually
//=====================================================================================
int16_t  menu_setinpattern(char caption[]) {      // set inputs             
  const   int16_t MAXMSIZE = NMAXIN;             // number of maximum useable menu options
  const   char LSIZE=17;                         // string length of line for option inscriptions
 
  char    astr[MAXMSIZE+1][LSIZE+1],             // all options by all inscription string
          opbuf[LSIZE+1],                        // option buffer         
          buf[LSIZE+5] ="                     ", // line buffer = serial number + option inscription
          lbuf[LSIZE+5]="---------------------", // end of line buffer (-> last element)
          more[LSIZE+5]="+ + + + + + + + + + +", // next line buffer (-> more elements)         
          bobross=0;                             // if to paint a beautiful new menu by beautiful colors ;)
 
         
  int16_t  btn, i, lch, MAXVOPT=10;
  static   int16_t  ch=0, maxframe=MAXVOPT, minframe=1;      // static for re-entering the menu;
   
  strcpy(astr[0], "static  0" );
  for (i=1; i<=MAXMSIZE; ++i) {                   // initialize all lines in inscription array
     sprintf(opbuf, "Input%3d = %.0f    ", i, inbuf[i] );
     strcpy(astr[i], opbuf );                     // inscription = option + inbuf value
  }

  if (ch<1) ch=1;
  if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }
 
  lcdcls();
  LCDWhiteRed();
  myGLCD.print(buf, 0, 0);             
  myGLCD.print(caption, (20-strlen(caption))*8/2, 0);
 
  LCDNormal();
  for (i=minframe; i<=maxframe; ++i) {
     sprintf(buf, "%3d %s", i, astr[i]);        // build line buffer = serial number + inscription
     myGLCD.print(buf, 0, (i+1-minframe)*10);
  }     
  LCDRedBlack();
  if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10 );
  else myGLCD.print(lbuf, 0, (MAXVOPT+1)*10 );
 
  LCDInvers();
  sprintf(buf, "%3d %s", ch, astr[ch]);
  myGLCD.print(buf, 0, (ch+1-minframe)*10);
  LCDNormal();

  do {
    lch=ch;
    btn=getbtn();       
    if ( (btn==PIN_DN ) ||  (btn==PIN_UP ) ) {                   // btn up/dn: dec/inc current option
      if (btn==PIN_DN ) {
          if(ch < MAXMSIZE)  ch++;
          else {
             ch=1;
             minframe=1;
             maxframe=MAXVOPT;
             if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
          }
          if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;}   // adjust visible frame
       }
       else
       if (btn==PIN_UP ) {
          if (ch > 1)  ch--;
          else {
             ch=MAXMSIZE;
             maxframe=MAXMSIZE;
             minframe=maxframe-MAXVOPT+1;
             if ( minframe != 1) { bobross=1; goto newbob; }
          }
          if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;}    // adjust visible frame
       }
   newbob:                                      // if frame has changed: move frame, paint anew
       LCDNormal();
       if(bobross) {
           for (i=minframe; i<=maxframe; ++i) {
              sprintf(buf, "%3d %s", i, astr[i]);
              myGLCD.print(buf, 0, (i+1-minframe)*10);  // write all visible complete line buffers
           }               
           bobross=0;
       }   
       else
       {
          sprintf(buf, "%3d %s", lch, astr[lch]);
          myGLCD.print(buf, 0, (lch+1-minframe)*10);    // write old line buffer (normal)
       }
       LCDRedBlack();
       if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10 );
       else myGLCD.print(lbuf, 0, (MAXVOPT+1)*10 );
       
       LCDInvers();
       sprintf(buf, "%3d %s", ch, astr[ch]);
       myGLCD.print(buf, 0, (ch+1-minframe)*10);        // write current line buffer (invers)
       LCDNormal();
    }   
    if ( (btn==PIN_RI) || (btn==PIN_LE) ) {             // change input buffer value
       if (btn==PIN_RI) inbuf[ch]=1;
       if (btn==PIN_LE) inbuf[ch]=0;
       sprintf(opbuf, "Input%3d = %.0f    ", ch, inbuf[ch] );
       strcpy(astr[ch], opbuf );
       sprintf(buf, "%3d %s", ch, astr[ch]);            // update inscription: option + inbuf value         
       LCDInvers();
       myGLCD.print(buf, 0, (ch+1-minframe)*10);        // write line buffer = serial number + inscription
       LCDNormal();       
    }
   
  } while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;             

  lcdcls();
  if (btn==PIN_ESC) { return -1; }
  return ch;
}                                     


//=====================================================================================
//=====================================================================================




// end of file




//=====================================================================================
//=====================================================================================






ps,
die originale veröffentlichte BPN-Implementierung benutzt ein für C-Programmierer sehr ungewöhnliches Inkrement ab 1, nicht ab 0, wodurch alle Nuller-Array-Zellen verschwendet schienen. Zunächst wollte ich dies daher aus Speicherplatz-Gründen wieder auf die Nuller-Inkrementierung "re-sizen", entschied mich aber dann doch dagegen: denn die Nuller-Array-Position wird intern u.a. für sog. "Bias-Neurone" (trainierter Schwellenwert) verwendet, die auch in die Backpropagation mit eingehen. Für künftige Weiterentwicklungen habe ich es daher so gelassen wie in der Original-Implementierung.
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC, C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 30. Apr 2015 09:56

Hier ist die Zusatzbibliothek ardustdio.h für SD file Zugriff u.a.m. - sie ermöglicht fprintf(), fgets() und fscanf() zum File-Schreiben und Lesen sowie ftoa() für einfache double(float)-zu-string Konvertierungen (sind normalerweise in gängigen C-Standard-Libs enthalten, nicht aber bei Arduinos) - außerdem sind ein paar praktische, zusätzliche string-Funktionen enthalten (strinsert, substr), die mir in den Arduino-libs fehlten:

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8491&p=66429#p66429
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

tito
Schreibt ab und zu
Schreibt ab und zu
Beiträge: 42
Registriert: 13. Okt 2011 11:56

Re: NXC, C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon tito » 1. Mai 2015 06:33

hallo,
tolle Leistung, keine Frage! Für sehr einfache Aufgaben habe ich sogar schon einmal irgendwo ein Backpropagation Netz auf dem NXT gesehen. Außerdem verwenden wohl häufig Leute, die so etwas benutzen, erst einmal Daten-Logging von bereits gut funktionierenden Programmen (z.B. Linefollower), um dabei Raddrehung und Linien-Zentrierung in Korrelation zu setzen. Damit werden dann die Netze auf PCs gefüttert, und deren Trainings-Ergebnisse dann auf die NXTs zurückkopiert. Ich bin aber sehr gespannt, wie man diese Netze sinnvoll komplett autonom einsetzen kann, inklusive Training, wie du es machst.
(...) (edit: in Diskussions-Thread abgetrennt )

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC, C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 1. Mai 2015 08:44

hi,
danke für dein feedback! :)

(...) (edit: in Diskussions-Thread viewtopic.php?f=70&t=8653 abgetrennt )
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC, C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 30. Mai 2015 07:49

Stiichwörter für Suchmaschinen:
Neuronales Netz Neural Net Jordan Netz Jordan Net Backpropagation Netz Backpropagation Net
rückgekoppeltes Neuronales Netz Recurrent neural network

http://www.mindstormsforum.de/viewtopic.php?f=70&t=5601#p67272

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 30. Mai 2015 07:57

Zwischenstand:

Der obige Code fürs Neuronale 2-schichtige Backpropagation Netz (bzw. 3-schichtig, wenn man die Sensor-Schicht mitzählt) funktioniert jetzt einwandfrei mit dem Arduino Due, Inputs lassen sich über Taster und Analog-Sensoren kodieren, und alle Eingabemuster lassen sich trainieren.
viewtopic.php?f=70&t=5601&p=67272#p67035
Das Netz ist auch in der Lage, einfache Vorhersage-Vermutungen zu treffen (und damit verbundene Reaktionen) für (noch) nicht trainierte Eingabezustände / ~muster.

Was ein Backpropagation Netz nicht besitzt, ist ein Erinnerungsvermögen, um Entscheidungen aus der Konstellation vorhergehender Situationen zu treffen.
Man stelle sich hierzu eine Sinuskurve vor und betrachte sie am Nulldurchgang:
Für den Fall dass die Null der augenblickliche Netzzustand an den Inputs wäre, kann man nicht vorraussagen, ob der folgende zu erwartende Zustand positiv oder negativ sein wird (auf Fahr-Roboter angewendet: ob man sich z.B. einem Hindernis nähert, oder sich von ihm entfernt, oder Stillstand herrscht): Dazu ist die Kenntnis des vorhegehenden Zustands also wichtig.
Am Beispiel der Sinuskurve bedeutet dies: war der Vorgänger größer, folgt nun ein negativer Wert, war er kleiner, dann ein positiver, war es vorher bereits Null, bleibt es wohl Null.
Auf den Fahrroboter angewendet bedeutet dies: Wenn er vorher weiter entfernt war vom Hindernis, muss er nun z.B. ausweichen, wenn er vorher näher dran war, entfernt er sich nun und muss nicht ausweichen.
Genau diesen vorhergehenden Zustand muss das Netz also kennen.

Dieses Problem kann eine weitere Neuron-Schicht lösen, die den letzten Zustand speichert und ihn bei der Neuberechnung des nächsten Zustands (als zusätzliche Eingänge) mit einberechnet: hier liegt dann also eine Rückkopplung vor, und dies wird mit Jordan- oder Elman-Netzen verwirklicht.

Bild
http://de.wikipedia.org/wiki/Jordan-Netz
http://en.wikipedia.org/wiki/Recurrent_ ... n_networks

Da dieses Erinnerungsvermögen für intelligente autonome Roboter essentiell ist, werde ich die weitere Entwicklung auf der Basis von Jordan-Netzen machen.



nun als nächster Schritt also ein 3-schichtiges Neuronales Netz mit Rückkopplung!

Jordan Netz / Jordan net

es hat dieselbe Grundarchitektur wie das Backpropagation Netz, nur jetzt mit zusätzlichen Kontextneuronen in einer eigenen Schicht.
Auch der Trainingsmodus erfolgt wie bei der Backpropagation nach demselben Algorithmus,
wobei die Kontextneuronen ihren Input rückgekoppelt erhalten aus dem Output-Layer und dabei untrainiert bleiben
(und daher ist der Unterschied der beiden Netze auf den ersten Blick kaum zu entdecken - allerdings wird jetzt langsam der RAM-Speicher knapp...).

Ab hier jetzt immer der aktuelle Stand der Bearbeitung:
Due_JordanNet.jpg
IR sensors are from A0-A8 (...A11),
switches and buttons are from in between D2-D13.
Each motor has 5 wires, 2 for encoder, 2 for direction, and 1 for pwm (enable).
Display is D49-D52,
SD is by SPI (DUE: D74-D76).

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 30. Mai 2015 18:51

Sourcecode inzwischen zu groß für den Forumseditor, daher File als Anhang!
(lib für stdio und strings s. viewtopic.php?f=78&t=8491&p=66429#p66429

Hier immer die zuletzt aktualisierte Version!


I proudly present:

the world-wide first recurrent neural multilayer net, running autonomously on an Arduino
(providing autonomous processing, and autonomous training!) :programmer:

=> in Arduino IDE oder in C-Editor zur Ansicht laden!
Dateianhänge
JORN0106.ino
hochgeladen: 5.12.2015
(111.03 KiB) 169-mal heruntergeladen
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

TheCritter
Schreibt ab und zu
Schreibt ab und zu
Beiträge: 25
Registriert: 24. Jun 2015 08:29

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon TheCritter » 8. Jul 2015 11:16

Oh ein Update dieser Seite, super :) :prima:
Ein fahrfähiges Mobil hast du noch nicht? Das sind sozusagen alles "Trockenübungen"?

Ich habe mir aus China so ein Auto Chassis mit 4 Motoren kommen lassen und suche nach geeigneter Software. Jetzt müsste nur noch die Motorsteuerung kommen. Einen Due und ein paar Sensoren, Akkus und Akkuhalterungen habe ich bereits.

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 8. Jul 2015 11:57

es ist nicht zwingend für ein "fahrfähiges Mobil" gedacht, sondern generell als lernfähiges Netz, das u.a. auch Encoder-Motoren als outputs und als Inputs steuern kann. Überhaupt ein mehrschichtiges Netz mit Rückkopplung autonom auf einer embedded-MCU zum Laufen zu kriegen, samt Menü-, Steuerungs-, Filezugriffs- und Trainings- samt Editiermöglichkeiten, war schon eine ziemliche Herausforderung (und es ist jetzt immerhin das weltweit erste Jordan-Netz, das auf einem Arduino läuft!) .
Aber auch ein Roboterarm ist damit grundsätzlich möglich, und auch andere Dinge, wie z.B. generelle Mustererkennung. Allerdings bräuchte man für leistungsfähige Anwendungen auch weit mehr Inputs, mehr Zwischenneurone, mehr Output-Neurone und viiiel mehr trainierbare Patterns.
D.h., für größere, leistungsfähige Anwendungen bräuchte ich also eine leistungsfähigere cpu-Basis mit weit mehr RAM, wie ich schon oben geschrieben habe:
Die Sache auf dem Due hat leider auch eine begrenzte Prognose, nicht nur wegen des jetzt schon zu knappen Speichers - man braucht später zwingend pre-emptives Multitasking (wie bei NXC, leJOS und POSIX oder C11), damit die sehr lange dauernden Netz-Trainings- und Erkennungs-Vorgänge (laufen als eigenständige Tasks) keine anderen, zeitkritischen Tasks stören. Der Due hat aber nur kooperatives MT, damit blockiert das extrem rechenintensive neuronale Netz alle anderen notwendigen laufenden Arbeiten, wie z.B. die Fahr- und Navigations-Tasks (die auch wieder teilweise lange Rechenzeiten brauchen). Auch hat der Due für die notwendigen 7-8 Encodermotoren zu wenige Pins und zu wenig Speicher dafür in der Netzarchitektur.



(edit: in Diskussions-Thread viewtopic.php?f=70&t=8653 abgetrennt )
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

TheCritter
Schreibt ab und zu
Schreibt ab und zu
Beiträge: 25
Registriert: 24. Jun 2015 08:29

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon TheCritter » 10. Jul 2015 09:24

Ok, hab die Pins jetzt so umgemapped bekommen das das LCD wieder funktioniert. Hab erst mal nur 2 Motoren genommen

Ich verstehe aber deine Motorsteuerung nicht ganz. Du hast pro Motor 5 Pins. Anzeigen tust du aber nur 3 pro Motor. Auch haben diese Motorcontroller 3 Eingangspins. Wie kommt es zu dem Unterschied?

Benutzeravatar
HaWe
Administrator
Administrator
Beiträge: 5351
Registriert: 11. Jan 2006 21:01
Wohnort: ein kleiner Planet in der Nähe von Beteigeuze

Re: NXC/C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon HaWe » 10. Jul 2015 13:36

stimmt, die Motoren brauchen je 5 Steuer-Pins, davon sind 3 Pins für:
2x dir + 1x pwm (enable) für die L293D-kompatible H-Bridge
plus 2 pins für die Encoder A+B
plus +3.3 bis 5V/Masse zur Spannungsversorgung der Drehencoder-IC
plus +5V/Masse für die H-Brücken-IC (ggf reichen auch 3.3V)
plus +12V/Masse für die Leistungsendstufe (bzw. 5-48V je nach Motoren und Motor-Leistungsendstufe).

Angezeigt werden in der letzten Version auf dem Display 4 Motore, es fehlt aber leider ausreichend RAM beim Due, um 4 Motoren vollständig zu steuern und auszulesen, daher bleibt es hier z.Zt. bei 2 Motoren, leider.
Auch mit den Sensoren sieht es ähnlich aus, wegen des zu geringen RAMs.
Der Rest der Netz-Architektur ist die Vorbereitung für den späteren Ausbau mit dem Tre o.ä., u.a. echte INT16-Werte aus ADC oder I2C- Quellen.

Wie die Motorsteuerung funktioniert, findest du hier:
viewtopic.php?f=70&t=8624&p=67243#p67243
Gruß,
HaWe
±·≠≈²³αβγδε∂ζλμνπξφωΔΦ≡ΠΣΨΩ∫√∀∃∈∉∧∨¬⊂⊄∩∪∅∞®
NXT NXC SCHACHROBOTER: https://www.youtube.com/watch?v=Cv-yzuebC7E

TheCritter
Schreibt ab und zu
Schreibt ab und zu
Beiträge: 25
Registriert: 24. Jun 2015 08:29

Re: NXC, C für NXT + Arduino: lernfähiges Neuronales Netz (NN, neural net)

Beitragvon TheCritter » 10. Jul 2015 15:21

Aha ok, jetzt verstehe ich den Encoder Motor. Wenn ich ein Fahrzeug bauen will was nur 2 Motoren hat, brauche ich da einen Encoder?
Bei Robotik, klar...
Kommt das Programm ohne diese Eingaben klar oder ist das unbedingt nötig damit das sinnvoll reagiert, also das Joran Net gefüttert wird?


Zurück zu „Projekte, Showcase, Bauanleitungen“

Wer ist online?

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

Lego Mindstorms EV3, NXT und RCX Forum : Haftungsauschluss