Цикл выполняется не до конца

alchemist92745
Offline
Зарегистрирован: 15.04.2020

Здравствуйте! Только начал знакомиться с программированием на ардуино. Нашел в интернете скетч для обучения нейронной сети на базе ардуино. Сейчас пытаюсь модифицировать под свои нужды. Столкнулся с такой проблемой: в функции get_data осуществляется заполнение массива данными для обучения, но цикл доходит только до 59 итерации, строка Serial.print("out_data");, которую я поместил для проверки выхода из цикла, конечно же не срабатывает. Не могу понять в чем проблема. За ранее извиняюсь, если этот ужасный код кровоточит вам глаза)).

/******************************************************************
 * ArduinoANN - An artificial neural network for the Arduino
 * All basic settings can be controlled via the Network Configuration
 * section.
 * See robotics.hobbizine.com/arduinoann.html for details.
 ******************************************************************/

#include <math.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
/******************************************************************
 * Network Configuration - customized per network 
 ******************************************************************/


#define DEBUG


#define left_b 2
#define right_b 3

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

byte curr_menu=0;
byte menu_items=3;
byte prog_start = 0;







const byte PatternCount = 10;
const byte InputNodes = 7;
const byte HiddenNodes = 8;
const byte OutputNodes = 4;
const float LearningRate = 0.3;
const float Momentum = 0.9;
const float InitialWeightMax = 0.5;
const float Success = 0.0004;

const byte Input[PatternCount][InputNodes] = {
  { 1, 1, 1, 1, 1, 1, 0 },  // 0
  { 0, 1, 1, 0, 0, 0, 0 },  // 1
  { 1, 1, 0, 1, 1, 0, 1 },  // 2
  { 1, 1, 1, 1, 0, 0, 1 },  // 3
  { 0, 1, 1, 0, 0, 1, 1 },  // 4
  { 1, 0, 1, 1, 0, 1, 1 },  // 5
  { 0, 0, 1, 1, 1, 1, 1 },  // 6
  { 1, 1, 1, 0, 0, 0, 0 },  // 7 
  { 1, 1, 1, 1, 1, 1, 1 },  // 8
  { 1, 1, 1, 0, 0, 1, 1 }   // 9
}; 

const byte Target[PatternCount][OutputNodes] = {
  { 0, 0, 0, 0 },  
  { 0, 0, 0, 1 }, 
  { 0, 0, 1, 0 }, 
  { 0, 0, 1, 1 }, 
  { 0, 1, 0, 0 }, 
  { 0, 1, 0, 1 }, 
  { 0, 1, 1, 0 }, 
  { 0, 1, 1, 1 }, 
  { 1, 0, 0, 0 }, 
  { 1, 0, 0, 1 } 
};




/******************************************************************
 * End Network Configuration
 ******************************************************************/


int i, j, p, q, r;
int ReportEvery1000;
int RandomizedIndex[PatternCount];
long  TrainingCycle;
float Rando;
float Error;
float Accum;


float Hidden[HiddenNodes];
float Output[OutputNodes];
float HiddenWeights[InputNodes+1][HiddenNodes];
float OutputWeights[HiddenNodes+1][OutputNodes];
float HiddenDelta[HiddenNodes];
float OutputDelta[OutputNodes];
float ChangeHiddenWeights[InputNodes+1][HiddenNodes];
float ChangeOutputWeights[HiddenNodes+1][OutputNodes];

