极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 29394|回复: 15

MPU6050采集加速度一段时间后变成0

[复制链接]
发表于 2015-4-17 20:13:57 | 显示全部楼层 |阅读模式
如题,我在用mpu6050做计步器,数据处理是在定时器中断里面写的。但是有一个问题就是,加速度采集的数据会周期性的从正常变到0在逐渐正常,往返很多次后,数据就一直为0了。如图所示,那根蓝色的线就是加速度,不知道到底是什么原因。很纠结!谢谢大神们的帮忙

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2015-4-18 07:54:05 | 显示全部楼层
论坛里有相关案例,可以参考
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-18 11:55:13 | 显示全部楼层
suoma 发表于 2015-4-18 07:54
论坛里有相关案例,可以参考

您好我知道看到一个输出全为0的帖子,跟我这个情况不太一样, 别的并没有找到。您可否告诉我一个帖子?谢谢!
回复 支持 反对

使用道具 举报

发表于 2015-4-18 12:07:36 | 显示全部楼层
热搜MPU6050,我今天帮不了你
回复 支持 反对

使用道具 举报

发表于 2015-4-18 13:01:47 | 显示全部楼层
貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-18 16:18:14 | 显示全部楼层
tsaiwn 发表于 2015-4-18 13:01
貌似你在定时器中断里面写太多事 ?
要有代码才能看出问题

那我把代码发上来看看吧!就是有点长,希望您可以帮帮忙~谢啦
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-18 16:19:35 | 显示全部楼层
//定时器T3中断处理函数
#pragma vector = T3_VECTOR
__interrupt void T3_ISR(void)
{
    IRCON = 0x00;           //清中断标志, 也可由硬件自动完成
    if(Count_20ms++ >= Sample_Period)        //取10时正好20ms,考虑到AD采样,应适当减少。
    {               
      LED1 = ~LED1;
      Count_20ms = 0;
      
      Result_Test = AD_Sample();
     
      Sample_AD = First_Order_Filter();     
      sprintf(strTemp, "%7d \t %7d \t %7d \t %7d \t %7d \t %7d \n", \
                     Sample_AD, step, Peak, Valley, Discrepency, time_peak);      
      UartSendString(strTemp, strlen(strTemp));   

      Find_Peak();
    }
}
/****************************************************************************
* 名    称: Find_Peak()
* 功    能: 峰峰值方法检测迈步
* 出口参数: 无
****************************************************************************/
void Find_Peak()
{
  int16_t Add_Data;
  
  Add_Data =  Sample_AD;      
  for(Move_Data = 0; Move_Data < Count_Window_Peak; Move_Data++)  //数据搬移,所有数据往前一位,此处尽量修改
    Value_Buf[Move_Data] = Value_Buf[Move_Data+1];
  
  Value_Buf[Count_Window_Peak] = Add_Data;      //添加新的数据
  time_peak++;                                   //用来计算峰峰值之间的时间
  
  if(Start == Flag_Find_Peak)
  {
    if(Value_Buf[0]<Value_Buf[1]&&Value_Buf[1]<Value_Buf[2]&&Value_Buf[2]<Value_Buf[3]&&
      Value_Buf[3]>Value_Buf[4]&&Value_Buf[4]>Value_Buf[5]&&Value_Buf[5]>Value_Buf[6])
    {
      Peak = Value_Buf[Mid_Test];
      Flag_Find_Peak = Stop;  
      time_peak = 0;                              //找到峰值时,time_peak重新开始计数
    }
  }
  if(Stop == Flag_Find_Peak)
  {
    if(Value_Buf[0]>Value_Buf[1]&&Value_Buf[1]>Value_Buf[2]&&Value_Buf[2]>Value_Buf[3]&&
       Value_Buf[3]<Value_Buf[4]&&Value_Buf[4]<Value_Buf[5]&&Value_Buf[5]<Value_Buf[6])
    {
      if(Low_Freq < Sample_Period*time_peak < High_Freq) //Sample_Period*time_peak计算峰峰值之间的时间
      {                                             //在限定时间内才算有效步伐
        Valley = Value_Buf[Mid_Test];
        Flag_Find_Peak = Start;
        Flag_Start_Compare = Start;
      }
      else                                          //若不在限定时间内则算无效,需重新开始
        Flag_Find_Peak = Start;
    }
  }
  if(Start == Flag_Start_Compare )
  {
    Discrepency = Peak-Valley;
    //得到Discrepency后,开始进行迈步检测
    if( Discrepency > 100)
    {
      if( Search_Mode == Mode )       //搜索模式,判断是否为短暂的行为
      {
        trans_step++;
        if( 5 == trans_step )      //若检测到连续的五次迈步,则进入确认模式
        {
          Mode = Confirm_Mode;
          trans_step = 0;
          stop_test = 0;
        }
      }
      if( Confirm_Mode == Mode )            //确认模式
      {
        if( TRUE == First_Confirm_Mode )   //第一次进入确认模式,需加上5此检测迈步
        {
          step = step + 5;
          First_Confirm_Mode = FAILURE;        
        }
        else
          step++;
      }
    }
    if(Discrepency <= 100)               //若差值过小,则说明不是正常迈步
    {
      stop_test++;
      if( 5 == stop_test )              //连续五次检测阈值过小,则重新进入搜索模式
      {
        First_Confirm_Mode = TRUE;
        Mode = Search_Mode;
        trans_step = 0;
        stop_test = 0;
      }
    }
    Flag_Start_Compare = Stop;
  }
  if (800 == time_peak)
  {
    First_Confirm_Mode = TRUE;
    Mode = Search_Mode;
    trans_step = 0;
    stop_test = 0;
    time_peak = 0;
  }
}
/****************************************************************************
* 名    称: First_Order_Filter()
* 功    能: 一阶惯性滤波
* 出口参数: 滤波的数值
****************************************************************************/
int16_t First_Order_Filter()
{
  float Trans;
  
  Current_Result =  Result_Test;
                               
  Trans = Filter_Param_1 * Current_Result;
  Trans = Trans + Pre_Result;
  Filter_Result  = (int16_t)Trans;                                                 //送D/A输出
  Pre_Result = Filter_Param * Trans;

  return (Filter_Result);
}

