上节我把MT6816的驱动给大致整明白了,接下去需要看看如何校准。
- 为什么编码器需要校准?
如上节所说,MT6816是一款绝对值编码器 ,它为每个测量位置分配了唯一的二进制代码或字,即使断电,也可以跟踪编码器的确切位置。
但是由于硬件或其他因素的影响,编码器输出的位置可能存在一定的误差。因此,需要进行校准来确保编码器输出的位置与实际步进电机的位置一致。具体而言,校准的目的是通过对编码器输出数据的处理和比较,确定真正的步进电机位置并建立编码器输出值与实际位置之间的对应关系。
- 磁编码器的校准方法
校准通常包括检查平均值的连续性和方向,以及对编码器编码器输出数据与步进电机实际相位角非线性关系的拟合。
具体的方法可以是:
- 检查平均值之间的差值,检查是否过小或过大,如果过小,则表示编码器输出的数据存在重复或重叠;如果过大,表示某些数据点存在异常或丢失。
- 方向检查:可以通过计算相邻两个采样数据的差值检查,差值为正值代表正转,负值则为反转,如果为0,则方向错误
- 步进电机旋转角度和编码器输出数据之间的关系通常是非线性的。在校准过程中,可以通过采集一系列已知角度位置的数据点,并拟合出角度与编码器数据之间的关系。这个拟合可以使用曲线拟合算法或其他数学方法来实现。通过拟合,可以建立编码器输出数据与实际相位角之间的非线性转换公式,从而实现更准确的角度测量。
一个个地来看代码实现,先是检查平均值之间的差值。步进电机的旋转是360°一圈,磁编码器的取值也是从小到大一圈的值,所以两个点取值有可能出现的情况是:第一个点是最大值,转了一个小角度后这个值会出现跳变到最小值附近,但两个点的平均值绝对不是真实的平均值,需要进行调整。具体的获取平均值的参考代码如下:
int32_t CycleAverage(int32_t a, int32_t b, int32_t cyc) //cyc即是一个周期值,在磁编码器的应用中为2^14
{
int32_t sub_data;
int32_t ave_data;
sub_data = a - b;
ave_data = (a + b) / 2;
if(abs(sub_data) > (cyc / 2)) //如果两个点差的绝对值大于周期的一半,则认为发生了跳变,进行相应的调整
{
if(ave_data >= (cyc / 2 )) ave_data -= (cyc / 2);
else ave_data += (cyc / 2);
}
return ave_data;
}
如果要求两个点的差 ,同样也要考虑是否会发生跳变,求循环差。循环差的概念在这里举个例子,以时间为例,从晚上20点到凌晨2点,一共过去了几个小时?一定不是20-2=18小时,而是6个小时,可以用下面的代码来取循环差:
int32_t CycleSub(int32_t a, int32_t b, int32_t cyc)
{
int32_t sub_data;
sub_data = a - b;
if(sub_data > (cyc >> 1)) sub_data -= cyc;
if(sub_data < (-cyc >> 1)) sub_data += cyc;
return sub_data;
}
再来考虑步进电机走的步数问题,一般步进电机一圈的步数是200,即每一步走1.8°,所以也要保证算出来的步数在循环周期(即200)之内,可以简单的用取余运算
来实现这个功能:
uint32_t CycleRem(uint32_t a,uint32_t b)
{
return (a+b)%b;
}
有了这些方法后,先进行旋转的方向性确认,
for(count=0; count<201; count++) //每一步的采集数据的平均值,存在coder_data_f[count]数组里,从coder_data_f[0]到[200],是一个旋转周期
{
encode_cali.coder_data_f[count] = (uint16_t)CycleAverage((int32_t)encode_cali.coder_data_f[count], (int32_t)encode_cali.coder_data_r[count], 2^14);
}
sub_data = CycleSub((int32_t)encode_cali.coder_data_f[0], (int32_t)encode_cali.coder_data_f[199], 2^14); //用最大的角度值和最小的角度值相减,求循环差
if(sub_data == 0) { encode_cali.error_code = CALI_Error_Average_Dir; return; } //如果差为0,校正错误
if(sub_data > 0) { encode_cali.dir = true; } //代表正转
if(sub_data < 0) { encode_cali.dir = false; } //代表反转
再次对电机每一步的编码器数据进行检查:
for(count=1; count<200; count++)
{
sub_data = CycleSub((int32_t)encode_cali.coder_data_f[count], (int32_t)encode_cali.coder_data_f[count-1], 2^14); //每一步和上一步的数据差,如果是正转,则差值大于0,如果是反转,则差值应小于0
if(abs(sub_data) > (CALI_Gather_Encode_Res * 3 / 2))
{ encode_cali.error_code = CALI_Error_Average_Continuity;
encode_cali.error_data = count;
return;
} //两次数据差过大,则报错
if(abs(sub_data) < (CALI_Gather_Encode_Res * 1 / 2))
{ encode_cali.error_code = CALI_Error_Average_Continuity;
encode_cali.error_data = count;
return;
} //两次数据差过小,也报错
if(sub_data == 0)
{ encode_cali.error_code = CALI_Error_Average_Dir;
encode_cali.error_data = count;
return;
} //数据差为0也不对
if((sub_data > 0) && (!encode_cali.dir)) //如果数据差大于0又不是正转,报错
{ encode_cali.error_code = CALI_Error_Average_Dir;
encode_cali.error_data = count;
return;
}
if((sub_data < 0) && (encode_cali.dir)) //如果数据差小于0又是正转,报错
{ encode_cali.error_code = CALI_Error_Average_Dir;
encode_cali.error_data = count;
return;
}
}
接下去进行步进电机相位角和磁编码器数据的拟合,会在下一节学习,还有最为关键的:正常工作时的编码器驱动回调函数的编写。
未完待续