具有 ASEK DLL 谐波线性化功能的先进编程算法
具有 ASEK DLL 谐波线性化功能的先进编程算法
作者:K. Robert Bate,
Allegro MicroSystems, LLC
介绍
无论是工业自动化和机器人技术,还是电动助力转向和电机位置传感,许多应用都需要监测旋转轴(以轴上或离轴排列形式)的角度。
在设计中使用磁体时,磁场输入在整个旋转范围很可能不均匀,它存在固有误差。这些磁场输入误差会导致系统内的测量误差。线性化能减少这些输入误差。
A1332和A1335可采用的谐波线性化能以最多 15 种修正谐波的形式应用线性化,利用快速傅立叶变换 (FFT) 可确定它们的相位和振幅,根据磁体在角度传感器 IC 周围的一次旋转产生的数据可完成 FFT。通过使用 Allegro 提供的软件计算系数,并对片内 EEPROM 进行编程,可使用这种技术。本应用说明介绍了,当 Allegro 提供的软件不够灵活或要使用定制软件时,客户能使用的功能和处理流程。
编程要求
所有软件都是在使用 .NET 4.0 的 Microsoft Visual Studio 2010 环境下开发的。请为您要使用的器件下载命令库 (C#/.NET),并添加到其所含的 3 个 DLL 的项目参考中。
收集数据
首先,关闭所有后线性化算法处理;这包括零点偏移、后线性化旋转 (RO)、短行程反转 (IV) 和旋转晶片位元 (RD)。预线性化调节可保持开启,如 ORATE 设置、IIR 过滤器 (FI) 和预线性化旋转 (LR)。
沿角度增加的位置移动编码器。如果角度传感器集成电路的输出未相应增大,可设置LR位元以反转角度传感器 IC 输出的反向,或在校准时沿反方向转动编码器,在此情况下,可能需要设置后线性化旋转位元 (RO)。参阅《A1332/ A1335 编程参考》了解更多详情。
最佳收集方法是按间距相等的步数旋转目标,这样产生的数据点数量就是 2 的幂数。通常,32 或 64 个间距均匀的数据点就足够了。如果不能实现,可收集数据点,然后必须按下节介绍的方法预处理数据。
另一种收集所需数据点的方法是多次旋转目标,然后按预定义的间隔收集数据。当收集到足够的数据点覆盖目标的整个旋转范围时,接下来必须按下节介绍的方法预处理数据。
预处理数据
如果收集的数据点数量不是 2 的幂数,或者收集的数据点间距不等,必须调整数据点数组的长度并/或使它们间距相等。要对数据执行此操作,可调用 ResizePointArray 例程。
参数 x 是编码器数值的数组,参数 y 是在该编码器数值中收集的器件读数。参数 newSize 是重新调整的数组大小。如果参数 x 设置为空,则假设已按从 0 开始至 360 结束的相等间距收集数值 y。如果参数 x 不是空,则需要在调整数组大小前,为输入数组排序。
double[] ResizePointArray(double[] x, double[] y, int newSize)
此例程会在输入数组上执行三次样条插值,以采用所需的数据点数量,生成间距相等的数组。
初始处理
数据收集完毕,并形成长度为 2 的幂数的数组后,就可以计算谐波系数了。要计算谐波系数,可调用 CalculateHarmonicLinearCoefficients 例程。
HarmonicCoefficients[] CalculateHarmonicLinearCoefficients (double[] points, out bool pointError)
其输入是已收集的角度数组。此例程会执行 FFT,并会返回系数数组和一个警告标记。当一个或多个输入角比例程计算的角度大 20 度时,需要设置点误差警告标记。
以一个包含 8 个输入项的数组为例,例程计算的角度应为 [0, 45, 90, 135, 180, 225, 270, 315]。如果输入数组是 [0, 45, 90, 135, 180, 204, 270, 315],则例程会设置 pointError,因为第 6 个输入项的误差超过 20 度。
选择谐波
当所有谐波系数已计算完毕后,必须选择所需的谐波。通常,计算例程生成的谐波数量会超过器件能支持的谐波数量,所以,必须选择一些算法以选择相关的谐波。
使用谐波的数量还取决于所用的器件种类和功能。A1332 的谐波最大数量是 15,但如果使用最大值,一些可编程的功能会使用默认值,如短行程设置和特定的 I2C 与 SPI 设置。不使用默认值时,这些可编程功能的谐波最大数量是 9。A1335 的谐波最大数量是 11,但要达到此数量,一些可编程功能会使用默认值,如短行程设置。不使用默认值时,这些可编程功能的谐波最大数量是 8。
最简单的算法是按照所需的谐波数量选择第一个谐波。这种方法很简单,它选择的谐波不会对输出产生显著的影响。
Allegro A1335 的示例编程器目前使用的算法是选择振幅大于 0.3 的谐波。需要注意的是,当前软件的一个限制是在所选谐波之间只能跳过 4 个谐波。如果跳过的谐波超过 4 个,还需要选择最后一个选定谐波和所需谐波之间的所有谐波。
器件编程
谐波选择完毕后,可调用例程 GenerateHarmonicLinearizationDeviceValues 生成要写入器件的数值。
HarmonicDeviceValues[] GenerateHarmonicLinearizationDeviceValues (HarmonicCoefficients[] coefficients)
谐波系数传递到此例程中,它会返回器件编程所需的一组数值。此例程抛出的唯一异常是在所选系数之间跳过 4 个以上谐波系数的情形。
要对器件进行谐波线性化编程,必须设置 HL 标记,必须将 HAR_MAX 字段设置为要使用的系数数量,同时必须编写 HARMONIC_PHASE_n、ADV_n 和 HARMONIC_AMPLITUDE_n 字段。
代码实例
using System;
using Allegro.ASEK;
名称空间HarmonicLinearizationExample
{
public class HarmonicLinearizationExample
{
public HarmonicLinearizationExample()
{
}
public void ProgramHarmonicLinearization(string filePath, ASEK asekProgrammer)
{
try
{
HarmonicCoefficients[] hc;
bool pointError = false;
double[] points = null;
string fieldBuffer = File.ReadAllText(filePath);
string line;
List
List
// 1.1 收集数据
// 从文本文件中读取角度。忽略空白行或以 a # 打头的行。
if (!string.IsNullOrEmpty(fieldBuffer))
{
using (StringReader sr = new StringReader(fieldBuffer))
{
while ((line = sr.ReadLine()) != null)
{
line = line.Trim();
if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
{
continue;
}
// 每行可采用 2 种格式中的 1 种。
// 首行包含编码器角度,其次是逗号隔开的器件角度 (22.125,23.543)
// 或只有器件角度 (23.543)
// 如果角度的间距不等,则两个数值都需要。
string[] values = line.Split(‘,’);
if (values.Length > 1)
{
double encoder = Convert.ToDouble(values[0]);
while (encoder >= 360.0)
{
encoder -= 360.0;
}
while (encoder < 0.0)
{
encoder += 360.0;
}
encoderReadings.Add(encoder);
deviceReadings.Add(Convert.ToDouble(values[1]));
}
else
{
deviceReadings.Add(Convert.ToDouble(values[0]));
}
}
}
// 1.2 预处理数据
if (!powerOfTwo(deviceReadings.Count()))
{
// 如果数据点数量差一个,只删除最后一个,
if (powerOfTwo(deviceReadings.Count() - 1))
{
deviceReadings.RemoveAt(deviceReadings.Count() - 1);
points = deviceReadings.ToArray();
}
else
{
// 否则应计算所需的样本数量。
// 如果样本数量小于 64,
// 可四舍五入至最接近 2 的幂数的数值,否则应向下取整数。
int desiredSamples = 8;
while (desiredSamples < deviceReadings.Count())
{
desiredSamples *= 2;
}
if (deviceReadings.Count() > 64)
{
desiredSamples /= 2;
}
// 如果没有编码器读数,可假设器件读数的间距相等。
如果 (encoderReadings.Count() != deviceReadings.Count())
{
// 将角度列表转换为数组,然后调整其长度。
points = ((IHarmonicLinearization)asekProgrammer).ResizePointArray(null, deviceReadings.ToArray(), desiredSamples);
}
else
{
// 将角度列表转换为数组,然后调整其长度。
points = ((IHarmonicLinearization)asekProgrammer).ResizePointArray(encoderReadings.ToArray(), deviceReadings.ToArray(), desiredSamples);
}
}
}
else
{
// 将角度列表转换为数组
points = deviceReadings.ToArray();
}
// 1.3 初始处理
// 通过数据点数组计算系数。
hc = ((IHarmonicLinearization)asekProgrammer).CalculateHarmonicLinearCoefficients(points, out pointError);
// 当一个或多个入射角比例程的计算结果大 20 度时,就会出现数据点误差。
// 以包含 8 个数值的数组为例 [0, 45, 90, 135, 180, 204, 270, 315],计算例程会为
// 第 6 个输入项发放警告标记,因为它应该接近 225。
if (pointError)
{
MessageBox.Show("其中一个入射角比预期值大 20 度。");
}
// 1.4 选择谐波
// 一组谐波系数计算完毕后,需要选择系数。计算例程
//返回的系数数量通常会超过器件能支持的系数数量,因此需要
// 需要使用一些限制系数数量的方法。可以先选出前 8 个,也可使用其他方法。
int numberOfHarmonicComponents = hc.Length;
int numberOfSelectedHarmonicComponents = 0;
int lastHarmonicComponentSelected = 0;
int maxHarmonicComponentsSelected = 8; // 在影响器件的其他功能前,可使用谐波的最大数量
// 就此例而言,选择前 8 个振幅超过 0.3 的谐波。
for (int index = 0; index < numberOfHarmonicComponents; ++index)
{
if ((hc[index].amplitude > 0.3) && (numberOfSelectedHarmonicComponents < maxHarmonicComponentsSelected))
{
// 如果要选择的谐波和选择的最后一个谐波
// 之间的谐波数量大于 4,还需要
// 选择它们之间的一些谐波。
int skip = index - lastHarmonicComponentSelected;
if (skip > 4)
{
// 确保要选择的谐波数量
// 不超过所需的谐波数量。
int numberNeeded = skip / 4;
if ((numberNeeded + numberOfSelectedHarmonicComponents) <= maxHarmonicComponentsSelected)
{
for (int jndex = 1; jndex <= numberNeeded; ++jndex)
{
hc[jndex].select = true;
++numberOfSelectedHarmonicComponents;
}
hc[index].select = true;
++numberOfSelectedHarmonicComponents;
}
else
{
// 代码在选择所需的谐波时,超过
// 所选系数的最大数量,因此它会停止选择。
break;
}
}
else
{
hc[index].select = true;
++numberOfSelectedHarmonicComponents;
}
lastHarmonicComponentSelected = index;
}
}
// 如果未选择谐波,可选择前 8 个。
if (numberOfSelectedHarmonicComponents == 0)
{
for (int i = 0; (i < numberOfHarmonicComponents) && (numberOfSelectedHarmonicComponents < 8); ++i)
{
hc[i].select = true;
++numberOfSelectedHarmonicComponents;
}
}
// 1.5 器件编程
// 生成需要写入 eeprom 的数值。
HarmonicDeviceValues[] eepromValues = ((IHarmonicLinearization)asekProgrammer).GenerateHarmonicLinearizationDeviceValues(hc);
// 确保器件通电
asekProgrammer.SetVcc(5.0);
// 确保写入器件的 eeprom,这需要使 SRAM 具备可写性,并关停处理器
((ISRAMWriteAccessMode)asekProgrammer).SetSRAMWriteAccessMode();
((IProcessorMode)asekProgrammer).SetProcessorIdle();
// 在 eeprom 中开启谐波线性化
((IRegisterAccess)asekProgrammer).WritePartialRegister(MemoryAccessType.extended, 0x306, 1, 15, 15); // HL = 1
// 设置要使用的谐波数量
((IRegisterAccess)asekProgrammer).WritePartialRegister(MemoryAccessType.extended, 0x309, eepromValues.Length, 19, 16); // HAR_MAX = 谐波数量
// For the harmonics
for (uint index = 0; index < eepromValues.Length; ++index)
{
uint registerValue = (uint)(((eepromValues[index].phase << 12) & 0x0FFF000) +
((eepromValues[index].advance << 10) & 0x0C00) +
(eepromValues[index].amplitude & 0x03FF));
((IRegisterAccess)asekProgrammer).WriteRegister(MemoryAccessType.extended, 0x30C + index, registerValue); // HARMONIC_PHASE, ADV and HARMONIC_AMPLITUDE
}
// 关闭电源,然后再接通,确保器件使用新的线性化数值。
asekProgrammer.SetVccOff();
asekProgrammer.SetVcc(5.0);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private bool powerOfTwo(int value)
{
int log2npoints = 0;
int j = value;
while ((j > 0) && ((j & 1) == 0)) // 计算输入值的对数底数 2
{
log2npoints++;
j >>= 1;
}
if ((value < 2) || (value != (1 << log2npoints)))
{
return false;
}
return true;
}
}
}
角度输入文件的格式
此文件包含一个角度值列表。如果有两个数值被逗号隔开,则第一个数值是编码器角度,第二个数值是器件角度。行可以是空白的,如果它们以 # 打头,则可将其视为注释。
角度输入文件实例:
329.59
354.81
6.832
13.566
17.592
20.228
22.638
24.638
25.956
27.454
28.77
30.054
30.966
包含两列时:
0,123
22.5,145.5
45,168
67.5,190.5
90,213
112.5,235.5
135,258
157.5,280.5
180,303
202.5,325.5
225,348
247.5,10.5
270,33
292.5,55.5
315,78
337.5,100.5