void setup(){




  //pinMode(A1, INPUT);
  //pinMode(A2, INPUT);
  //pinMode(A3, INPUT);
  //pinMode(A4, INPUT);


  
  Serial.begin(9600);
  


  pinMode(left_b, INPUT_PULLUP);
  pinMode(right_b, INPUT_PULLUP);
  // initialize the LCD
  lcd.begin();

  // Turn on the blacklight and print a message.
  lcd.backlight();


  //attachInterrupt(digitalPinToInterrupt(2), menu_change, LOW);
  //attachInterrupt(digitalPinToInterrupt(3), load_prog, LOW); 
  attachInterrupt(0, menu_change, FALLING);//0 is Dpin2
  attachInterrupt(1, load_prog, FALLING);//1 is Dpin3

  randomSeed(analogRead(3));
  ReportEvery1000 = 1;
  for( p = 0 ; p < PatternCount ; p++ ) 
  {    
  RandomizedIndex[p] = p ;
  }
  
}  
//const int val_num, const int data_size, float mass[100][4]
void loop(){
  const int val_num = 4; //количество снимаемых параметров
  const int data_size = 100; //размер массива сколько будем собирать данных
  float mass[data_size][val_num];
  do {
  menu_select();
  } while (prog_start==0);
  
  if ((curr_menu == 1) && (prog_start == 1))
  {
    lcd.clear();
    lcd.print(F("Getting data"));
    
    #ifdef DEBUG
    //val_num, data_size, mass
    Serial.println("Inn");
    get_data(val_num, data_size, mass);
    Serial.println("Out");

    for (int i=1; i<=100;i++)
  {
    for (int j=1; j<=4;j++){
    Serial.print("Mass[");
    Serial.print(i);
    Serial.print(",");
    Serial.print(j);
    Serial.print("] = ");
    Serial.print(mass[i][j]);
    Serial.println();
    }
    }
    #endif
    
    prog_start=0;
  }
    if ((curr_menu == 2) && (prog_start == 1))
  {
    lcd.clear();
    lcd.print(F("Training NN"));
    train_nn();
    prog_start=0;
  }
    if ((curr_menu == 3) && (prog_start == 1))
  {
    lcd.clear();
    lcd.print(F("Running Observer"));
    run_net();
    prog_start=0;
  }
  
}


//Левая кнопка листает меню, правая подтверждает выбранный пункт
void menu_change(void)
{
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 50)
  {
    int buttState = digitalRead(left_b);
    if (buttState == 0) {
      curr_menu++;
      if (curr_menu > menu_items) {
        curr_menu = 1;
      }
    }
    //Serial.print(F(" Curr_menu "));// Удалить потом
    //Serial.print(curr_menu);
    //Serial.print(F(" Prog_start "));
    //Serial.print(prog_start);
  }
  last_interrupt_time = interrupt_time;
}
void load_prog(){
  prog_start = 1;
  
  }

void menu_select() 
{  
    lcd.clear();
    switch (curr_menu) {
    case 1:
      lcd.clear();
      lcd.setCursor(6,0);
      lcd.print(F("Menu"));
      lcd.setCursor(0,1);
      lcd.print(F("Get data"));
      break;
    case 2:
      lcd.setCursor(6,0);
      lcd.print(F("Menu"));
      lcd.setCursor(0,1);
      lcd.print(F("Train NN"));
      break;
    case 3:
      lcd.setCursor(6,0);
      lcd.print(F("Menu"));
      lcd.setCursor(0,1);
      lcd.print(F("Run Observer"));
      break;
    default:
      break;
    }
    //prog_start=0;
}








