位置检测有哪些传感器可以用?(带磁编码器AS5600代码)

本文介绍了编码器、霍尔传感器和磁编码器在电机控制中的应用。编码器包括增量式和绝对式,通过光电、电磁等方式检测位置变化;霍尔传感器利用120度间隔的信号检测电机角度;磁编码器则提供绝对位置信息,可通过模拟值、PWM或IIC接口输出。此外,文章详细阐述了这些传感器的工作原理、中断处理和信号处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常见的有三种

编码器、霍尔传感器、磁传感器。

编码器一般指AB相正交脉冲的增量器件,有的还会有一个Z相信号,用来指示零位;

霍尔传感器一般是指ABC三个成120度角度间隔排列的器件,这种传感器一般集成在电机内部,电机每转一圈会生成6N个信号,每个信号代表电机转到了特定的角度;

磁编码器本质上也是靠霍尔效应检测,但它是一种输出绝对位置的器件,这类传感器一般都有多种接口,如SPI、IIC、模拟值或PWM占空比。

编码器

编码器是一种用于运动控制的传感器,利用光电、电磁、电容或者电感等感应原理,来检测物体的机械或者位置变化,然后把信息转化为电信号输出,作为运动的反馈。

旋转编码器

光电编码器

通过光电转换,将输出轴的角位移、角速度等机械两转化为电脉冲,以数字量输出。

磁式编码器

电容式编码器

增量式编码器

资料

链接:https://pan.baidu.com/s/1Hp1fucofpC7nCn8-D8w_0A?pwd=pvpm
提取码:pvpm

代码

SimpleFOC Library设计了两种正交编码器的计数方法:

正交计数:对信号A和B的每次跳变都进行采样,这样在每个脉冲周期内将有四次信号跳变,采样四次,计数四次(即CPR Count Per Rotation),具体计算见以下源码。 普通计数:对信号A和B的上升沿进行计数,这样每个脉冲周期内将有两次信号跳变,采样两次,但只对某一个通道计数一次(即PPR Pulses Per Rotation),具体计算见以下源码。 正交模式下CPR = PPR*4 普通模式下CPR = PPR,显然正交模式下分辨率更高,但系统开支也更大,如果对分辨率要求不高可以使用普通模式。

MCU对A和B信号线连接的两个IO口使能外部中断,中断回调函数中实现了计数,回调函数源码如下所示,A和B两个通道的回调函数内容一样,只以A通道为例说明。

//定义对象,使能外部中断
Encoder encoder = Encoder(2, 3, 8192);      //定义正交编码器对象,连接到2和3号引脚,编码器PPR=8192
void doA(){encoder.handleA();}              //A通道中断回调函数
void doB(){encoder.handleB();}              //B通道中断回调函数
encoder.enableInterrupts(doA, doB);         //使能外部中断,设置回调函数

void Encoder::enableInterrupts(void (*doA)(), void(*doB)(), void(*doIndex)())
{
  // attach interrupt if functions provided
  switch(quadrature){        //quadrature:是否开启正交模式
    case Quadrature::ON:
      // A callback and B callback
      if(doA != nullptr) attachInterrupt(digitalPinToInterrupt(pinA), doA, CHANGE);
      if(doB != nullptr) attachInterrupt(digitalPinToInterrupt(pinB), doB, CHANGE);
      break;
    case Quadrature::OFF:
      // A callback and B callback
      if(doA != nullptr) attachInterrupt(digitalPinToInterrupt(pinA), doA, RISING);
      if(doB != nullptr) attachInterrupt(digitalPinToInterrupt(pinB), doB, RISING);
      break;
  }

  //如果有Z相信号线,使能Z相的中断
  if(hasIndex() && doIndex != nullptr) attachInterrupt(digitalPinToInterrupt(index_pin), doIndex, CHANGE);
}

//  Encoder interrupt callback functions
// A channel
void Encoder::handleA()
{
  bool A = digitalRead(pinA);                       //测量A引脚高低电平
  switch (quadrature)
  {                               
    case Quadrature::ON:                            //正交模式下:
    // CPR = 4xPPR
     if ( A != A_active )                           //如果A引脚状态不等于"当前记录的状态"
     {
       //如果A引脚"当前记录的状态"等于B引脚"当前记录的状态"计数+1
       //对应图六中左侧A信号的上升沿与下降沿状态
       //否则为逆时针旋转,计数-1
       pulse_counter += (A_active == B_active) ? 1 : -1;
       pulse_timestamp = _micros();
       A_active = A;                                 //更新A引脚当前状态
     }
     break;
   case Quadrature::OFF:                             //普通模式下
   // CPR = PPR
     if(A && !digitalRead(pinB))                     //如果A引脚是高电平且B引脚是低电平
     {
       pulse_counter++;                              //计数+1,对应图六中左上角A上升沿状态
       pulse_timestamp = _micros();
     }
     break;
   }
}

