三轴加速度计LIS2DUX12开发.2----静态校准
- 概述
- 硬件准备
- 视频教学
- 样品申请
- 源码下载
- 六位置法的标定方案
- 旋转加速度计以找到极值
- 计算偏移和灵敏度
- 应用校准参数
- 注意事项
- 串口中断
- 变量定义
- 主程序流程
- 串口发送定义
- 演示
概述
最近在弄ST和瑞萨RA的课程,需要样片的可以加群申请:615061293 。
零偏是影响加速度计输出精度的重要指标之一,零偏可分为静态零偏和动态零偏 。静态零偏也称为固定零偏,通常经标定与补偿减小静态零偏。动态零偏是由于加速度计自身的缺陷或环境因素(如温度、振动、电子干扰等)引起的,悬丝加速度计在运动过程中其精度会受到动态零偏的影响,因此在投入使用前要先对加速度计的动态零偏进行测试。
硬件准备
首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。
主控为STM32U073CC,加速度计为LIS2DUX12
视频教学
https://www.bilibili.com/video/BV17J4m1W7Fb/
三轴加速度计LIS2DUX12开发(2)----静态校准
样品申请
https://www.wjx.top/vm/OhcKxJk.aspx#
源码下载
六位置法的标定方案
本文在校准三轴加速度计时使用六位置校准法,该方法使用地球的重力力加速度在静态下校准三轴加速度传感器,具体的校准过程如下图所示。具体校准过程如下:
- 将传感器的Y轴垂直水平面向下;
- 以X轴为基准轴,绕其逆旋转90°,使乙轴垂直水平面向上
- 以Y轴为基准轴,绕其逆旋转90°,使X轴垂直水平面向下
- 以Y轴为基准轴,绕其逆时针旋转90°使2轴垂直水平面向下
- 绕Y轴逆时针旋转909、使X轴垂直水平面向上
- 绕Z轴顺时针旋转90°、使Y轴垂直水平面向上
在没有精密设备的情况下。这种方法基本上是在试图找到每个轴的偏移(zero-g offset)和灵敏度(scale factor)。这种方法通常称为静态校准方法,因为它不需要动态输入,只需将设备置于静态的已知方向即可。
旋转加速度计以找到极值
将加速度计沿每个轴正向和反向对齐,使其尽可能地与地球重力向量对齐。在理想情况下,当某一轴完全与地球的重力向量对齐时,该轴应显示约 ±1g 的读数,而其他轴应显示 0g。
记录每个轴在这六个方向(X+, X-, Y+, Y-, Z+, Z-)的输出,即每个轴的最大值和最小值。
在未校准情况下,读出的数据会超过1g的数值,所以要进行加速度计校准。
计算偏移和灵敏度
偏移(Offset):可以通过计算每个轴最大值和最小值的平均值得到:
Offset=(Max value+Min value)/2
灵敏度(Scale factor):可以通过两个极值之差与2g(因为从+1g到-1g的总变化是2g)的比例来计算:
Scale factor=(Max value-Min value)/2g
应用校准参数
一旦计算得到每个轴的偏移量和灵敏度,校准参数就可以应用到新的加速度计测量数据中以修正这些数据。修正后的加速度值由下列公式计算得出:
Calibrated value= (Raw value−Offset)/Scale Factor
这个步骤实质上是一个线性变换,它调整原始加速度读数以反映真实的加速度。
这些算法步骤基于直接的数学操作,并不涉及复杂的统计算法或优化算法。这些方法足以处理大多数基本应用场景下的加速度计校准需求,尤其是在资源受限的嵌入式系统中。如果环境变化大或加速度计的非理想特性影响较大(如高温、机械应力等),可能需要更复杂的算法来进行动态校准或更高级的误差补偿。
注意事项
- 确保在静态环境中进行测试,避免任何震动或移动。
- 使用精确的水平仪确保加速度计的对齐。
- 可以通过多次测量和取平均值来增加校准的准确性。
这种校准方法相对简单,适合大多数基本应用,但对于需要极高精度的应用,可能需要更复杂的校准技术和专业设备。
串口中断
开启串口中断来接收数据。
要在主程序钟开启中断接收。
HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuff, 1); //打开串口中断接收
定义接收函数。
// 捕获中断回调函数,每次捕获到信号就会进入这个回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle)
{
Rx_flag=RxBuff[0];
RxBuff[0]=0;
// printf("flag=%d",Rx_flag);
HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuff, 1); //每接收一个数据,就打开一次串口中断接收,否则只会接收一个数据就停止接收
}
变量定义
data_accx_min, data_accx_max, data_accy_min, data_accy_max, data_accz_min, data_accz_max:这些变量存储加速度计在X、Y、Z轴上的最小值和最大值。这些极值通常是通过在特定时间内收集加速度计数据,然后从这些数据中找出最小值和最大值得到的。
Offset_x, Offset_y, Offset_z:这些是各轴的偏移值,计算方法是各轴最大值和最小值的算术平均值。偏移值用于调整每个轴的零点位置,使其在没有运动时接近零。
Scale_factor_x, Scale_factor_y, Scale_factor_z:这些是各轴的灵敏度系数,计算方法是各轴最大值和最小值的差除以2。这个系数用于调整每个轴的测量值,使其在已知加速度的情况下反映实际的物理加速度。
calibrated_x, calibrated_y, calibrated_z:这些变量用于存储校准后的加速度值,校准的目的是确保加速度计输出准确反映实际加速度。
校准参数计算函数 calculate_calibration_params,此函数执行以下操作:
计算偏移(Offset):通过取每个轴的最大值和最小值的平均值来计算偏移。这样做是为了将未校准的加速度计读数的中心调整到0点附近,以补偿传感器的系统偏差。
计算灵敏度(Scale factor):通过取每个轴的最大值和最小值之差的一半来计算灵敏度。这个值表示了在理想条件下,传感器输出从最小到最大应覆盖的理想范围(通常是±1g)。通过这种方式,您可以根据实际的传感器响应调整加速度计的读数。
float data_accx_min=0,data_accx_max=0;//加速度计x轴极值
float data_accy_min=0,data_accy_max=0;//加速度计y轴极值
float data_accz_min=0,data_accz_max=0;//加速度计z轴极值
float Offset_x=0.0f;//x偏移
float Scale_factor_x=0.0f;//x灵敏度
float Offset_y=0.0f;//y偏移
float Scale_factor_y=0.0f;//y灵敏度
float Offset_z=0.0f;//z偏移
float Scale_factor_z=0.0f;//z灵敏度
float calibrated_x=0.0f;//校准后加速度计x轴值
float calibrated_y=0.0f;//校准后加速度计y轴值
float calibrated_z=0.0f;//校准后加速度计z轴值
int acc_i=0;
uint8_t RxBuff[1]; //进入中断接收数据的数组
int Rx_flag=0; //接受到数据标志
void calculate_calibration_params(void) {
Offset_x=(data_accx_max+data_accx_min)/2;
Offset_y=(data_accy_max+data_accy_min)/2;
Offset_z=(data_accz_max+data_accz_min)/2;
Scale_factor_x=(data_accx_max-data_accx_min)/2;
Scale_factor_y=(data_accy_max-data_accy_min)/2;
Scale_factor_z=(data_accz_max-data_accz_min)/2;
}
主程序流程
使用 lis2dux12_status_get 函数检查新的加速度计数据是否已经准备好。如果status.drdy(数据就绪标志)为真,这意味着有新数据可读。
通过调用 lis2dux12_xl_data_get 函数读取加速度计数据。这些数据被存储在 data_xl.mg 数组中,分别对应 X、Y、Z 轴的加速度值。
根据 Rx_flag 的值,更新对应轴的最小值或最大值。每次更新后,调用 calculate_calibration_params 函数重新计算校准参数。
Rx_flag == 1 和 Rx_flag == 2 分别更新 X 轴的最小和最大值。
Rx_flag == 3 和 Rx_flag == 4 分别更新 Y 轴的最小和最大值。
Rx_flag == 5 和 Rx_flag == 6 分别更新 Z 轴的最小和最大值。
使用更新后的校准参数(偏移和灵敏度)来校准读取的加速度数据。校准公式为:
Calibrated value= 1000*(Raw value−Offset)/Scale Factor
这里乘以1000是为了将结果转换为毫重力单位(mg),常用于显示加速度计的读数。
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* Read output only if new values are available */
lis2dux12_status_get(&dev_ctx, &status);
if (status.drdy) {
lis2dux12_xl_data_get(&dev_ctx, &md, &data_xl);
if(Rx_flag==1)//X轴min
{
data_accx_min=data_xl.mg[0];
Rx_flag=0;
calculate_calibration_params();
}
else if(Rx_flag==2)//X轴max
{
data_accx_max=data_xl.mg[0];
Rx_flag=0;
calculate_calibration_params();
}
else if(Rx_flag==3)//Y轴min
{
data_accy_min=data_xl.mg[1];
Rx_flag=0;
calculate_calibration_params();
}
else if(Rx_flag==4)//Y轴max
{
data_accy_max=data_xl.mg[1];
Rx_flag=0;
calculate_calibration_params();
}
else if(Rx_flag==5)//Y轴min
{
data_accz_min=data_xl.mg[2];
Rx_flag=0;
calculate_calibration_params();
}
else if(Rx_flag==6)//Y轴max
{
data_accz_max=data_xl.mg[2];
Rx_flag=0;
calculate_calibration_params();
}
calibrated_x=1000*(data_xl.mg[0]-Offset_x)/Scale_factor_x;
calibrated_y=1000*(data_xl.mg[1]-Offset_y)/Scale_factor_y;
calibrated_z=1000*(data_xl.mg[2]-Offset_z)/Scale_factor_z;
printf("min_x=%4.2f,max_x=%4.2lf,min_y=%4.2f,max_y=%4.2f,min_z=%4.2f,max_z=%4.2f\r\n",
data_accx_min,data_accx_max,data_accy_min,data_accy_max,data_accz_min,data_accz_max);
printf("校准前acc[mg]:%4.2f\t%4.2f\t%4.2f\r\n",
data_xl.mg[0], data_xl.mg[1], data_xl.mg[2]);
printf("校准后acc[mg]:%4.2f\t%4.2f\t%4.2f\r\n",
calibrated_x, calibrated_y, calibrated_z);
HAL_Delay(10);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */