1

Тема: Алгоритм підвищення точності позиціонування

На підприємстві по виготовленню цегли я розробив контролер на ATmega8A , роботу двох енкодерів я під'єднав на два апаратні переривання INT0, INT1.
DAC зовнішній 12біт.
В цілому все працює нормально, але тепер з'явилося бажання підвищити роздільну здатність різки цегли.
Хто знає ефективні алгоритми реалізації ?
Приклад своєї реалізації та схему завтра сюди додам.
Зараз не з ПК пишу...

2

Re: Алгоритм підвищення точності позиціонування

Зазвичай із квадратурним енкодером працюють в режимі одного відліку на період (у ручних на мордах осцилографів/блоків живлення часто навіть деркач лише в одній позиції на період і фіксує).
Можна зробити до чотирьох відліків на період (ех, колись таке робив апаратною схемою перед реверсивними лічильниками К155ИЕ6). Треба рахувати усі перепади на обох лініях, а напрямок +/- залежно від рівня на іншій лінії. Тоді при похитуванні близько порогу однієї з ліній лічильник буде стрибати +1/-1, але накопичуватися похибка не буде.

Тобто "показуйте код, будемо думати".

До речі, таймер в STM32 може апаратно працювати з енкодером, але я не дивився, у якому режимі по роздільній здатності.
А платка BluePill з STM32F103C8 по ціні не дуже відрізняється від дрібних платок з AVR. Врахуйте на майбутнє.

3

Re: Алгоритм підвищення точності позиціонування

Доброго вечора. Проект цей доволі старий, тому реалізований був на Атмега8А. Проект передраний з другого контролера, який на тому підприємстві був встановлений. В квадратурного енкодера було використано тільки по одній ножці . Тобто даний контролер "не бачить" напрямку руху, і не бачить помилок енкодера при його поломці. Код виглядає так:

По одному каналу переривання ми зменшуємо амплітуду ЦАП:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here
  dp=1;
  if(step_dac)
  {
      if(step_dac>=step_dac_dn)
      {
       step_dac=step_dac-step_dac_dn;
      }
      else if(step_dac){ step_dac=0; }
  }
  if(step_dac<0)step_dac=0;
  timer_cnt=0; timer_res=0; // reset cnt data
}

По другому каналу переривання (другий енкодер) , ми збільшуємо амплітуду ЦАП:

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here
  dp1=1;
  if(step_dac<4095)step_dac=step_dac+step_dac_up;
  if(step_dac>4095)step_dac=4095;
}

ЦАП у нас 12 біт, тому відлік в діапазоні від 0 до 4095. Це дорівнює значенню напруги на виході вже ОУ (після підсилення): від 0 до 10В.
Ідея зміщення різки цегли заключається в корекції амплітуди ЦАП (множення на коефіцієнт, який програмується з меню). Такий алгоритм дозволяє регулювати товщину цегли з кроком 10мм. А хочеться зробити точніше....

Програмування коефіцієнта з меню:

if(menu==2)
    {        
       if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_up<999)step_dac_up++; }
        if(++timer_button>45){timer_button=42; if(step_dac_up<999)step_dac_up++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_up>1)step_dac_up--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_up>1)step_dac_up--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[23];
       led_out(step_dac_up);
    }
    else if(menu==1)
    {
      if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_dn<999)step_dac_dn++; }
        if(++timer_button>45){timer_button=42; if(step_dac_dn<999)step_dac_dn++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_dn>1)step_dac_dn--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_dn>1)step_dac_dn--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[13];
       led_out(step_dac_dn);
    }

Оновлення даних відбувається в головному циклі так:

if(step_dac!=step_dac_buff)
       {
        SPI_MCP4921(step_dac);
        step_dac_buff=step_dac;
        timer_ovf=390; 
       }

Код вивода даних в ЦАП:

// === SPI DAC ======//
#define CLK PORTB.2
#define DATA PORTB.3
#define CS PORTB.4

