第十九届摩托组(个人分享)(二)编码器T法测速测速代码编写

目录

【前言】

1.测量车模参数

2.编写代码

2.1结构体及相关宏定义

2.2T法测速的算法代码

2.3T法测速的应用代码

3.测试

3.1测试代码

3.2测试视频


视频讲解

【前言】

        本篇笔记主要介绍T法测速的代码,原理请看上篇分享

        最终测得的速度是标准化后的,单位是 cm/s。

        这里我想强调一下为什么要标准化速度。

        我相信大部分同学是直接用编码器读取的值当成速度的,这样M法测速确实方便但是弊端也很明显。毕竟别人问我们车子跑的不错,速度多少啊,我们总不能来一句“速度八十”,那这里的80,是20ms采集的80,还是10ms采集的80。你们队20ms更新一次我们队10ms更新一次速度肯定是不一样的。而且即使更新的周期一样了,采的都是80,那么速度也不一定是一样的,毕竟不同组别,即使假设编码器都是1024线的,齿轮比,轮胎直径也可能是不一样的。

        为了方便交流,也算是给自己心里留个底,我还是非常建议大家把自己的速度标准化,带上单位。

还有另外一件事,我减群友有人问,在换相的时候回出现脉冲

图片素材来自群友
图片素材来自群友
图片素材来自群友

        这个情况我不太清楚,因为我再测试的时候,确实有过波动,但是没遇到过这样的情况,不太清楚。

        原来我以为我没有出现脉冲,我以为是因为有一个限速滤波,但是吧限速滤波关了之后还是没有出现较大的脉冲。我感觉可能是测速方法的问题。

        在这里我要强调一下,我今年是不参赛的,写这个系列是纯粹的分享。起因是在学校帮学弟敲代码,后来水群发现部分同学编码器处理上是有问题的。后来和学弟交流,发现控制框架甚至都有点问题,一个很明显的例子,倒着跑比正着跑稳,我认为就是控制框架有了问题。转向环和平衡环非但没有打配合,还开始打架了,也就是走起来摇头晃脑。(个人理解不喜勿喷)

1.测量车模参数

        既然要给速度带上单位,那自然要测量一波。

车轮直径
​​​​​

        车轮半径我们这里可以取5cm。

        这里光栅齿轮和车轮齿轮比为16:50,光栅编码器一圈是有30个光栅,也就是说,车轮转一圈,光栅发出的脉冲数为

Z=30\cdot \frac{50}{16}=93.75

        记S为一个脉冲代表的,车子行使的距离,就有

S=\frac{2\cdot \pi \cdot r }{Z}

        那么我们只要测量出脉冲间隔时间T就可以得到速度V的公式了

V=\frac{S}{T}

        都是非常简单的,那么我们就自己来看代码。

2.编写代码

        这个代码是在学校敲得,当时用的沁恒的单片机,移植到tc264上也是完全没问题的。视频中测试的就是用tc264测试的。

用沁恒写的T法测速代码库
​​​​​​
用tc264写的T法测速代码库
​​​​​​

2.1结构体及相关宏定义

#define PI_speed    (3.1415926f)
#define GAIN_R      (5.0f)//半径(5。0cm)
#define GAIN_ABS    (GAIN_R*PI_speed*2.0f/93.75f*1000.0f)
//单位:cm/s
#define MAX_SPEED_TIMER  (20.0f)

 //定义卡尔曼参数
 //过程噪声协方差,Q增大,动态响应变快,收敛稳定性变坏
 //测量噪声协方差,R增大,动态响应变慢,收敛稳定性变好
 #define KAL_Q  (0.010f)
 #define KAL_R  (0.40f)


struct S_Speed_ab_Struct
{
    int8                dir;            //速度方向
    int8                speed_0;        //速度归零标志位
    float               time;           //一次脉冲的时间间隔
    float               gain;           //速度转化增益 
    float               abs_speed;      //速度绝对值
    float               speed;          //速度             单位cm/s
    float               last_speed;     //上次速度         单位cm/s
    float               kal_speed;      //卡尔曼滤波后的速度单位cm/s
    struct KFPTypeS     KALMAN_speed;   //卡尔曼滤波结构体


    timer_index_enum    Speed_timer;    //选择的定时器

    gpio_pin_enum       A_pin;          //A信号引脚
    gpio_pin_enum       B_pin;          //B信号引脚

    int8                a_pin;          //A信号电平
    int8                b_pin;          //B信号电平
};

typedef struct S_Speed_ab_Struct Speed_ab_Struct;
typedef Speed_ab_Struct *PSpeed_ab_Struct;



