惯导算法讲解

对数据的获取进行了滤波和减去零飘的处理。

```c
void ICM_getValues() {
    // 一阶低通滤波,单位g/s
    icm_data_acc_x = (((float)imu660ra_acc_x) * alpha) + icm_data_acc_x * (1 - alpha);
    icm_data_acc_y = (((float)imu660ra_acc_y) * alpha) + icm_data_acc_y * (1 - alpha);
    icm_data_acc_z = (((float)imu660ra_acc_z) * alpha) + icm_data_acc_z * (1 - alpha);
    // 陀螺仪角速度转弧度
    icm_data_gyro_x = ((float)imu660ra_gyro_x - GyroOffset_Xdata) * M_PI / 180 / 16.4f;
    icm_data_gyro_y = ((float)imu660ra_gyro_y - GyroOffset_Ydata) * M_PI / 180 / 16.4f;
    icm_data_gyro_z = ((float)imu660ra_gyro_z - GyroOffset_Zdata) * M_PI / 180 / 16.4f;
}
```

`GyroOffset`的获取很简单,取车辆静止不动的一段时间内的数据平均值即可。

角度解算的数据获取中断周期为2MS。

速度编码器的数据获取中断周期为20MS。

通过四元数最后的到了车辆的偏航角。

# 2.车辆自身坐标获取

我们假设有两个按键,一个按键决定了车辆当前坐标系,一个按键决定了发车与否。

那我们将车放在车库中心,按下第一个按键,调用以下函数

```c
void Inertial_Navigation_Start(void) {
    //设置当前坐标,抓取当前坐标系为基准坐标系
    navigation.start_yaw = eulerAngle_yaw_total;
    navigation.x_cur = 0;
    navigation.y_cur = 0;
    navigation.x_start = 0 * 1; //mm
    navigation.y_start = 0 * 1; //mm
    navigation.x_set = 0 * 1;   //mm
    navigation.y_set = 0 * 1;   //mm
}
```
即可获得一个参考二维坐标系,这个坐标系以车库为中心,出车库右侧为X轴正方向,车辆前方为Y轴正方向。我们在中断中进行以下计算,周期为2MS。
```c
navigation.cur_yaw = eulerAngle_yaw_total - navigation.start_yaw;
navigation.x_cur = navigation.x_cur + sin(navigation.cur_yaw / 180 * 3.14159f) * (encoder_bdc / 150 * 50 * 0.002);
navigation.y_cur = navigation.y_cur + cos(navigation.cur_yaw / 180 * 3.14159f) * (encoder_bdc / 150 * 50 * 0.002);
```

我们车轮子行进一米大概会让编码器产生150000个脉冲,以20ms为编码器速度获取周期进行换算,得到的`x_cur`,`y_cur`即为车辆的坐标。

`eulerAngle_yaw_total`为yaw轴偏航累计角度,因为直接读取姿态解算的yaw值的话,它会有-180~180的限制,且在边界会出现跳变,故采用以下代码进行`eulerAngle_yaw_total`值的获取。

```c
float i = eulerAngle_yaw - eulerAngle_yaw_old;
if (i < -180) {
    i += 360;
}
else if (i > 180) {
    i -= 360;
}
eulerAngle_yaw_total += i;
eulerAngle_yaw_old = eulerAngle_yaw;
```

# 3.车库(目标点)方向计算

这部分思路很简单,既然得到了车辆自身的坐标,那么只需要进行atan计算即可获取车辆所需的偏航角。但是需要注意的是atan的返回值在-45~45°之间,需要我们针对车辆坐标系进行判断后才能知道准确的-180~180之间的偏航角。

```c
if (navigation.y_set - navigation.y_cur < 0 && navigation.x_set - navigation.x_cur > 0) {
    navigation.inertial_navigation_set_yaw_update = atan((navigation.x_set - navigation.x_cur) / (navigation.y_set - navigation.y_cur)) / 3.14159f * 180 + 180;
}
else if (navigation.y_set - navigation.y_cur < 0 && navigation.x_set - navigation.x_cur < 0) {
    navigation.inertial_navigation_set_yaw_update = atan((navigation.x_set - navigation.x_cur) / (navigation.y_set - navigation.y_cur)) / 3.14159f * 180 - 180;
}
else {
    navigation.inertial_navigation_set_yaw_update = atan((navigation.x_set - navigation.x_cur) / (navigation.y_set - navigation.y_cur)) / 3.14159f * 180;
}
```

获取到```navigation.inertial_navigation_set_yaw_update```后进行类似偏航累计的处理

```c
float i = navigation.inertial_navigation_set_yaw_update - navigation.inertial_navigation_set_yaw_old;
while (i > 180) {
    i = i - 360;
    if (i < 180) {
        break;
    }
}
while (i < -180) {
    i = i + 360;
    if (i > -180) {
        break;
    }
}
```