void SPI_MCP4921(unsigned int data) // DAC
{
 unsigned int mask=0b1000000000000000;
 char x=0; 
 
 data = data | 0x3000; 
  CS=0;
  delay_us(1); 
 for(x=0; x<16; x++)
 { 
   
   if(data&mask) DATA=1;
   else DATA=0;
   delay_us(1);
    CLK=1;
   delay_us(1); 
    CLK=0; 
   delay_us(1);  
     mask>>=1;
 }
   delay_us(1); 
   CS=1;
}

Принципова схема пристроя:

https://drive.google.com/file/d/1S9SVnz … sp=sharing

4

Re: Алгоритм підвищення точності позиціонування

На контролері який був у них встановлений (який я скопіював, і зробив більш функціональний) використовувався МК ATtiny2313, який ще простіше від ATmega8A та мав обмежені можливості. Наприклад він не калібрувався взагалі з меню. Був налаштований на етапі прошивки.

5

Re: Алгоритм підвищення точності позиціонування

ReAl написав:

Зазвичай із квадратурним енкодером працюють в режимі одного відліку на період (у ручних на мордах осцилографів/блоків живлення часто навіть деркач лише в одній позиції на період і фіксує).
Можна зробити до чотирьох відліків на період (ех, колись таке робив апаратною схемою перед реверсивними лічильниками К155ИЕ6). Треба рахувати усі перепади на обох лініях, а напрямок +/- залежно від рівня на іншій лінії. Тоді при похитуванні близько порогу однієї з ліній лічильник буде стрибати +1/-1, але накопичуватися похибка не буде.

Тобто "показуйте код, будемо думати".

До речі, таймер в STM32 може апаратно працювати з енкодером, але я не дивився, у якому режимі по роздільній здатності.
А платка BluePill з STM32F103C8 по ціні не дуже відрізняється від дрібних платок з AVR. Врахуйте на майбутнє.

З STM32 доволі часто працюю. Але під Атмега зроблене залізо, і міняти не можна...
Які є ідеї підвищити роздільну здатність? Маючи таку схему і такий МК.

6

Re: Алгоритм підвищення точності позиціонування

Я погано знаюся на вбудованій розробці, але трохи розуміюся на асемблері та C.

interrupt [EXT_INT0] void ext_int0_isr(void)
{
    dp=1;

    step_dac -= step_dac_dn;
    if(step_dac<0)
        step_dac=0;

    timer_cnt=0; 
    timer_res=0; // reset cnt data
}

робить те ж саме, тільки значно простіше і навіть швидше, і читається легше.

interrupt [EXT_INT1] void ext_int1_isr(void)
{
    dp1=1;

    step_dac+=step_dac_up;
    if(step_dac>4095)
      step_dac=4095;
}

Те саме для другої процедури.
Якщо вас хвилює швидкодія, то порівняння на більше-менше - це "під капотом" віднімання без збереження результату. Тобто роблячи перевірку перед тим, як віднімати чи додавати, ви нічого не економите.

7

Re: Алгоритм підвищення точності позиціонування

artos5 написав:

Доброго вечора. Проект цей доволі старий, тому реалізований був на Атмега8А. Проект передраний з другого контролера, який на тому підприємстві був встановлений. В квадратурного енкодера було використано тільки по одній ножці . Тобто даний контролер "не бачить" напрямку руху, і не бачить помилок енкодера при його поломці. Код виглядає так:

По одному каналу переривання ми зменшуємо амплітуду ЦАП:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here
  dp=1;
  if(step_dac)
  {
      if(step_dac>=step_dac_dn)
      {
       step_dac=step_dac-step_dac_dn;
      }
      else if(step_dac){ step_dac=0; }
  }
  if(step_dac<0)step_dac=0;
  timer_cnt=0; timer_res=0; // reset cnt data
}

По другому каналу переривання (другий енкодер) , ми збільшуємо амплітуду ЦАП:

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here
  dp1=1;
  if(step_dac<4095)step_dac=step_dac+step_dac_up;
  if(step_dac>4095)step_dac=4095;
}

ЦАП у нас 12 біт, тому відлік в діапазоні від 0 до 4095. Це дорівнює значенню напруги на виході вже ОУ (після підсилення): від 0 до 10В.
Ідея зміщення різки цегли заключається в корекції амплітуди ЦАП (множення на коефіцієнт, який програмується з меню). Такий алгоритм дозволяє регулювати товщину цегли з кроком 10мм. А хочеться зробити точніше....