void train_nn(){


/******************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Initialize HiddenWeights and ChangeHiddenWeights 
******************************************************************/

  for( i = 0 ; i < HiddenNodes ; i++ ) {    
    for( j = 0 ; j <= InputNodes ; j++ ) { 
      ChangeHiddenWeights[j][i] = 0.0 ;
      Rando = float(random(100))/100;
      HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
    }
  }
/******************************************************************
* Initialize OutputWeights and ChangeOutputWeights
******************************************************************/

  for( i = 0 ; i < OutputNodes ; i ++ ) {    
    for( j = 0 ; j <= HiddenNodes ; j++ ) {
      ChangeOutputWeights[j][i] = 0.0 ;  
      Rando = float(random(100))/100;        
      OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
    }
  }
  Serial.println(F("Initial/Untrained Outputs: "));
  toTerminal();
/******************************************************************+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Begin training 
******************************************************************/

  for( TrainingCycle = 1 ; TrainingCycle < 2147483647 ; TrainingCycle++) {    

/******************************************************************
* Randomize order of training patterns
******************************************************************/

    for( p = 0 ; p < PatternCount ; p++) {
      q = random(PatternCount);
      r = RandomizedIndex[p] ; 
      RandomizedIndex[p] = RandomizedIndex[q] ; 
      RandomizedIndex[q] = r ;
    }
    Error = 0.0 ;
/******************************************************************
* Cycle through each training pattern in the randomized order
******************************************************************/
    for( q = 0 ; q < PatternCount ; q++ ) {    
      p = RandomizedIndex[q];

/******************************************************************
* Compute hidden layer activations
******************************************************************/

      for( i = 0 ; i < HiddenNodes ; i++ ) {    
        Accum = HiddenWeights[InputNodes][i] ;
        for( j = 0 ; j < InputNodes ; j++ ) {
          Accum += Input[p][j] * HiddenWeights[j][i] ;
        }
        Hidden[i] = 1.0/(1.0 + exp(-Accum)) ;
      }

/******************************************************************
* Compute output layer activations and calculate errors
******************************************************************/

      for( i = 0 ; i < OutputNodes ; i++ ) {    
        Accum = OutputWeights[HiddenNodes][i] ;
        for( j = 0 ; j < HiddenNodes ; j++ ) {
          Accum += Hidden[j] * OutputWeights[j][i] ;
        }
        Output[i] = 1.0/(1.0 + exp(-Accum)) ;   
        OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;   
        Error += 0.5 * (Target[p][i] - Output[i]) * (Target[p][i] - Output[i]) ;
      }

/******************************************************************
* Backpropagate errors to hidden layer
******************************************************************/

      for( i = 0 ; i < HiddenNodes ; i++ ) {    
        Accum = 0.0 ;
        for( j = 0 ; j < OutputNodes ; j++ ) {
          Accum += OutputWeights[i][j] * OutputDelta[j] ;
        }
        HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;
      }


/******************************************************************
* Update Inner-->Hidden Weights
******************************************************************/


      for( i = 0 ; i < HiddenNodes ; i++ ) {     
        ChangeHiddenWeights[InputNodes][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[InputNodes][i] ;
        HiddenWeights[InputNodes][i] += ChangeHiddenWeights[InputNodes][i] ;
        for( j = 0 ; j < InputNodes ; j++ ) { 
          ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
          HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;
        }
      }

/******************************************************************
* Update Hidden-->Output Weights
******************************************************************/

      for( i = 0 ; i < OutputNodes ; i ++ ) {    
        ChangeOutputWeights[HiddenNodes][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[HiddenNodes][i] ;
        OutputWeights[HiddenNodes][i] += ChangeOutputWeights[HiddenNodes][i] ;
        for( j = 0 ; j < HiddenNodes ; j++ ) {
          ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
          OutputWeights[j][i] += ChangeOutputWeights[j][i] ;
        }
      }
    }

/******************************************************************
* Every 1000 cycles send data to terminal for display
******************************************************************/
    ReportEvery1000 = ReportEvery1000 - 1;
    if (ReportEvery1000 == 0)
    {
      Serial.println(); 
      Serial.println(); 
      Serial.print (F("TrainingCycle: "));
      Serial.print (TrainingCycle);
      Serial.print (F("  Error = "));
      Serial.println (Error, 5);

      toTerminal();

      if (TrainingCycle==1)
      {
        ReportEvery1000 = 999;
      }
      else
      {
        ReportEvery1000 = 1000;
      }
    }    


/******************************************************************
* If error rate is less than pre-determined threshold then end
******************************************************************/

    if( Error < Success ) break ;  
  }
  Serial.println ();
  Serial.println(); 
  Serial.print (F("TrainingCycle: "));
  Serial.print (TrainingCycle);
  Serial.print (F("  Error = "));
  Serial.println (Error, 5);

  toTerminal();

  Serial.println ();  
  Serial.println ();
  Serial.println (F("Training Set Solved! "));
  Serial.println (F("--------")); 
  Serial.println ();
  Serial.println ();  
  ReportEvery1000 = 1;
}

void toTerminal()
{

  for( p = 0 ; p < PatternCount ; p++ ) { 
    Serial.println(); 
    Serial.print (F("  Training Pattern: "));
    Serial.println (p);      
    Serial.print (F("  Input "));
    for( i = 0 ; i < InputNodes ; i++ ) {
      Serial.print (Input[p][i], DEC);
      Serial.print (F(" "));
    }
    Serial.print (F("  Target "));
    for( i = 0 ; i < OutputNodes ; i++ ) {
      Serial.print (Target[p][i], DEC);
      Serial.print (F(" "));
    }
/******************************************************************
* Compute hidden layer activations
******************************************************************/

    for( i = 0 ; i < HiddenNodes ; i++ ) {    
      Accum = HiddenWeights[InputNodes][i] ;
      for( j = 0 ; j < InputNodes ; j++ ) {
        Accum += Input[p][j] * HiddenWeights[j][i];
      }
      Hidden[i] = 1.0/(1.0 + exp(-Accum)) ;
    }

/******************************************************************
* Compute output layer activations and calculate errors
******************************************************************/

    for( i = 0 ; i < OutputNodes ; i++ ) {    
      Accum = OutputWeights[HiddenNodes][i] ;
      for( j = 0 ; j < HiddenNodes ; j++ ) {
        Accum += Hidden[j] * OutputWeights[j][i];
      }
      Output[i] = 1.0/(1.0 + exp(-Accum)) ; 
    }
    Serial.print (F("  Output "));
    for( i = 0 ; i < OutputNodes ; i++ ) {       
      Serial.print (Output[i], 5);
      Serial.print (" ");
    }
  }

}

//const int val_num, const int data_size, float mass[100][4]
////////////////////////////////GET_DATA Изменить ихсходя из требований////////////////////////////
void get_data(const int val_num, const int data_size, float mass[100][4]){
  Serial.print(F("Inn_data"));
  

  //int Sensor=analogRead(A0);
  //Serial.println(Sensor);
  for (int i=1; i<=data_size; i++)
  {
      Serial.println(i);
      delay(1000);
      mass[i][1]={analogRead(A0)};
      mass[i][2]={analogRead(A0)+0.2};
      mass[i][3]={analogRead(A0)+0.3};
      mass[i][4]={analogRead(A0)+0.4};
  }
  Serial.print("out_data");
  }
void run_net(){
  
  }

void prepare_data(){

  }

 

alchemist92745
Offline
Зарегистрирован: 15.04.2020

Так же для проверки вынес этот участок кода в отдельный скетч. Там он работает корректно:

#include <math.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <avr/pgmspace.h>

//float get_data(val_num, data_size, mass);

//const byte val_num = 4; //количество снимаемых параметров
//const byte data_size = 100; //размер массива сколько будем собирать данных
//float mass[data_size][val_num]={};

void setup() {
  Serial.begin(9600);

  //get_data(val_num, data_size, mass);

  
}

void loop() {
  const int val_num = 4; //количество снимаемых параметров
  const int data_size = 100; //размер массива сколько будем собирать данных
  float mass[data_size][val_num];
  get_data(val_num, data_size, mass);
  for (int i=1; i<=100;i++)
    {
    for (int j=1; j<=4;j++){
    Serial.print("Mass[");
    Serial.print(i);
    Serial.print(",");
    Serial.print(j);
    Serial.print("] = ");
    Serial.print(mass[i][j]);
    Serial.println();
    }
    }
    end_prog();

}
//
void get_data(const int val_num, const int data_size, float mass[100][4]){


  //int Sensor=analogRead(A0);
  //Serial.println(Sensor);
  for (int i=1; i<=data_size; i++)
  {
      delay(100);
      mass[i][1]={analogRead(A0)};
      mass[i][2]={analogRead(A0)*2};
      mass[i][3]={analogRead(A0)*3};
      mass[i][4]={analogRead(A0)*4};
  }
  }
void end_prog(){

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

наверное памяти не хватает

b707
Offline
Зарегистрирован: 26.05.2017

а Ардуина какая?

alchemist92745
Offline
Зарегистрирован: 15.04.2020

b707 пишет:

а Ардуина какая?

ардуино Uno. После компиляции скетча пишет, что занято 75% динамической памяти и пишет предупреждение, якобы может работать нестабильно. Сократил использование памяти до 74% предупреждение больше не пишет, проблема осталась та же.

b707
Offline
Зарегистрирован: 26.05.2017

Налицо крайне нерациональное использование памяти. Только в описании глобальных переменных  (строчки с 35 по 94) я уже насчитал порядка 1.5Кб оперативки.

 

а вот это вообще что?

void get_data(const int val_num, const int data_size, float mass[100][4]){

массив размером в 400 флоатов. описанный в параметрах? :)

Kakmyc
Offline
Зарегистрирован: 15.01.2018

У тебя только двухуровневый массив на 400 элементов типа float, съедает 1600 байт, а ещё дисплей 512байт и математика.
Напомню , ОЗУ у 328меги 2кб

alchemist92745
Offline
Зарегистрирован: 15.04.2020

b707 пишет:

Налицо крайне нерациональное использование памяти. Только в описании глобальных переменных  (строчки с 35 по 94) я уже насчитал порядка 1.5Кб оперативки.

 

а вот это вообще что?

void get_data(const int val_num, const int data_size, float mass[100][4]){

массив размером в 400 флоатов. описанный в параметрах? :)

 

Для моей задачи необходим большой массив данных, 100 на 4 это еще маленький. Я собираюсь заполнять его по ходу выполнения программы и изменять его значения в других функциях. Насколько я понял в таком случае PROGMEM с ним мне не поможет. Как я могу освободить динамическую память в таком случае, если работу с большими массивами именно float не избежать? 

b707
Offline
Зарегистрирован: 26.05.2017

Kakmyc пишет:
У тебя только двухуровневый массив на 400 элементов типа float, съедает 1600 байт

сдается мне, что таких массивов у него два - один в процедуре loop(), другой - в get_data. По-моему это два разных массива, если не ошибаюсь.

Это не считая кучи массивов в начале кода.

Суммарно у него потребность в оперативке превышает обьем памяти Уно раза этак в три...

b707
Offline
Зарегистрирован: 26.05.2017

alchemist92745 пишет:

Как я могу освободить динамическую память в таком случае, если работу с большими массивами именно float не избежать? 

в вашем случае, думаю, никак. Уменьшить можно раза в два - но все равно в Уно не влезет. Берите мегу .

И крайне полезно было бы прочесть учебник по Си в плане описания переменных и передачи парметров в функции. А то вам и меги не хватит

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

что-то добрые вы сегодня, не дали студенту самому посчитать память )))

b707
Offline
Зарегистрирован: 26.05.2017

alchemist92745 пишет:

если работу с большими массивами именно float не избежать? 

это особенно забавно с учетом того, что "массив флоат" заполняется с помощью команды analogread().

Стоило бы эту ветку отправить вашим преподавателям, такое долбо..во нужно наказывать.

b707
Offline
Зарегистрирован: 26.05.2017

ua6em пишет:

что-то добрые вы сегодня, не дали студенту самому посчитать память )))

да лан. он такое дерево. что даже с подсказками не решит

alchemist92745
Offline
Зарегистрирован: 15.04.2020

b707 пишет:

alchemist92745 пишет:

если работу с большими массивами именно float не избежать? 

это особенно забавно с учетом того, что "массив флоат" заполняется с помощью команды analogread().

Стоило бы эту ветку отправить вашим преподавателям, такое долбо..во нужно наказывать.

 

Мне с преобразователя нужно будет снять несколько изменяющихся во времени параметров, сформировав тот самый массив float. Если я правильно понимаю то это делается с помощью analogread().

alchemist92745
Offline
Зарегистрирован: 15.04.2020

Почему так грубо? Я же написал в старт посте, что только начал ознакомление с ардуино.

b707
Offline
Зарегистрирован: 26.05.2017