霍尔传感器

如上图所示,右侧的3个器件就是霍尔传感器,它们成120度夹角安装,也有60度安装的,但控制逻辑不一样。本节以一对极120度夹角电机模型说明。一对极指转子只有一对磁铁NS极,图四为多对极电机。

按特定的组合给电机的UVW三相供电,如U+,V-,W断开,此时电机转子将转到某一固定位置,假设为扇区Sector1,然后切换供电状态为W+,V-,U断开,电机将继续旋转60度到扇区二,按特定的顺序依次切换供电状态,电机将会一直转动下去,共有6种供电状态,对应着6个扇区,6种霍尔传感器的触发状态,如下图所示。

图六、霍尔的六种触发状态

霍尔传感器就是根据这六种状态确定转子的位置,加上这6种状态的循环次数就是当前电机转子的真实位置。

//HallSensor.h
//霍尔传感器状态与扇区对应关系
// seq 1 > 5 > 4 > 6 > 2 > 3 > 1     000 001 010 011 100 101 110 111
const int8_t ELECTRIC_SECTORS[8] = { -1,  0,  4,  5,  2,  1,  3 , -1 };

霍尔位置传感器的程序源码与ABZ正交编码器程序源码基本相同,引脚配置和外部中断设置基本相同,主要内容在updateState函数中,下边简单分析updateState函数。

void HallSensor::updateState() 
{
 long new_pulse_timestamp = _micros();
 //刷新三个霍尔传感器的状态
 int8_t new_hall_state = C_active + (B_active << 1) + (A_active << 2);
 //去除噪声
  // glitch avoidance #1 - sometimes we get an interrupt but pins haven't changed
 if (new_hall_state == hall_state) {
 return;
  }
 hall_state = new_hall_state;
 //匹配当前所在扇区
 int8_t new_electric_sector = ELECTRIC_SECTORS[hall_state];
 static Direction old_direction;
 //
 if (new_electric_sector - electric_sector > 3) {    //如从扇区0转到了扇区5,则循环次数-1
    //underflow
 direction = Direction::CCW;
 electric_rotations += direction;
  } else if (new_electric_sector - electric_sector < (-3)) {//如从扇区5转到了扇区0,则循环次数+1
    //overflow
 direction = Direction::CW;
 electric_rotations += direction;
  } else {
 direction = (new_electric_sector > electric_sector)? Direction::CW : Direction::CCW;
  }
 electric_sector = new_electric_sector;

  // glitch avoidance #2 changes in direction can cause velocity spikes.  Possible improvements needed in this area
 if (direction == old_direction) {
    // not oscilating or just changed direction
 pulse_diff = new_pulse_timestamp - pulse_timestamp;
  } else {
 pulse_diff = 0;
  }
 
 pulse_timestamp = new_pulse_timestamp;
 total_interrupts++;
 old_direction = direction;
 if (onSectorChange != nullptr) onSectorChange(electric_sector);
}

磁编码器

磁编码器有基于霍尔效应原理的,也有基于磁阻效应的。SimpleFOC项目使用的有以下几种,这里以AS5600说明。

/** Typical configuration for the 12bit AMS AS5600 magnetic sensor over I2C interface */
MagneticSensorI2CConfig_s AS5600_I2C = {
  .chip_address = 0x36,
  .bit_resolution = 12,
  .angle_register = 0x0C,
  .data_start_bit = 11
};

/** Typical configuration for the 12bit AMS AS5048 magnetic sensor over I2C interface */
MagneticSensorI2CConfig_s AS5048_I2C = {
  .chip_address = 0x40,  // highly configurable.  if A1 and A2 are held low, this is probable value
  .bit_resolution = 14,
  .angle_register = 0xFE,
  .data_start_bit = 15
};

/** Typical configuration for the 14bit AMS AS5147 magnetic sensor over SPI interface */
MagneticSensorSPIConfig_s AS5147_SPI = {
  .spi_mode = SPI_MODE1,
  .clock_speed = 1000000,
  .bit_resolution = 14,
  .angle_register = 0x3FFF,
  .data_start_bit = 13, 
  .command_rw_bit = 14,
  .command_parity_bit = 15
};
// AS5048 and AS5047 are the same as AS5147
MagneticSensorSPIConfig_s AS5048_SPI = AS5147_SPI;
MagneticSensorSPIConfig_s AS5047_SPI = AS5147_SPI;