Програмування коефіцієнта з меню:

if(menu==2)
    {        
       if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_up<999)step_dac_up++; }
        if(++timer_button>45){timer_button=42; if(step_dac_up<999)step_dac_up++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_up>1)step_dac_up--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_up>1)step_dac_up--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[23];
       led_out(step_dac_up);
    }
    else if(menu==1)
    {
      if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_dn<999)step_dac_dn++; }
        if(++timer_button>45){timer_button=42; if(step_dac_dn<999)step_dac_dn++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_dn>1)step_dac_dn--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_dn>1)step_dac_dn--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[13];
       led_out(step_dac_dn);
    }

Оновлення даних відбувається в головному циклі так:

if(step_dac!=step_dac_buff)
       {
        SPI_MCP4921(step_dac);
        step_dac_buff=step_dac;
        timer_ovf=390; 
       }

Код вивода даних в ЦАП:

// === SPI DAC ======//
#define CLK PORTB.2
#define DATA PORTB.3
#define CS PORTB.4

void SPI_MCP4921(unsigned int data) // DAC
{
 unsigned int mask=0b1000000000000000;
 char x=0; 
 
 data = data | 0x3000; 
  CS=0;
  delay_us(1); 
 for(x=0; x<16; x++)
 { 
   
   if(data&mask) DATA=1;
   else DATA=0;
   delay_us(1);
    CLK=1;
   delay_us(1); 
    CLK=0; 
   delay_us(1);  
     mask>>=1;
 }
   delay_us(1); 
   CS=1;
}

Принципова схема пристроя:

https://drive.google.com/file/d/1S9SVnz … sp=sharing

Є повна схема чи опис роботи обладнання?

8

Re: Алгоритм підвищення точності позиціонування

А можете ще показати, як timer_ovf далі використовується?

9

Re: Алгоритм підвищення точності позиціонування

HetmanNet написав:
artos5 написав:

Доброго вечора. Проект цей доволі старий, тому реалізований був на Атмега8А. Проект передраний з другого контролера, який на тому підприємстві був встановлений. В квадратурного енкодера було використано тільки по одній ножці . Тобто даний контролер "не бачить" напрямку руху, і не бачить помилок енкодера при його поломці. Код виглядає так:

По одному каналу переривання ми зменшуємо амплітуду ЦАП:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here
  dp=1;
  if(step_dac)
  {
      if(step_dac>=step_dac_dn)
      {
       step_dac=step_dac-step_dac_dn;
      }
      else if(step_dac){ step_dac=0; }
  }
  if(step_dac<0)step_dac=0;
  timer_cnt=0; timer_res=0; // reset cnt data
}

По другому каналу переривання (другий енкодер) , ми збільшуємо амплітуду ЦАП:

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here
  dp1=1;
  if(step_dac<4095)step_dac=step_dac+step_dac_up;
  if(step_dac>4095)step_dac=4095;
}

ЦАП у нас 12 біт, тому відлік в діапазоні від 0 до 4095. Це дорівнює значенню напруги на виході вже ОУ (після підсилення): від 0 до 10В.
Ідея зміщення різки цегли заключається в корекції амплітуди ЦАП (множення на коефіцієнт, який програмується з меню). Такий алгоритм дозволяє регулювати товщину цегли з кроком 10мм. А хочеться зробити точніше....

Програмування коефіцієнта з меню:

if(menu==2)
    {        
       if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_up<999)step_dac_up++; }
        if(++timer_button>45){timer_button=42; if(step_dac_up<999)step_dac_up++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_up>1)step_dac_up--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_up>1)step_dac_up--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[23];
       led_out(step_dac_up);
    }
    else if(menu==1)
    {
      if(PLUS==0 && MINUS)
       {
        if(timer_button==0){if(step_dac_dn<999)step_dac_dn++; }
        if(++timer_button>45){timer_button=42; if(step_dac_dn<999)step_dac_dn++;}
        flags_butt=1;
       }
       else if(MINUS==0 && PLUS) 
       {
        if(timer_button==0){if(step_dac_dn>1)step_dac_dn--; }
        if(++timer_button>45){ timer_button=42; if(step_dac_dn>1)step_dac_dn--; } 
        flags_butt=1;
       }
       led_buff[3]=led_table[13];
       led_out(step_dac_dn);
    }