alchemist92745 пишет:

Если я правильно понимаю ...

неправильно

b707
Offline
Зарегистрирован: 26.05.2017

Уважаемые админы. все же обьясните, кто втихую чистит ветки и по какому принципу?

sadman41
Offline
Зарегистрирован: 19.10.2016

Из ардуино.ру решили сделать бретборд.ру

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Придётся и тут блокироваться

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

alchemist92745 пишет:

Мне с преобразователя нужно будет снять несколько изменяющихся во времени параметров, сформировав тот самый массив float. Если я правильно понимаю то это делается с помощью analogread().

У Ардуино УНО 10-и разрядный АЦП. Из 10-и двоичных разрядов реально достоверны только старшие 8.

С АЦП читают в формате int. См.: http://arduino.ru/Reference/AnalogRead

alchemist92745
Offline
Зарегистрирован: 15.04.2020

Duino A.R. пишет:

alchemist92745 пишет:

Мне с преобразователя нужно будет снять несколько изменяющихся во времени параметров, сформировав тот самый массив float. Если я правильно понимаю то это делается с помощью analogread().

У Ардуино УНО 10-и разрядный АЦП. Из 10-и двоичных разрядов реально достоверны только старшие 8.

С АЦП читают в формате int. См.: http://arduino.ru/Reference/AnalogRead

 