/****************************************************************************
* 名    称: AD_Sample()
* 功    能: 一阶惯性滤波
* 出口参数: 滤波的数值
****************************************************************************/
int16_t AD_Sample()
{
  int16_t ax, ay, az,sum;
  MPU6050_GetRawAccelGyro(&ax, &ay, &az);
  
  ax = ax>>3;
  ay = ay>>3;
  az = az>>3;
  
  if(ax<0) ax = -ax;
  if(ay<0) ay = -ay;
  if(az<0) az = -az;
  
  sum = (ax + ay + az)>>1;
  return (sum);
}
没发主程序,因为主程序只有一些初始化,然后就是死循环了,死循环里面什么都没做
回复 支持 反对

使用道具 举报

发表于 2015-4-18 19:11:05 | 显示全部楼层
新手213123 发表于 2015-4-18 16:19
//定时器T3中断处理函数
#pragma vector = T3_VECTOR
__interrupt void T3_ISR(void)


果然你在中断 __interrupt void T3_ISR(void) 里面做太多事了 !
其中最慢的是 UartSendString(strTemp, strlen(strTemp));   
由你的 sprintf( )可算出 strTemp 有 59 bytes;
   UartSendString( ) 是类似 Serial.print( ),
只是 Serial.print( ) 有串口缓存区,
只要缓存区没满会立即返回, 真正送出的动作不会占用 CPU 的时间,
但是 UartSendString( ) 则是自己 byte by byte 送给 UART (串口)寄存器 !
如果你是用波特率 9600, 每送出一 char 要大约 1.05 ms,
送出 59 个就花掉了 60ms;
如果你用波特率 115200, 则大约也要 6ms 左右!
可以先试着把事情移出 ISR( ),
改放到 loop( ) 内,
在 ISR( ) 内设定一个 flag 通知在 loop( ) 内要做采集相关工作,
int hasJob = 0;  // 有事要做 !
__interrupt void T3_ISR(void)
{
    IRCON = 0x00;           //清中断标志, 也可由硬件自动完成
    if(Count_20ms++ >= Sample_Period)        //取10时正好20ms,考虑到AD采样,应适当减少。
    {               
      LED1 = ~LED1;
      Count_20ms = 0;
      hasJob = 1; // 通知 loop 内的程序码 !
    }
} // ISR()
当然, 在 loop( ) 内要检查 flag 并清除以免重复做, 类似这样:
  if(hasJob != 0) {
    hasJob = 0;
    // do 采集相关工作 ...
  }