/** Typical configuration for the 14bit MonolithicPower MA730 magnetic sensor over SPI interface */
MagneticSensorSPIConfig_s MA730_SPI = {
  .spi_mode = SPI_MODE0,
  .clock_speed = 1000000,
  .bit_resolution = 14,
  .angle_register = 0x0000,
  .data_start_bit = 15, 
  .command_rw_bit = 0,  // not required
  .command_parity_bit = 0 // parity not implemented
};

原理

AS5600

是基于霍尔的旋转磁位置传感器,有12位高分辨率,使用平面传感器来将垂直于芯片表面的磁场分量转换为电压。它有3种输出方式,模拟值,PWM和IIC。

模拟值模式

//examples\utils\sensor_test\magnetic_sensors\magnetic_sensor_analog_example\find_raw_min_max\find_raw_min_max.ino
#include <SimpleFOC.h>
/**
 * 编码器处于Analog模式,指定AD引脚,它的电压与位置成线性正比
 */
//AD使用A1引脚,AD最小值为14,最大值为1020,10位AD采样
MagneticSensorAnalog sensor = MagneticSensorAnalog(A1, 14, 1020);

void setup()
{
  // monitoring port
 Serial.begin(115200);
  //传感器初始化,读取一次AD值,并记录当前时间
 sensor.init();
 Serial.println("Sensor ready");
 _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
 Serial.print(sensor.getAngle());
 Serial.print("\t");
 Serial.println(sensor.getVelocity());
}
//src\sensors\MagneticSensorAnalog.cpp
#include "MagneticSensorAnalog.h"
MagneticSensorAnalog::MagneticSensorAnalog(uint8_t _pinAnalog, int _min_raw_count, int _max_raw_count)
{
 pinAnalog = _pinAnalog;                   //AD引脚
 cpr = _max_raw_count - _min_raw_count;    //cpr计算
 min_raw_count = _min_raw_count;
 max_raw_count = _max_raw_count;

 if(pullup == Pullup::USE_INTERN){
 pinMode(pinAnalog, INPUT_PULLUP);
  }else{
 pinMode(pinAnalog, INPUT);
  }

}


void MagneticSensorAnalog::init()
{
  // velocity calculation init
 angle_prev = 0;
 velocity_calc_timestamp = _micros(); 

  // full rotations tracking number
 full_rotation_offset = 0;
 raw_count_prev = getRawCount();       //读取一次当前AD值
}

//  Shaft angle calculation
//  angle is in radians [rad]
float MagneticSensorAnalog::getAngle(){
  //读取一次当前AD值
 raw_count = getRawCount(); 
//计算AD值变化量,如果变化量大于0.8cpr,则认为已经进入下一圈了,或者是后退到上一圈了
 int delta = raw_count - raw_count_prev;
  // if overflow happened track it as full rotation
 if(abs(delta) > (0.8*cpr) ) full_rotation_offset += delta > 0 ? -_2PI : _2PI; 
 //计算绝对角度,弧度
 float angle = full_rotation_offset + ( (float) (raw_count) / (float)cpr) * _2PI;

  //计算速度
 long now = _micros();
 float Ts = ( now - velocity_calc_timestamp)*1e-6;
  // quick fix for strange cases (micros overflow)
 if(Ts <= 0 || Ts > 0.5) Ts = 1e-3; 
 velocity = (angle - angle_prev)/Ts;

  // save variables for future pass
 raw_count_prev = raw_count;
 angle_prev = angle;
 velocity_calc_timestamp = now;

 return angle;
}

// function reading the raw counter of the magnetic sensor
int MagneticSensorAnalog::getRawCount(){
 return analogRead(pinAnalog);//AD阻塞采样
}

磁编码器的模拟值输出方式计算位置原理十分简单,只是一个AD值到角度的线性转换,需要注意的是AD的最大最小值最好自己测量一下,避免误差。另外一个需要注意的地方是getAngle()函数需要以较高的频率调用(频率跟电机转动速度相关),否则可能会导致角度计算错误,最好是使用定时器定时调用,或者在多任务系统种单独一个任务以固定周期调用该函数。

上述源码AD采样使用的是阻塞方式采样,STM32中我们可以使用中断或DMA中断方式采样,在中断函数中调用getAngle()函数,并开启下一次采样。