至此我们得到了目标偏航角的累加。我们再取一次余。

```c
if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw > 360) {
    while (1) {
        navigation.inertial_navigation_set_yaw_up_total -= 360;
        if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw < 360) {
            break;
        }
    }
}
else if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw < -360) {
    while (1) {
        navigation.inertial_navigation_set_yaw_up_total += 360;
        if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw > -360) {
            break;
        }
    }
}
```

然后我们车辆在惯导回车库的时候,实际上不需要车头朝向车库,旋转最近的旋转角度,使车头或者车尾朝向车库即可。

```c
if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw < 90 && navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw > -90) {
    speed_reversal_flag = 0;
}
if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw > 90) {
    while (1) {
        //
        navigation.inertial_navigation_set_yaw_up_total -= 180;
        if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw < 90) {
            speed_reversal_flag = 1;
            break;
        }
    }
}
if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw < -90) {
    while (1) {
        navigation.inertial_navigation_set_yaw_up_total += 180;
        if (navigation.inertial_navigation_set_yaw_up_total - navigation.cur_yaw > -90) {
            speed_reversal_flag = 1;
            break;
        }
    }
}
```

在speed_reversal_flag == 1时,我们在行进轮速度控制的时候使车辆倒车即可。

# 4.回库流程

在出断路后,我们将pid计算中的角度环的偏差替换为`navigation.cur_yaw - navigation.inertial_navigation_set_yaw`,其中`navigation.inertial_navigation_set_yaw`为经过了斜坡函数获得的值。

```c
if (navigation.inertial_navigation_set_yaw < navigation.inertial_navigation_set_yaw_up_total) {
    navigation.inertial_navigation_set_yaw += 0.04f;
}
else if (navigation.inertial_navigation_set_yaw > navigation.inertial_navigation_set_yaw_up_total) {
    navigation.inertial_navigation_set_yaw -= 0.04f;
}
```

我们车辆基础性能有限,只能进行了这样的处理,希望大家可以多多交流控制方面的思路,互相进步。



在回库过程中,我们只需要设置目标值`navigation.x_set`与`navigation.y_set`即可让车回到设定点位。  

我们组先将车开回(0,0),再设置到(0,x),x值为一个常量,为计时器的触发地点,随后再将目标值设定为(0,0),车辆到达后,进行关闭输出处理。

需要注意的是,因为无法精确地到达目标点,我们可以在到达目标点半径一定范围后就将状态机切换为下一个状态。

更多参考paper佬的开源https://github.com/paper-tei/smart_car

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
捷联惯导(Inertial Navigation System,INS)是一种基于惯性测量单元(Inertial Measurement Unit,IMU)的导航系统,可以在没有GPS信号或其他外部引导的情况下提供航向、俯仰和偏航信息。 以下是一个简单的C语言代码示例,用于实现基于加速度计和陀螺仪的捷联惯导系统: ```c #include <stdio.h> #include <math.h> #define PI 3.14159265 // IMU读取的加速度和角速度数据 double ax, ay, az; // 加速度计 double wx, wy, wz; // 陀螺仪 // 姿态角 double roll, pitch, yaw; // 时间间隔 double dt = 0.01; int main() { // 初始化 roll = 0.0; pitch = 0.0; yaw = 0.0; // 循环更新姿态角 while (1) { // 读取IMU数据 read_imu_data(); // 计算加速度和角速度的矢量值 double accel_mag = sqrt(ax * ax + ay * ay + az * az); double gyro_mag = sqrt(wx * wx + wy * wy + wz * wz); // 计算加速度和角速度的角度 double accel_angle_x = asin(ax / accel_mag) * 180.0 / PI; double accel_angle_y = asin(ay / accel_mag) * 180.0 / PI; double accel_angle_z = asin(az / accel_mag) * 180.0 / PI; double gyro_angle_x = wx * dt; double gyro_angle_y = wy * dt; double gyro_angle_z = wz * dt; // 融合加速度和角速度的角度 roll = 0.98 * (roll + gyro_angle_x) + 0.02 * accel_angle_x; pitch = 0.98 * (pitch + gyro_angle_y) + 0.02 * accel_angle_y; yaw = 0.98 * (yaw + gyro_angle_z) + 0.02 * accel_angle_z; // 输出姿态角 printf("Roll: %lf, Pitch: %lf, Yaw: %lf\n", roll, pitch, yaw); } return 0; } void read_imu_data() { // TODO: 读取IMU数据并更新ax, ay, az, wx, wy, wz变量 } ``` 需要注意的是,上述代码仅为示例,实际应用中需要根据具体的硬件和算法进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值