检测磁性编码器是否通过 360 或 0 以及在哪个方向(roll-over/wraparound 方向?) - C

Detect if magnetic encoder passes 360 or 0 and in which direction (roll-over/wraparound direction?) - C

我使用磁性编码器,它会对手输入做出反应。转弯时,它会计算角度,那部分工作得很好。这意味着如果我将编码器旋转 360 度,角度将被重置。 (因此再次为 0)。但是,我想检测编码器是否已通过 360 度并返回到 0,或者它是否从 0 返回到 360。我需要知道编码器以 30 度为步长转动的方向。

所以: 我怎么知道编码器是顺时针方向转动(fx. 从 0 -> 360 -> 0 -> 360)还是逆时针方向转动(fx. 从 360 -> 0 - > 360 -> 0)

现在它认为从 360 到 0 的步骤是逆时针转,而实际上是顺时针转...

有什么建议吗?

由于磁性编码器一次可以转动超过 180 度(虽然不太可能),从技术上讲不可能知道它转动的方向,因为例如。顺时针转 90 度也可能是逆时针转 270 度。

如果您的磁性编码器没有指示旋转方向,您最好只能猜测(例如最短距离 - 即顺时针 90 度比逆时针 270 度更有可能),但是你有时可能会弄错。

如果可以接受,则相对容易(假设角度为 0 到 360 之间的整数度):

int rotation_angle(int old_angle, int new_angle) {
  int result = (360 + new_angle - old_angle) % 360;
  return (result > 180) ? result - 360 : result;
}

然后:

printf("%d\n", rotation_angle(0, 330)); // prints : -30
printf("%d\n", rotation_angle(330, 0)); // prints : 30

如果能保证最快旋转半圈(180°)的轮询发生的频率更高,应该有以下考虑:

  1. 当前读数与上一次读数的绝对差值不能超过半圈=180°
  2. 如果绝对差是 >= 180°,我们已经超过了 。我们移动的度数是通过根据当前旋转方向(顺时针加,逆时针减)加减一整圈(360°)来计算的。
  3. 如果绝对差为< 180°且差号为正,我们顺时针移动(增量角)
  4. 如果绝对差为< 180°且差号为负,我们逆时针移动(递减角)
  5. 如果差异== 0则没有移动发生。

在代码中:

int LastAngle = GetAngle();    // Init angle reading
bool bClockWise = true;
...
// polling or interrupt handler
int CurrAngle = GetAngle();
int diff = CurrAngle - LastAngle;
if (diff==0)
{
    //No move
    ...
}
else if (abs(diff) < 180)   //Angle changed from last read
{
    //if we are here diff is not 0
    //we update rotation accordingly with the sign
    if (diff > 0)
        bClockWise = true;     //we were rotating clockwise
    else
        bClockWise = false;    //we were rotating counterclockwise
}
//If absolute difference was > 180° we are wrapping around the 0
//in this case we simply ignore the diff sign and leave the rotation
//to the last known verse.
...

如果你想计算转数,你可以编码:

int Turns = 0;
if ((diff != 0) && (abs(diff) > 180))
{
    if (bClockWise)
        Turns++;     //Increase turns count
    else
        Turns--;     //Decrease turns count
}

以下宏可用于检查运动和旋转感:

#define IsMoving    (diff)        //Give a value !=0 if there is a movement
#define IsCw        (bClockWise)  //Give true if clockwise rotation
#define IsWrap      (abs(diff) >= 180)  //Give true if knob wrapped

P.S.请注意,diff变量是旋转感检测和移动的功能,不是绝对的区别运动之间的度数.

如果你想计算真实的运动,你应该考虑环绕:

int Angle = 0;    //the real angle tracked from code start
if (diff != 0)
{
    if (abs(diff) >= 180)
    {
        if (bClockWise)
            Angle += diff + 360;     //Adjust for positive rollover
        else
            Angle += diff - 360;     //Adjust for negative rollover
    }
    else
        Angle += diff;
}

这是一个非常简单的解决方案:

int rotation_angle(int new_reading, int old_reading) {
    /* angle readings are in [0..360] range */
    /* compute the difference modulo 360 and shift it in range [-180..179] */
    return (360 + 180 + new_reading - old_reading) % 360 - 180;
}

rotation_angle() return 符号角差

备注:

  • 因为 new_readingold_reading 被假定在 [0..360 范围内,它们的差异范围是 [-360 ..360],加上 360 确保模运算 return 是 0359 之间的正值。
  • 在模前添加 180 并从结果中减去 180 将输出值的范围移动到 [-180..180].
  • 0表示角度不变
  • 负值表示转向较小的角度值。
  • 正值表示转向大角度值
  • 如果角度变化可以超过 180 度,则 return 值的解释很可能是不正确的。必须足够快地执行角度采样以防止出现此类情况。