极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

楼主: ignore

关于电机中断计数问题

[复制链接]
 楼主| 发表于 2017-3-10 16:50:53 | 显示全部楼层
Super169 发表于 2017-3-10 15:11
單看你的程式, 有幾個複雜的問題, 互相影響了, 真的不太肯定你收集到的結果, 會是什麼.

首先, 可能 ...

将millis(),等等去除掉,问题一大堆,行程马上就乱套了,对于这种问题,是不是时间上所引起的?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-10 16:55:51 | 显示全部楼层
Super169 发表于 2017-3-10 16:00
那115rpm 應該沒問題吧 (一秒 2 圈? 真的很慢了).  5ms 的時間, 可因應不同的輸入而調教.
我之前用編碼 ...

没错,差不多一秒两圈,问题是这样的,正转如果A到B(100个脉冲),再从B到A,理论上也是100个脉冲,但是由于存在一个误差(不知道什么原因引起),就是先读到了100个脉冲(实际只走了99个),导致最终回到a。a与A差一个脉冲,所以问题就是怎么解决这一个脉冲,
回复 支持 反对

使用道具 举报

发表于 2017-3-10 17:24:16 | 显示全部楼层
本帖最后由 Super169 于 2017-3-10 17:25 编辑
ignore 发表于 2017-3-10 16:50
将millis(),等等去除掉,问题一大堆,行程马上就乱套了,对于这种问题,是不是时间上所引起的?


不是完全除去 millis(), 是要知道, millis() 會停止, 即在開始時的 millis, 經過多少時間, 數值都不變.
我自己也有做 5ms 甚至 20ms (手動轉盤) 的篩選.
其實你的做法也沒有錯, 只是如果不執行時, 應該不用更新 timer3.

void Count()
{  
       if(Start==1)
       {
           if((millis()-time3)>5)                    //为了不计入噪音干扰脉冲,
           {                                         //当2次中断之间的时间大于5ms时,计一次有效计数
               count += 1;                           //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,编码器码盘计数加一  
               Serial.print("count=");
               Serial.println(count);
               DirectionCompare();                   //行程比较控制停止
           }
           time3=millis();
       }
}


這個應該放在 if((millis()-time3)>5)  之內.  如果沒執行, 就不要更新了, 否則, 當真實輸出比較快時, 會有可能出現永不執行.


為了清楚用的是開始的時間, 可能一開始用 unsigned long thisTime = millis(); 把時間記下來, 會比較清楚.  
當然, 你一直用 millis 也是一樣的, 因為在 ISP 內 millis() 會停止.
只是因為放在 if ... 之後, 會有一個錯覺是執行完的時間, 而事實上只會時執行開始的時間.

另外, 這個 5 的值, 要因應你的設備而調校.   比如我用手動轉盤時, 如果用 CHANGE,要設定成 20 才準確 (反正手動也不可能太快).
回复 支持 反对

使用道具 举报

发表于 2017-3-10 17:31:10 | 显示全部楼层
ignore 发表于 2017-3-10 16:55
没错,差不多一秒两圈,问题是这样的,正转如果A到B(100个脉冲),再从B到A,理论上也是100个脉冲,但是 ...


先把問題分開, 中斷數目, 以及方向, 可以分開去看.
你有試過, 如果單獨計算中斷的數目 (就是在 ISP 內只做 count++, 然後在 loop 中當 count 有變成顯示 count) , 正向一圈, 再反向一圈, 用 FALLING / RISING / LOW 會是 40 還是 39?

如果是 40, 即是中斷沒問題, 是加入方向後出錯.

如果是 39, 就是中斷有問題, 你先正向一圈, 是否停在 20?  一開始反向時, 第一格會跳嗎?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-10 17:31:54 | 显示全部楼层
Super169 发表于 2017-3-10 17:24
不是完全除去 millis(), 是要知道, millis() 會停止, 即在開始時的 millis, 經過多少時間, 數值都不變. ...

这个程序比较早的,下面这段是我后面做循环测试用的,
#include <IRremote.h>
/*#include <EEPROM.h>

union data                                      //EEPROM 数据类型 字节拆分 转换
{
  long a;
  byte b[4];
};
data col;
*/
int RECV_PIN = 9;                               //红外脚
int Frelay=11;                                   // 继电器控制电机 启/停
int Drelay=12;                                   // 继电器控制电机 正/反转 通过H桥改变电源正/反
unsigned long time1 = 0, time2 = 0 ,time3 = 0 ,time4 = 0;
long num;                                       
long num1;                                       //每100ms 计数
long num2;                                       //每200ms 计数
int count=0;                                     //计数
int Start=0;
int Pause=0;
int Change=0;
int Direction=0;
int a=0;
int n;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
     Serial.begin(9600);
     pinMode(Frelay,OUTPUT);
     pinMode(Drelay,OUTPUT);
     pinMode(2,INPUT_PULLUP);
     attachInterrupt(0,Count,CHANGE);
   
     irrecv.enableIRIn();
}