PWM模式

PWM模式和模拟值输出模式很相似,只是将模拟值输出的电压变成了PWM形式,PWM的高电平时间与角度成正比。

examples\utils\sensor_test\magnetic_sensors\magnetic_sensor_pwm_example\magnetic_sensor_pwm\magnetic_sensor_pwm.ino
#include <SimpleFOC.h>
/**
 * MagneticSensorPWM(uint8_t MagneticSensorPWM, int _min, int _max)
 * - pinPWM         - PWM输入引脚
 * - min_raw_count  - 最短高电平时间us
 * - max_raw_count  - 最长高电平时间us
*/
MagneticSensorPWM sensor = MagneticSensorPWM(2, 4, 904);
//定义中断回调函数
void doPWM(){sensor.handlePWM();}

void setup() {
  // monitoring port
 Serial.begin(115200);

  // initialise magnetic sensor hardware
 sensor.init();
  //使能外部中断(监测引脚上升沿下降沿)检测PWM引脚高电平时间
  //如果注释掉这一句,则以默认的阻塞方式检测PWM引脚高电平时间
 sensor.enableInterrupt(doPWM);

 Serial.println("Sensor ready");
 _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
 Serial.print(sensor.getAngle());
 Serial.print("\t");
 Serial.println(sensor.getVelocity());
}

IIC模式

AS5600可以使用IIC通信方式得到角度数据,需要注意的是,AS5600的地址是固定的,也就是说IIC总线上只能有一个AS5600。如果想要使用多个AS5600就需要多个IIC总线。

//examples\utils\sensor_test\magnetic_sensors\magnetic_sensor_i2c_example\magnetic_sensor_i2c_example.ino
#include <SimpleFOC.h>
// Example of AS5600 configuration 
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);

void setup() {
  // monitoring port
 Serial.begin(115200);

  //i2C使用快速模式400K
 Wire.setClock(400000);
  // initialise magnetic sensor hardware
 sensor.init();

 Serial.println("Sensor ready");
 _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
 Serial.print(sensor.getAngle());
 Serial.print("\t");
 Serial.println(sensor.getVelocity());
}

参考

无刷电机控制器分析(1.1)SimpleFOC位置检测传感器例程源码分析 - 知乎

### as5047p编码器AS5600的差异、兼容性和性能对比 #### 设备概述 as5047p是一款高精度角度传感器,采用磁感应技术提供绝对位置测量。该设备具有低功耗特性并支持SPI通信协议。相比之下,AS5600同样是一个基于霍尔效应的角度传感器,也提供了绝对位置检测能力,并且集成了I²C接口用于数据传输。 #### 技术参数区别 - **分辨率** - as5047p能够达到12位的有效角分辨率,在某些应用场合下可以满足更高的精度需求。 - AS5600则具备12位原始分辨率以及额外的可编程滤波选项来优化信噪比,从而提高实际有效分辨率[^3]。 - **工作电压范围** - as5047p的工作电源范围较宽泛(2.7V至5.5V),这使得它可以在更多类型的电路环境中稳定运行。 - 虽然具体数值未提及,但是通常情况下AS5600也有类似的供电灵活性,适应多种应用场景的需求[^4]。 - **接口类型** - as5047p主要依赖于SPI总线进行主机间的高效通讯。 - AS5600除了基本的数据读取外还增加了对I²C的支持,允许更简单的硬件连接方式和更低的成本设计考量[^5]。 #### 应用场景适用性 对于那些已经围绕着特定MCU平台构建起来的设计来说,如果目标微控制器拥有丰富的GPIO资源并且倾向于快速批量转移大量数据,则可以选择as5047p;而对于希望简化PCB布局或是利用较少引脚完成工作的项目而言,有I²C特性的AS5600可能是更好的选择。 #### 兼容性分析 尽管两者都是为了实现旋转运动监测而开发的产品,但由于它们各自不同的电气规格和技术特点,直接互换可能会遇到困难。然而,在软件层面调整配置文件之后,许多上层应用程序逻辑仍然保持一致,因此迁移成本相对可控。 ```python # Python伪代码展示如何初始化两个不同型号的编码器 def initialize_encoder(encoder_type): if encoder_type == "as5047p": import spidev spi = spidev.SpiDev() spi.open(bus, device) # 进一步设置spi参数... elif encoder_type == "AS5600": import smbus2 bus = smbus2.SMBus(i2c_bus_number) # 初始化i2c相关参数... initialize_encoder('as5047p') initialize_encoder('AS5600') ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值