/*************************************************************
** Function name:       Speed_EXPORT
** Descriptions:        声明一个Speed_ab对象,就是结构体的名称
**                      xtimer:采用的定时器位号
**                      xApin:A信号线
**                      xBpin:B信号线
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
#define Speed_EXPORT(x,xtimer,xApin,xBpin)      \
Speed_ab_Struct x = {                           \
        .dir = 1,                               \
        .speed_0 = 0,                           \
        .time = 0.0,                            \
        .gain = GAIN_ABS,                     \
        .abs_speed = 0.0,                       \
        .speed = 0.0,                           \
        .last_speed = 0.0,                      \
        .kal_speed=0,                           \
        .KALMAN_speed={0.02,0,KAL_Q,KAL_R,0.0}, \
        .Speed_timer=xtimer,                    \
        .A_pin=xApin,                           \
        .B_pin=xBpin,                           \
        .a_pin=0,                               \
        .b_pin=0,                               \
}
//声明对象
#define Speed_EXPORT_extern(x)    Speed_ab_Struct x

        这里面的注释都写的很清楚,我要说明一点是,这里时间的单位是ms,是因为我在调试的时候需要用信号发生器来看一下,捕获的脉冲时间对不对,我想没有人会感觉屏幕上,0.001-0.020的波动线条回很好看吧。所以时间的单位是ms。

        这个在计算增益上乘上1000就可以了,所以这里会有一个1000.0f

#define GAIN_ABS    (GAIN_R*PI_speed*2.0f/93.75f*1000.0f)


2.2T法测速的算法代码

写的比较细,主要有四部分,分别是获取时间,获取速度,获取卡尔曼滤波速度,判断速度清零。



 /*************************************************************
 ** Function name:      GetElectricalAngle
 ** Descriptions:       获取时间并且开始计时
 ** Input parameters:   PSpeed_ab_Struct:结构体指针
 ** Output parameters:  none
 ** Returned value:     none
 ** Created by:         qkk
 ** Created date:       24/01/16
 *************************************************************/
void Get_and_start_speed_timer(PSpeed_ab_Struct pSpeed)
{
    pSpeed->time =0.001*timer_get(pSpeed->Speed_timer);
    timer_start(pSpeed->Speed_timer,TIMER_US);
}
/*************************************************************
** Function name:      Get_speed_dir
** Descriptions:       得到速度方向
** Input parameters:   PSpeed_ab_Struct:结构体指针
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
** 注:只放在A中断中
*************************************************************/
 void Get_speed_dir(PSpeed_ab_Struct pSpeed)
{
   pSpeed->a_pin=gpio_get_level(pSpeed->A_pin);
   pSpeed->b_pin=gpio_get_level(pSpeed->B_pin);


   if(pSpeed->a_pin==pSpeed->b_pin)pSpeed->dir=-1;
   else pSpeed->dir=1;

}
/*************************************************************
** Function name:      Get_speed_dir
** Descriptions:       得到速度
** Input parameters:   PSpeed_ab_Struct:结构体指针
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
** 注:只放在A中断中
*************************************************************/
 void Get_speed(PSpeed_ab_Struct pSpeed)
{
    if(pSpeed->speed_0 == 1)
    {
        pSpeed->abs_speed=0;
        pSpeed->speed_0 = 0;
    }else {
        pSpeed->abs_speed=pSpeed->gain*(1.0/(pSpeed->time+0.0001)-1.0/MAX_SPEED_TIMER);
    }

    pSpeed->speed = pSpeed->abs_speed*pSpeed->dir;

    if(pSpeed->speed > pSpeed->last_speed+20.0)
    {
        pSpeed->speed  = pSpeed->last_speed+20.0;
    }
    else if(pSpeed->speed < pSpeed->last_speed-20.0)
    {
        pSpeed->speed   = pSpeed->last_speed-20.0;
    }

    pSpeed->last_speed  = pSpeed->speed;

}

 /*************************************************************
 ** Function name:      Get_kalman_speed
 ** Descriptions:       得到滤波速度
 ** Input parameters:   PSpeed_ab_Struct:结构体指针
 ** Output parameters:  none
 ** Returned value:     none
 ** Created by:         qkk
 ** Created date:       24/01/16
 ** 注:2ms滤波一次
 *************************************************************/
  void Get_kalman_speed(PSpeed_ab_Struct pSpeed)
 {
      pSpeed->kal_speed=KalmanFilter(&pSpeed->KALMAN_speed,pSpeed->speed );
 }

2.3T法测速的应用代码

这里的应用一共有四个部分,无论是ch32v307还是tc264

void My_speed_INIT      (void); //初始化代码
void My_speed_loop      (void); //放在1ms的中断中
void My_r_speed_update  (void); //放在R轮A边沿触发中断
void My_l_speed_update  (void); //放在L轮A边沿触发中断

详细代码如下