void loop()
{
     Infrared();                                  //红外遥控  
     SpeedCompare();                              //速度比较
     MotorLoop();
     if(Start==1)
     {
           Serial.print("count=");
           Serial.println(count);
           DirectionCompare();                   //行程比较控制停止
      }
}

void Infrared()
{
     if (irrecv.decode(&results))
     {
         Serial.println(results.value, HEX);
         if(results.value==0xB012615A)            //正转按钮  A键
         {
              delay(50);
              if(results.value==0xB012615A)
              {
                  a=1;
                  time4=millis();
              }
         }
         if(results.value==0xB2CC429A)              //暂停键   C键
         {
              delay(50);
              if(results.value==0xB2CC429A)
              {
                   if(Pause==0)
                   {  
                       digitalWrite(Frelay,0);
                       Start=0;
                       Pause=1;
                   }else{
                       digitalWrite(Frelay,1);
                       Start=1;
                       Pause=0;
                   }
              }
         }
         irrecv.resume();
     }
}

void MotorLoop()
{
     if(a==1 && Direction==0)
     {
          if(millis()-time4>1000)
          {
              digitalWrite(Frelay,1);
              delay(6);//继电器延时
              Start=1;
              a=0;
              n++;
          }
          /*col.a=n;
          for(int addr=0 ; addr < 4 ; addr++)      //将count写入EEPROM
          {
               EEPROM.write(addr, col.b[addr]);
               delay(3);
           }*/
     }else if(a==1 && Direction==1)
     {
          if(millis()-time4>1000)
          {
              digitalWrite(Drelay,1);
              delay(6);
              digitalWrite(Frelay,1);
              delay(6);
              Start=1;
              a=0;
              n++;
          }
          /*col.a=n;
          for(int addr=0 ; addr < 4 ; addr++)      //将count写入EEPROM
          {
               EEPROM.write(addr, col.b[addr]);
               delay(3);
           }*/
     }
     Serial.print("n = ");
     Serial.println(n);        
}

void Count()
{  
      if(Start==1)
       {
           if((millis()-time3)>5)                    //为了不计入噪音干扰脉冲,
           {                                         //当2次中断之间的时间大于5ms时,计一次有效计数
               count += 1;                           //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,编码器码盘计数加一  
               time3=millis();
           }
           
       }
}

void SpeedCompare()
{
     if(Start==1)
     {  
         if ((millis()-time1>=100) && Change==0)
         {
             time1=millis();
             num1=count;
             Change=1;
         }
         if ((millis()-time2>=200) && Change==1)
         {
             time2=millis();
             num2=count;  
             num=num2-num1;
             Change=0;
         }
         if(num<10 && count>100)
         {
             Start=0;
             digitalWrite(Drelay,0);
             delay(4);
             digitalWrite(Frelay,0);
             delay(4);
             count=0;
             if(Direction==0)
             {
                 Direction=1;
                 a=1;
              }else{
                 Direction=0;
                 a=1;
              }
              time4=millis();
         }
     }
}

void DirectionCompare()
{
     if(800<count)                //全程 行程比较
     {
          Start=0;
          digitalWrite(Drelay,0);
          delay(4);
          digitalWrite(Frelay,0);
          delay(4);
          count=0;
          if(Direction==0)
          {
              Direction=1;
              a=1;
           }else{
              Direction=0;
              a=1;
          }
          time4=millis();
     }
}
回复 支持 反对

使用道具 举报