Оновлення даних відбувається в головному циклі так:

if(step_dac!=step_dac_buff)
       {
        SPI_MCP4921(step_dac);
        step_dac_buff=step_dac;
        timer_ovf=390; 
       }

Код вивода даних в ЦАП:

// === SPI DAC ======//
#define CLK PORTB.2
#define DATA PORTB.3
#define CS PORTB.4

void SPI_MCP4921(unsigned int data) // DAC
{
 unsigned int mask=0b1000000000000000;
 char x=0; 
 
 data = data | 0x3000; 
  CS=0;
  delay_us(1); 
 for(x=0; x<16; x++)
 { 
   
   if(data&mask) DATA=1;
   else DATA=0;
   delay_us(1);
    CLK=1;
   delay_us(1); 
    CLK=0; 
   delay_us(1);  
     mask>>=1;
 }
   delay_us(1); 
   CS=1;
}

Принципова схема пристроя:

https://drive.google.com/file/d/1S9SVnz … sp=sharing

Є повна схема чи опис роботи обладнання?

Принцип роботи простий:
1) переміщається глиняний пласт по стрічці.
2) прямо на глиняний пласт встановлений ролик, який механічно передає оберти на енкодер
3) ці оберти передаються на ЦАП у вигляді цифрового коду амплітуди, а ЦАП в свою чергу передає сигнал на частотний перетворювач від 0 до 10В.
4) Частотний перетворювач приводить в дію колесо з проволокою, яка відрізає на шматки цей глиняний пласт.
5) Другий енкодер якраз виконує зворотній зв'язок сигналу про положення колеса яке відрізає пласт.

Моє рішення заключається в тому, щоб один енкодер програмно додавав амплітуду на виході ЦАП, а другий енкодер віднімав амплітуду на виході ЦАП. Так програма запрацювала найкраще. І тепер завдання, як роздільну здатність можна покращити? Без зміни роздільної здатності ЦАП?

10

Re: Алгоритм підвищення точності позиціонування

koala написав:

А можете ще показати, як timer_ovf далі використовується?

Там використовується сугубо обробка динамічної індикації. Алгоритм обробки енкодерів або ЦАП не відбувається в тій процедурі.
Код індикації:

// Timer2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void) // перерив. дисплея
{
// Reinitialize Timer2 value
TCNT2=250;
disp_shou();
}

Функція disp_shou();

void disp_shou()
{
  if (jamp2==0)
     { 
       chatode_off();
       
       switch (y)
          { 
           case 0:
            
            data_to_seg_chatode(led_buff[0] , 1);
            
            break;
            case 1:
            
            data_to_seg_chatode(led_buff[1] , 2);

            break;
            case 2:
              
            data_to_seg_chatode(led_buff[2] , 3);
            
            break;
            case 3:
              
            data_to_seg_chatode(led_buff[3] , 4);
            
            break;
            default:
           
          }
          
     } else       // заг. анод
     {   
         anode_off(); // викл  
         
         switch (y)
          { 
           case 0:
            
            data_to_seg_anode(led_buff[0] , 1);
            
           break;
           case 1:
            
            data_to_seg_anode(led_buff[1] , 2);
            
            break;
            case 2:
              
            data_to_seg_anode(led_buff[2] , 3);
            
            break;
            case 3:
              
            data_to_seg_anode(led_buff[3] , 4);
            
            break;
            default:
           
          }
         
     } 
     
      if (++y>3)
      {
        y=0; 
      }
}
Подякували: koala1

11

Re: Алгоритм підвищення точності позиціонування

HetmanNet написав:

Є повна схема чи опис роботи обладнання?

Схема є за посиланням на гугл диск. Тут я так і не зрозумів як викласти фото схеми...

12

Re: Алгоритм підвищення точності позиціонування

koala написав:

Я погано знаюся на вбудованій розробці, але трохи розуміюся на асемблері та C.