/*************************************************************
** Function name:      My_speed_INIT
** Descriptions:       T法测速测速初始化
** Input parameters:   none
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
*************************************************************/
void My_speed_INIT(void)
{
    exti_init(r_speed.A_pin,EXTI_TRIGGER_FALLING);
    gpio_init(r_speed.B_pin, GPI, GPIO_HIGH, GPI_FLOATING_IN);
    exti_init(l_speed.A_pin,EXTI_TRIGGER_FALLING);
    gpio_init(l_speed.B_pin, GPI, GPIO_HIGH, GPI_FLOATING_IN);

    Get_and_start_speed_timer(&r_speed);
    Get_and_start_speed_timer(&l_speed);

}

/*************************************************************
** Function name:      My_speed_loop
** Descriptions:       T法测速循环
** Input parameters:   none
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
** 注:主要功能包括超时检测和滤波,放在主循环中
*************************************************************/
void My_speed_loop(void)
{
    RUN_BY_speed_BLOCK(1,             \
     {
             JUDGE_speed_0(&r_speed);
             JUDGE_speed_0(&l_speed);

     }
    )

    RUN_BY_speed_BLOCK(2,             \
     {
              Get_kalman_speed(&r_speed);
              Get_kalman_speed(&l_speed);
     }
    )

}

/*************************************************************
** Function name:      My_r_speed_update
** Descriptions:       T法测速测速更新数据
** Input parameters:   none
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
** 注:放在下降沿触发中断中
*************************************************************/
void My_r_speed_update(void)
{
    Get_and_start_speed_timer(&r_speed);
    Get_speed_dir(&r_speed);
    Get_speed(&r_speed);
}
/*************************************************************
** Function name:      My_r_speed_update
** Descriptions:       T法测速测速更新数据
** Input parameters:   none
** Output parameters:  none
** Returned value:     none
** Created by:         qkk
** Created date:       24/01/16
** 注:放在下降沿触发中断中
*************************************************************/
void My_l_speed_update(void)
{
    Get_and_start_speed_timer(&l_speed);
    Get_speed_dir(&l_speed);
    Get_speed(&l_speed);
}


如果要更改引脚,直接在声明对象的时候把引脚改了,把更新左右速度的函数放到相应的中断中就可以了,不需要更改初始化代码。

CH32V307 声明对象


Speed_EXPORT(r_speed,TIM_3,D10,D11);
Speed_EXPORT(l_speed,TIM_4,D12,D13);

TC264 声明对象

//声明对象
Speed_EXPORT(r_speed,STM0,ERU_CH5_REQ1_P15_8,P15_8,P00_9);//A相P15_8B相P0_9
Speed_EXPORT(l_speed,STM1,ERU_CH4_REQ8_P33_7,P33_7,P33_6);//A相P33_7B相P33_6


3.测试

3.1测试代码

测试代码的时候,需要注意打开一个1ms的中断,把这三个函数放进去

然后把相应的速度更新函数放到相应的中断中就可以了。

下面是tc264的

下面是CH32V307

测试代码我这里只贴tc264的代码了,主要就是300ms改变一次电压方向,并没有对速度进行闭环。

	while (TRUE)
	{
        // 此处编写需要循环执行的代码
        RUN_BY_LIMIT_BLOCK(10,\
              VOFA();
        )
         // LED  1s闪一次,表示程序正在运行
        RUN_BY_LIMIT_BLOCK(1000,\
              gpio_toggle_level (P21_4);
        )

        RUN_BY_LIMIT_BLOCK(300,\
                {


              if(timer_i==0)
                  {
                  pwm_set_duty(ATOM0_CH4_P02_4,3000);
                  pwm_set_duty(ATOM0_CH5_P02_5,0);
                  timer_i=1;
                  }
              else
                  {
                  pwm_set_duty(ATOM0_CH4_P02_4,0);
                  pwm_set_duty(ATOM0_CH5_P02_5,3000);
                  timer_i=0;
                  }
                }
        )
        // 此处编写需要循环执行的代码
	}

3.2测试视频

测试视频如下

测试

这里再放一张速度慢时的波形,

4.开源代码的获取

        写到这里,其实已经把代码都放出来了,把本文中的代码简单移植一下或者自己写一份都是没问题的。

        至少我自己在测试的过程中,我个人认为还是不错的,原始数据还是比较稳定的,甚至都可以不用卡尔曼滤波,直接用T法测速后的原始数据就可以了。

        工程文件我放到B站了,需要的同学可以自取。有两个版本一个是基于ch32v307vct6的工程文件,在ch32v307vct6上,正常测试没有问题。接口如下

        另一个版本是基于tc264的工程文件,测试视频就是这个,接口如下

        码字不易,如果同学们感觉有帮助的话可以在B站我的工房支持一下,但是核心内容都在这篇文章中了,工程文件里面并不会有新的东西。

基于ch32v307vct6的工程文件
基于TC264的工程文件
 

        

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值