发表于 2017-3-10 17:56:50 | 显示全部楼层
本帖最后由 Super169 于 2017-3-10 17:59 编辑
ignore 发表于 2017-3-10 17:31
这个程序比较早的,下面这段是我后面做循环测试用的,
#include
/*#include


看來不是中斷的問題, 應該是程式中計算的問題了, 需要一點時間消化, 今晚回家後再看看.

另外有個小提議, 在 ISP中都會對 count 進行修改, 而 loop 之中會用到 count.  安全一點, 可以在設定上加上 volatile, 讓 compiler 知道它的值有可能會在別處更改的.   當然, 跟你現在的問題可能沒大關係, 只是使用 ISP 時比較安全的做法.


可能, 你原本在 ISP 中轉向, 會比 loop 中準確.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-11 08:27:54 | 显示全部楼层
Super169 发表于 2017-3-10 17:56
看來不是中斷的問題, 應該是程式中計算的問題了, 需要一點時間消化, 今晚回家後再看看.

另外有個小 ...

经过不断的测试发现,这个误差是有时间性的,意思就是在程序中的millis()没能算对,导致时间上的误差导致中断读书误差,不过,这要怎么配合?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-11 08:35:18 | 显示全部楼层
Super169 发表于 2017-3-10 17:31
先把問題分開, 中斷數目, 以及方向, 可以分開去看.
你有試過, 如果單獨計算中斷的數目 (就是在 ISP 內 ...

听你这么一说,我反应到,之前开这个贴的时候,想询问的问题就是,为什么同一道程序,我用CHANGE触发,跟用FALLING触发,行程一样,计数却相同,本应该是CHANGE比FALLING多一倍才对的,
回复 支持 反对

使用道具 举报

发表于 2017-3-11 10:03:12 | 显示全部楼层
的确有很多人建议不要在中断函数里用串口,

很高兴这里有对编码器深入讨论的贴子
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-11 13:14:31 | 显示全部楼层
wing 发表于 2017-3-11 10:03
的确有很多人建议不要在中断函数里用串口,

很高兴这里有对编码器深入讨论的贴子

综合来说,有这么种情况,电机存在一定毛刺的,淘宝的两款电机,说是一样的,结果,一款正反转基本一致,一款正反转差异在3rmp,程序上说,需要在中断计数,进行毛刺处理,还在研究中,,,
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-11 17:28:05 | 显示全部楼层
Super169 发表于 2017-3-10 17:56
看來不是中斷的問題, 應該是程式中計算的問題了, 需要一點時間消化, 今晚回家後再看看.

另外有個小 ...

从今天的测试来说,目前基本算是解决问题,其主要问题出现在与电机的毛刺上面,对于电机的毛集本上是在2ms左右的,不大不小,所以,要做的就是在中断所调用的函数上进行所谓的滤毛刺处理,分为软件消除和硬件消除(硬件需要分析毛刺产生源头,后续进行优化电路硬件等),再这边先奉上程序块吧,若有改进之处,尽管指出,需要指出的是,精度是不适用于工业级,适合小打小闹。
         if((millis()-time3)>5)                     //个人认为这个5ms,1:是需要根据电机转速,码盘线数计算的
                                                           //                             2:是根据电机所产生毛刺的长度来调整
         {                                       
               int L=digitalRead(2);                        
               if(Llast==LOW && L==HIGH)    // 这是根据CHANGE触发方式来编写的,低到高,触发,延时5ms
                                                             // 判断,若改变,计数加1,若不变,计数不增加
               {
                   count +=1;                          
                   time3=millis();
                   Llast=L;
               }else if(Llast==HIGH && L==LOW)
               {
                   count +=1;
                   time3=millis();
                   Llast=L;
               }
           }
回复 支持 反对

使用道具 举报

发表于 2017-3-13 12:32:37 | 显示全部楼层
ignore 发表于 2017-3-11 17:28
从今天的测试来说,目前基本算是解决问题,其主要问题出现在与电机的毛刺上面,对于电机的毛集本上是在2m ...

不好意思, 這幾天有點事忙.

我用旋轉編碼器手動去測試, 接收的中斷也不穩定.  我用 5ms / 10ms / 20ms 去試, 結果都有機會出現來回的數目不同.  數值太小, 有可能混入了亂跳的數.  太大又可能會忽略了正常的中斷.  用手轉動時, 速度可快可慢, 很難完全準確判斷.   

除非你有一個恆定的速度 (或許正如你說: 电机的毛集本上是在2ms左右的,不大不小), 否則, 想要從軟件中準確地完全消除, 實在不容易.


不知其他大大會否有好的方法了.

你的程式, digitalRead 只有 0,1 (LOW/HIGH) 的值, 只要之前跟現在不同, 就增加 count 吧, 應該不用理會 HIGH/LOW, 直接 (Llast != L) 就是有改變了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-13 13:51:43 | 显示全部楼层
Super169 发表于 2017-3-13 12:32
不好意思, 這幾天有點事忙.

我用旋轉編碼器手動去測試, 接收的中斷也不穩定.  我用 5ms / 10ms / 20ms ...

我个人认为还是得用示波器观察各类电机的毛刺到底是什么样的分布,再利用这个时间来消除毛刺,还有就是利用硬件来滤波,不晓得,先查查看
回复 支持 反对

使用道具 举报

发表于 2017-3-13 15:08:53 | 显示全部楼层
ignore 发表于 2017-3-13 13:51
我个人认为还是得用示波器观察各类电机的毛刺到底是什么样的分布,再利用这个时间来消除毛刺,还有就是利 ...

還有一個有趣的地方,在 attachInterrupt, 用 LOW 的話, 我用 5ms 基本上都很接近, 在比較極端的情況才會出錯.  但用 FALLING / RISING / CHANGE 差別就比別大, 基本上隨手轉一兩個圈也有分別.
理論上 LOW, FALLING, HIGH 的次數應該是一致的 (最多只會差一次吧), 感覺上 LOW 比較不易受影響.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-3-14 08:49:02 | 显示全部楼层
Super169 发表于 2017-3-13 15:08
還有一個有趣的地方,在 attachInterrupt, 用 LOW 的話, 我用 5ms 基本上都很接近, 在比較極端的情況才會 ...

这个不太懂,是不是LOW是采用单片机的计数器计数,计数器貌似有带防抖动的计数,如果采用U型红外对射与20线的编码盘计数的话,会出现这么一个问题,红外对射是利用光,那码盘转动起来的话,这个光就有强弱之分,强是我们要的,弱不是我们要的,所以这是就得去除这些弱信号,在硬件上处理就是加个电容并电阻接地,再串电阻,不过这个电容值多大,还是得研究下,
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-3-28 18:53 , Processed in 0.051873 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表