Спасибо за разъяснения, это помогло решить проблему

b707
Offline
Зарегистрирован: 26.05.2017

alchemist92745 пишет:

Цитата:

С АЦП читают в формате int. См.: http://arduino.ru/Reference/AnalogRead

 

Спасибо за разъяснения, это помогло решить проблему

да ну? Два массива по 400 элементов, плюс куча массивов в начале. плюс буфер экрана - волшебно поместились в 2К оперативы? :)

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

alchemist92745 пишет:

Спасибо за разъяснения, это помогло решить проблему

Пожалуйста. Я позволю себе сделать ещё несколько замечаний и уточнений.

1. Я не знаю, что вынудило Вас использовать МК Ардуино УНО для задач ИИ с методами, по сути, относящимися к анализу "БигДата". На мой взгляд, это можно было сделать только от полного безисхода. Эти МК ориентированы совершенно на другой класс задач и обладают весьма ограниченными ресурсами.

2. Если Вы действительно взялись изучать Ардуино, "таки надо изучать". Если Вы собрались использовать какие-либо возможности аппаратуры и языка программирования, то следует плотно ознакомиться с их особенностями до попытки их применить. Пример с analogRead() очень "режет глаз".

3. Я не зря написал, что у АЦП Ардуино "честные" только 8 старших двоичных разрядов. Если Ваш процесс физический, и Вас устраивает реальная точность в 8 двоичных разрядов (а это лучше 0,5%, между прочим), то исходные данные вообще можно помещать в беззнаковые байтовые массивы. При их математической обработке, возможно, придется делать преобразования типов, чтобы избежать переполнений и не потерять точность, а результат потом снова приводить, для экономии памяти при хранении, к 8-и двоичным разрядам. Я Вашу программу не смотрел и, если честно, смотреть не буду. Здесь есть реальные специалисты  в области программирования вообще и программирования Ардуино, в частности. Прислушайтесь к их рекомендациям.