回复 支持 反对

使用道具 举报

发表于 2015-4-18 19:23:53 | 显示全部楼层
tsaiwn 发表于 2015-4-18 19:11
果然你在中断 __interrupt void T3_ISR(void) 里面做太多事了 !
其中最慢的是 UartSendString(strTemp ...

不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-18 21:12:04 | 显示全部楼层
tsaiwn 发表于 2015-4-18 19:23
不过如果你的中断来得太快,
可能会有漏掉的情况(因为来不及处理),
你有估计过中断大约多久一次吗?

谢谢!我一直以为是ad转换最浪费时间,没想到是串口最费。我的中断是例程里面考过来的。据说进入10次为20ms。不过我现在放弃用中断来处理了,我把这些都放到主程序里面,问题就解决了!谢啦!
回复 支持 反对

使用道具 举报

发表于 2015-4-18 23:07:34 | 显示全部楼层
新手213123 发表于 2015-4-18 21:12
谢谢!我一直以为是ad转换最浪费时间,没想到是串口最费。我的中断是例程里面考过来的。据说进入10次为20 ...

(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,
   即使你用波特率 115200,
   则大约也要 6ms 左右才有办法从 UartSendString( ) 返回,
   其他你做的估计大约 0.5ms 可做完,
   可是在该 6.5ms 内无法产生中断(因为中断内是禁止中断的)!
   这意思是说,
   本来是每 2ms 中断一次,
   但是当  if(Count_20ms++ >= Sample_Period) 成立之时,
   会有大约 6.5ms 不会有中断(因为在忙着做UartSendString( ) 的事情) !!
  注意, 在这 6.5ms 中, 连 millis( ) 也不会变化,
  因为 millis( ) 是靠中断才能改变, 既然中断被禁止, millis( ) 就不会变化了 !
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-21 20:30:23 | 显示全部楼层
tsaiwn 发表于 2015-4-18 23:07
(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,

好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-21 20:30:47 | 显示全部楼层
tsaiwn 发表于 2015-4-18 23:07
(1)        Congratulations
(2)进入10次为20ms, 那意思是每 2ms 进入一次
   问题是, 你原先的写法,

还有一个问题是,怎么估算程序运行的时间?谢谢
回复 支持 反对

使用道具 举报

发表于 2015-4-22 01:04:21 | 显示全部楼层
新手213123 发表于 2015-4-21 20:30
好深奥!我以前一直怕中断里面进中断。那这样的话,之前那个一直出现0会是什么原因呢?


之前因为你在中断内做太多事,
导致太长时间没有中断, 那样取样就错了,
其实应该不只会出现 0,
你那些不是 0 的答案应该也都不太正确 !
回复 支持 反对

使用道具 举报

发表于 2015-4-22 01:19:19 | 显示全部楼层
新手213123 发表于 2015-4-21 20:30
还有一个问题是,怎么估算程序运行的时间?谢谢

因为 UNO 和大部分板子用 16MHz clock,
这样就是 16 个machine clock 要 1 us,
通常 C 语言的一个句子大约 4 个指令 到 10 个machine clock
例如 byte x = 65;  这句要 2 个machine clock,
又如 unsigned long kk = 12345678;  这句要 8 个machine clock
(因为 long 占用 4 个 bytes);
函数调用大约 4 个 clock, 如果有参数要额外时间,
又如果函数返回要把 value 放入 variable  也要时间,
不过都只是多几个 clock,
但是调用 analogRead( ) 一次大约要 108 us  ( 因为要等 A to D 完成!)
请注意, 108us 是很久喔!
Serial 传送虽然很慢, 但只要你没有把串口的缓存区用满,
可以当作它不花 CPU 的时间(因为每个 char 大约花 3 us),
但是, 一旦你会把串口的缓存区用满,,
那多出的 char 无法送入缓存区, CPU 必须用 while( ) 等待无法做其他事,
这时(假设波特率9600)等于 CPU 要等待 1.1 ms 才送出一个 char !!
请注意, 1 ms = 1024 us 喔 !
有了这些概念
就可以估算出大略时间
如果要比较准
可在前后各调用一次 micros( ) 相减查知 !
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-9 03:15 , Processed in 0.042244 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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