平衡车 --- 测量减速比 M法、T法测速
🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
我们这篇文章主要想要讲述的是测量减速比和M法测速
测量减速比
//
// @简介: 读取左编码器的位置
//
float App_Encoder_GetPos_L(void)
{return encoder_l /22.0f/(30613.0f/1500.0f)*360.0f;
}
//
// @简介: 读取右编码器的位置
//
float App_Encoder_GetPos_R(void)
{return encoder_r/22.0f/(30613.0f/1500.0f)*360.0f;
}
M法测速
减速比为20.4,每圈线数为22,因为有11对级。
M法是适用于电机高转速的时候的,这是因为M是由电机的编码器测出来的,而时间是由单片机进行计时的,这两个之间是存在一定误差的,误差大小为1/M,所以当M越大时,误差是越小的,所以说M法测速适用于大转速的时候。
对于T法而言,T法是适用于低转速的时候的,T法测速的误差来源是单片机的计算的时间的误差,当本身测量的时间足够长的时候,也就是速度足够慢的时候,小于1微秒,相对于整段时间而言就是微不足道的,所以,这个时候的误差会小很多。
static float last_pos_l = 0;
static float last_pos_r = 0;
//
// @简介: M法测速的测试代码
//
void Encoder_M_Method_Test(void)
{App_Usart_Init();App_Encoder_Init();while(1){Delay(1);float pos_l = App_Encoder_GetPos_L();float pos_r = App_Encoder_GetPos_R();float M_l = pos_l - last_pos_l;float M_r = pos_r - last_pos_r;float omega_l = M_l/0.001f;float omega_r = M_r/0.001f;My_USART_Printf(USART2,"%f,%f\n",omega_l,omega_r);last_pos_l = pos_l;last_pos_r = pos_r;}
}
T法测速
首先,我们用direction来表示电机转动的方向
static volatile int8_t direction_l = 1;//左电机旋转方向,1 - 正转, -1 - 反转
static volatile int8_t direction_r = 1;//右电机旋转方向,1 - 正转, -1 - 反转
//
// @简介:中断响应函数,右编码器的A相
//
void EXTI3_IRQHandler(void)
{EXTI_ClearFlag(EXTI_Line3);uint8_t a = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3);//A相当前电压uint8_t b = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4);//B相当前电压if(a==Bit_SET)//上升沿{if(b==Bit_RESET){direction_r = 1;encoder_r++;}else{direction_r = -1;encoder_r--;}}else{if(b==Bit_RESET){direction_r = -1;encoder_r--;}else{direction_r = 1;encoder_r++;}}
}
//
// @简介: 中断响应函数,左编码器的A相
//
void EXTI15_10_IRQHandler(void)
{if(EXTI_GetFlagStatus(EXTI_Line14)==Bit_SET){EXTI_ClearFlag(EXTI_Line14);uint8_t a = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14);//A相当前电压uint8_t b = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15);//B相当前电压if(a==Bit_SET)//上升沿{if(b==Bit_RESET){direction_l = 1;encoder_l++;}else{direction_l = -1;encoder_l--;}}else{if(b==Bit_SET){direction_l = -1;encoder_l--;}else{direction_l = 1;encoder_l++;}}}
}
接下来,我们获取左右电机编码器的时间,从而用T法进行测速
static volatile int8_t t0_l = 0,t1_l = 0;//左编码器电机的时间
static volatile int8_t t0_r = 0,t1_r = 0;//右编码器电机的时间
然后,我们在中断函数中进行处理:
t1_r = t0_r;t0_r = GetUs();
t1_l = t0_l;t0_l = GetUs();
//
// @简介: 获取左编码器的速度
//
float App_Encoder_GetSpeed_L(void)
{return direction_l/((t1_l -t0_l)* 10.0e-6f)/22.0f/(30613.0f/1500.0f)*360.0f;
}
//
// @简介: 获取右编码器的速度
//
float App_Encoder_GetSpeed_R(void)
{return direction_r/((t1_l -t0_l)* 10.0e-6f)/22.0f/(30613.0f/1500.0f)*360.0f;
}
然后,我们对这个代码进行测试
//
// @简介: T法测速的测试代码
//
void Encoder_T_Method_Test(void)
{App_Usart_Init();App_Encoder_Init();while(1){Delay(1);float omega_l = App_Encoder_GetSpeed_L();float omega_r = App_Encoder_GetSpeed_R();My_USART_Printf(USART2,"%f,%f\n",omega_l,omega_r);}
}
T法测速改进
T法测速是存在问题的,比如:
1. 速度不归零
2. 换向波形异常
3. 偶尔出现毛刺
我们接下来,主要是要解决这三个问题。
解决方式,为我们应该设置一个新的now,记录当前时间,然后两者取最大时间,代码如下:
//
// @简介: 获取左编码器的速度
//
float App_Encoder_GetSpeed_L(void)
{uint64_t now = GetUs();float T;if(t0_l - t1_l > now - t0_l){T = (t0_l - t1_l)*1.0e-6f;}else{T = (now - t0_l)*1.0e-6f;}return direction_l/T/22.0f/(30613.0f/1500.0f)*360.0f;
}
//
// @简介: 获取右编码器的速度
//
float App_Encoder_GetSpeed_R(void)
{uint64_t now = GetUs();float T;if(t0_r - t1_r > now - t0_r){T = (t0_r - t1_r)*1.0e-6f;}else{T = (now - t0_r)*1.0e-6f;}return direction_r/T/22.0f/(30613.0f/1500.0f)*360.0f;
}
这样子的话,从串口助手中看,当静止不动的时候,速度就为0了
对于第二种情况
这样,我们对direction进行标记,我们就知道在哪个时候发生了换向,此时,我们直接让omega为0就好了。
//
// @简介: 获取左编码器的速度
//
float App_Encoder_GetSpeed_L(void)
{if(direction_l == 2 || direction_l ==-2) return 0;uint64_t now = GetUs();float T;if(t0_l - t1_l > now - t0_l){T = (t0_l - t1_l)*1.0e-6f;}else{T = (now - t0_l)*1.0e-6f;}return direction_l / T / 22.0f/(30613.0f/1500.0f)*360.0f;
}
//
// @简介: 获取右编码器的速度
//
float App_Encoder_GetSpeed_R(void)
{if(direction_r == 2 || direction_r ==-2) return 0;uint64_t now = GetUs();float T;if(t0_r - t1_r > now - t0_r){T = (t0_r - t1_r)*1.0e-6f;}else{T = (now - t0_r)*1.0e-6f;}return direction_r/T/22.0f/(30613.0f/1500.0f)*360.0f;
}
你看,这个时候,换向点的地方,就不会那么突兀了。
接下来,我们解决这个毛刺的问题:
这个是我们测速的代码,我们可以看到,检测到边沿的时候,会产生对应的中断。导致now和t1、t0的时间发生改变。
这样就导致了,上面图片中的现象,这个就是毛刺产生的原因。
所以,我们只要关闭这个中断就好了
//
// @简介: 获取左编码器的速度
//
float App_Encoder_GetSpeed_L(void)
{__disable_irq();//关闭单片机的总中断int8_t direction_cpy = direction_l;uint64_t t0_cpy = t0_l;uint64_t t1_cpy = t1_l;__enable_irq();//开启总中断if(direction_cpy == 2 || direction_cpy ==-2) return 0;uint64_t now = GetUs();float T;if(t0_cpy - t1_cpy > now - t0_cpy){T = (t0_cpy - t1_cpy)*1.0e-6f;}else{T = (now - t0_cpy)*1.0e-6f;}return direction_cpy / T / 22.0f/(30613.0f/1500.0f)*360.0f;
}
//
// @简介: 获取右编码器的速度
//
float App_Encoder_GetSpeed_R(void)
{__disable_irq();//关闭单片机的总中断int8_t direction_cpy = direction_r;uint64_t t0_cpy = t0_r;uint64_t t1_cpy = t1_r;__enable_irq();//开启总中断if(direction_cpy == 2 || direction_cpy ==-2) return 0;uint64_t now = GetUs();float T;if(t0_cpy - t1_cpy > now - t1_cpy){T = (t0_cpy - t1_cpy)*1.0e-6f;}else{T = (now - t0_cpy)*1.0e-6f;}return direction_cpy/T/22.0f/(30613.0f/1500.0f)*360.0f;
}
这样子的话,毛刺就没有了
好了,今天的内容就到这里,明天再见