interrupt [EXT_INT0] void ext_int0_isr(void)
{
    dp=1;

    step_dac -= step_dac_dn;
    if(step_dac<0)
        step_dac=0;

    timer_cnt=0; 
    timer_res=0; // reset cnt data
}

робить те ж саме, тільки значно простіше і навіть швидше, і читається легше.

interrupt [EXT_INT1] void ext_int1_isr(void)
{
    dp1=1;

    step_dac+=step_dac_up;
    if(step_dac>4095)
      step_dac=4095;
}

Те саме для другої процедури.
Якщо вас хвилює швидкодія, то порівняння на більше-менше - це "під капотом" віднімання без збереження результату. Тобто роблячи перевірку перед тим, як віднімати чи додавати, ви нічого не економите.

Дякую за зауваження та рекомендації. Згоден з Вами що можна викинути одну умову if() так як це гірше не зробить програмі, а швидкість підвищиться :)
Тут декілька мкс ролі не зіграють ;)
Значно важливіша реалізація підвищення роздільної здатності різки цегли.

13 Востаннє редагувалося HetmanNet (04.10.2023 22:11:19)

Re: Алгоритм підвищення точності позиціонування

artos5 написав:
HetmanNet написав:

Є повна схема чи опис роботи обладнання?

Схема є за посиланням на гугл диск. Тут я так і не зрозумів як викласти фото схеми...

По ній незрозуміло як працює обладнання, лише як конкретний пристрій який є складовою обладнання.

14

Re: Алгоритм підвищення точності позиціонування

HetmanNet написав:
artos5 написав:
HetmanNet написав:

Є повна схема чи опис роботи обладнання?

Схема є за посиланням на гугл диск. Тут я так і не зрозумів як викласти фото схеми...

По ній незрозуміло як працює обладнання, лише як конкретний пристрій який є складовою обладнання.

Зараз завантажу на хмарне сховище відео, та закину сюди посилання. Там відео як саме працює ця лінія.
Якщо буде не зрозуміло, то розпишу більш детально як воно працює, по крокам.
Додаю посилання на відео роботи лінії:
https://drive.google.com/file/d/1LPXv4Q … drive_link

15

Re: Алгоритм підвищення точності позиціонування

Плата контролера
https://drive.google.com/file/d/1gbQ9YDLWpAkknOc2qEj5bGdSgrQ924d3/view?usp=sharing

16

Re: Алгоритм підвищення точності позиціонування

void disp_shou()
{
    if (jamp2==0)  {
        chatode_off();
        data_to_seg_chatode(led_buff[y] , y+1);
    }
    else {               // заг. анод
        anode_off(); // викл  
        data_to_seg_anode(led_buff[y] , y+1);
    }     
    if (++y>3) {
        y=0; 
    }
}

Перепрошую, не втримався.

17

Re: Алгоритм підвищення точності позиціонування

І на скільки швидше працюватиме замість оператора switch()? Якщо не брати до уваги що ваш код має гарну читаємість.

18

Re: Алгоритм підвищення точності позиціонування

artos5 написав:

І на скільки швидше працюватиме замість оператора switch()? Якщо не брати до уваги що ваш код має гарну читаємість.

Тут треба тестувати. Оптимізатори іноді роблять дива з кодом. Принципово - те саме O(n).

19

Re: Алгоритм підвищення точності позиціонування

Якщо вірити https://godbolt.org/z/8bjohT16T - то switch робить ланцюжок порівнянь, а мій код - лише одне. Зате мій код вимагає непрямої адресації, а це додаткові операції. У середньому мій код, наскільки я розумію, трохи швидший, але непринципово.

Подякували: artos51

20

Re: Алгоритм підвищення точності позиціонування

Я колись зіштовхувався з дивними речами.
В компіляторі для AVR серії мікроконтролерів, код вигляду: if(){}else if(){}
Мав менший розмір флеш ніж:
switch()
case 0:
break;
case 1:
break;

В динамічній індикації можна дуже підвищити продуктивність, якщо прибрати операції ділення з залишком, а замість цього задіяти операції віднімання та циклу.
Для 8біт мікроконтролерів  це значно підвищує продуктивність, так як у цього ядра немає апаратних операцій ділення та множення.