Corrección de distorsión de movimiento de la nube de puntos láser mediante IMU

Corrección de distorsión de movimiento de la nube de puntos láser mediante IMU

1. Principios del pensamiento

En el proceso de posicionamiento y mapeo de SLAM láser, cuando el lidar se mueve lentamente y no tiene una rotación relativamente grande, la distorsión de movimiento de la nube de puntos se puede ignorar y la posición del radar aún se puede estimar mediante el registro ICP durante la comparación cuadro a cuadro. Sin embargo, cuando el rango de movimiento lidar es relativamente grande, se debe considerar la distorsión de movimiento de la nube de puntos láser, y la información de estimación de pose a corto plazo proporcionada por la IMU o el codificador se puede usar para eliminar la distorsión de movimiento de la nube de puntos. Aquí utilizo la IMU para eliminar la distorsión de movimiento de la nube de puntos. La salida integral de información de aceleración de la IMU es muy divergente. Si es un dispositivo IMU peor, la información de aceleración no se utilizará. Aquí, se considera la información de velocidad angular de la IMU. En el tiempo de la nube de puntos de 1 cuadro, la velocidad angular de todas las IMU se integra para obtener el ángulo de rotación de la IMU, y luego se convierte al lidar, y la interpolación lineal esférica del cuaternión de actitud se realiza en la actitud del radar para convertir la nube de puntos correspondiente a la actitud del estado final para eliminar la distorsión del movimiento de la nube de puntos.

2. Proceso de operación

Equipamiento usado:

  • Lidar: Velodyne VLP16, frecuencia 10HZ, rotación mecánica, 16 líneas, en dirección vertical ( − 1 5 ∘ , 1 5 ∘ ) (-15^{\circ} , 15^{\circ} )( 1 5 ,1 5 ), el ángulo entre cada línea es2 ∘ 2^{\circ}2 , hay 1800 puntos láser en el plano horizontal, la resolución promedioes 0. 2 ∘ 0.2^{\circ}0 _ 2
  • IMU: D435i, la frecuencia es de 200 HZ.

proceso específico:

  1. Integración de velocidad angular para la IMU : la integración de la velocidad angular se refiere al proceso de preintegración de VINS-Mono, y la frecuencia del lidar es mucho más baja que la frecuencia de la IMU. En un ciclo de datos de una nube de puntos (100 ms), la IMU generará alrededor de 200 ÷ 10 = 20 200{\div} 10 = 202 0 0 ÷ 1 0=2 0个,根据公式:
    α t = ω t 0 + ω t 2 ⋅ dtdt = t − t 0 α = ∑ i = 0 n − 1 α i \alpha_{t} = \frac{\omega_{t_0} + \omega_{t}}{2}\cdot dt \newline dt = t - t_0 \\ \alpha = \sum_{i =0}^{n-1} \alpha_iat=2Vayat0+Vayatdt _dt _=tt0a=yo = 0n 1ayo
    donde α t \alpha_{t}atpara ttEl tiempo t es relativo al tiempo anteriort 0 t_0t0El cambio de ángulo de ω t 0 , ω t \omega_{t_0} , \omega_{t}Vayat0,Vayatson la velocidad angular en el momento correspondiente, nnn Datos de la IMU. De esta forma, el ángulo de rotación de la IMU en un ciclo se puede obtener integrando, acumulando e iterando continuamente.

    Aquí, se adopta el método de cálculo de cuaternión. Cuaternión q = [ cos ( θ 2 ) sen ( θ 2 ) ⋅ r ] = [ wxyz ] q=\begin{bmatrix} cos(\frac{\theta}{2} ) \\ sin(\frac{\theta}{2})\cdot r \end{bmatrix} = \begin{bmatrix} w \\ x \ \y\\z\end{bmatrix}q=[cos ( _ _2i)pecado ( _ _2i)r]=wXyz, para datos IMU adyacentes θ 2 \frac{\theta}{2}2iTodos tienden a 0, equivalente infinitesimal disponible
    lim ⁡ dog → 0 sin (dog) dog = 1 lim ⁡ dog → 0 cos (dog) = 1 \lim_{dog\to 0} \frac{sin(dog)}{dog} = 1 \\ \lim_{dog\to 0} cos(dog) = 1perro 0límiteperros en ( perro ) _=1perro 0límitecos ( perro ) _ _=1
    则有:
    qt = qt 0 ⋅ qdtqdt = ( 1 , α x 2 , α y 2 , α z 2 ) q_t = q_{t_0}\cdot q_{dt} \\ q_{dt} = (1, \frac{\alpha_x}{2} , \frac{\alpha_y}{2}, \frac{\alpha_z}{2})qt=qt0qdt _qdt _=( 1 ,2ax,2atu,2az)
    dondeqt q_tqtActitud cuaternión en el momento, qt 0 q_{t_0}qt0es el cuaternión de actitud en el momento anterior, qdt q_{dt}qdt _es el valor de cambio del cuaternión en este período de tiempo, α x \alpha_xax, α y \alpha_yatu, αz \alpha_zazes el cambio de ángulo de tres ejes XYZ correspondiente.

    Adjuntar parte del código de ROS+Eigen

    std::vector<sensor_msgs::Imu::ConstPtr> imu_msg_vector;
    std::mutex mutex_lock;
    
    // IMU消息回调存储
    void imu_cb(const sensor_msgs::Imu::ConstPtr &imu_msg)
    {
          
          
        mutex_lock.lock();
        imu_msg_vector.push_back(imu_msg);
        mutex_lock.unlock();
    }
    
    // IMU角度积分
    Eigen::Quaterniond imu_q = Eigen::Quaterniond::Identity(); // R
    Eigen::Vector3d angular_velocity_1;
    angular_velocity_1 << imu_msg_v[0]->angular_velocity.x, imu_msg_v[0]->angular_velocity.y, imu_msg_v[0]->angular_velocity.z;
    double lastest_time = imu_msg_v[0]->header.stamp.toSec();
    for (int i = 1; i < imu_msg_v.size(); i++)
    {
          
          
        double t = imu_msg_v[i]->header.stamp.toSec();
        double dt = t - lastest_time;
        lastest_time = t;
        Eigen::Vector3d angular_velocity_2;
        angular_velocity_2 << imu_msg_v[i]->angular_velocity.x, imu_msg_v[i]->angular_velocity.y, imu_msg_v[i]->angular_velocity.z;
        Eigen::Vector3d aver_angular_vel = (angular_velocity_1 + angular_velocity_2) / 2.0;
        imu_q = imu_q * Eigen::Quaterniond(1, 0.5 * aver_angular_vel(0) * dt, 0.5 * aver_angular_vel(1) * dt, 0.5 * aver_angular_vel(2) * dt);
    }
    
  2. Transformación de actitud IMU->radar : El cambio de ángulo de actitud de la IMU medido arriba debe asignarse al sistema de coordenadas LIDAR. La implementación necesita conocer la matriz de transformación de transformación de parámetros externos de lidar e IMU (no se usa el vector de traducción, pero debe usarse la matriz de rotación), R lidar IMU R_{lidar}^{IMU}Rl i d a ryo soy tuEs la matriz de rotación de lidar a IMU (debe calibrarse con anticipación). Durante el movimiento de lidar e IMU, se puede considerar como R lidar IMU R_{lidar}^{IMU}Rl i d a ryo soy tuInmutable. Relacionado:
    R lidar 0 lidar 1 ⋅ R lidar IMU = R lidar IMU ⋅ RIMU 0 IMU 1 R_{lidar_0}^{lidar_1} \cdot R_{lidar}^{IMU} = R_{lidar}^{IMU} \cdot R_{IMU_0}^{IMU_1}Rl i d a r0l i d a r1Rl i d a ryo soy tu=Rl i d a ryo soy tuRyo soy tu0yo soy tu1
    其中R lidar 0 lidar 1 R_{lidar_0}^{lidar_1}Rl i d a r0l i d a r1Es la transformación de actitud desde el estado inicial hasta el estado final del radar, $R_{IMU_0}^{IMU_1} es la transformación de actitud desde el estado inicial hasta el estado final de la IMU (es decir, el cuaternión del ángulo de actitud de la IMU integrado anteriormente es la transformación de actitud desde el estado inicial hasta el estado final de la IMU (es decir, el cuaternión del ángulo de actitud de la IMU integrado anteriormente)Es la transformación de actitud del estado inicial al estado final de I M U ( es decir, el cuaternión del ángulo de actitud I M U q$ integrado arriba ) . Multiplique la fórmula por la inversa al mismo tiempo, podemos obtener:
    R lidar 0 lidar 1 = R lidar IMU ⋅ RIMU 0 IMU 1 ⋅ R lidar IMU − 1 R_{lidar_0}^{lidar_1} = R_{lidar}^{IMU} \cdot R_{IMU_0}^{IMU_1} \cdot {R_{lidar}^{I MU }}^{-1}Rl i d a r0l i d a r1=Rl i d a ryo soy tuRyo soy tu0yo soy tu1Rl i d a ryo soy tu1
    código propio:

    Eigen::Matrix3d lidar_imu_R << -1,     -1.22465e-16,            0,
              1.22465e-16,          -1,            0,
               0,            0,           1;     // lidar->IMU 外参数旋转矩阵
    Eigen::Matrix3d lidar_R;
    lidar_R = lidar_imu_R * (imu_q.toRotationMatrix()) * lidar_imu_R.inverse();
    

    El efecto es el siguiente:

  3. Realice la conversión de interpolación lineal esférica en el cuaternión de actitud del radar : en un ciclo (100 ms), el radar gira en el sentido de las agujas del reloj en la vista superior y el ángulo horizontal es el siguiente:
    inserte la descripción de la imagen aquí

    En un plano horizontal de 360 ​​grados, dividido en 1800 partes, primero calcula el ángulo θ i \theta_i correspondiente a cada nube de puntosiyo:
    θ i = arccos ( xixi 2 + yi 2 ) , si yi < 0 θ i = 2 π − arccos ( xixi 2 + yi 2 ) , si yi > 0 \theta_i = arccos\left ( \frac{x_i}{\sqrt{x_i^2 + y_i^2}} \right ), si \ y_i < 0 \\ \theta_i = 2\pi - arccos\left (\frac{x_i}{\sqrt{x_i^2 + y_i^2}} \right ),si \y_i > 0iyo=arcos _ _ _ _ _(Xi2+yi2 Xyo),yo si _ yo<0iyo=14:00 _arcos _ _ _ _ _(Xi2+yi2 Xyo),yo si _ yo>0
    再进行插值,注意是将当前时刻的点云转换到末状态雷达姿态上:
    q s l e r p = S l e r p ( q 0 , q 1 ; t ) = q 0 ( q 0 − 1 q 1 ) t = q 1 ( q 1 − 1 q 0 ) 1 − t = ( q 0 q 1 − 1 ) 1 − t q 1 = ( q 1 q 0 − 1 ) t q 0 q_{slerp}={Slerp}\left(q_{0}, q_{1} ; t\right)=q_{0}\left(q_{0}^{-1} q_{1}\right)^{t}=q_{1}\left(q_{1}^{-1} q_{0}\right)^{1-t}=\left(q_{0} q_{1}^{-1}\right)^{1-t} q_{1}=\left(q_{1} q_{0}^{-1}\right)^{t} q_{0} qs l e r p=S l e r p( q0,q1;t )=q0( q0 1q1)t=q1( q1 1q0)1 - t=( q0q1 1)1 - tq1=( q1q0 1)tq0
    donde q 0 q_0q0es el cuaternión inicial, q 1 q_1q1es el cuaternión de estado final, ttt es el tamaño de paso de la interpolación,t ∈ [ 0 , 1 ] t\in [0,1]t[ 0 ,1 ]

    El cuaternión qslerp q_{slerp}qs l e r pConvertir a matriz de rotación R slerp R_{slerp}Rs l e r pLuego, la forma puede usar la fórmula de transformación de coordenadas: P
    1 = [ x 1 y 1 z 1 1 ] = T slerp ⋅ P 0 = [ R slerp 0 0 1 ] ⋅ [ x 0 y 0 z 0 1 ] P_1 = \begin{bmatrix} x_1 \\ y_1 \\ z_1 \\ 1 \end{bmatrix} = T_ {slerp} \ cdot P_0 = \begin{bmatrix} R_{slerp} & \boldsymbol 0 \\ \boldsymbol 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x_0 \\ y_0 \\ z_0 \\ 1 \end{bmatrix}PAG1=X1y1z11=Ts l e r pPAG0=[Rs l e r p001]X0y0z01
    donde P 0 P_0PAG0son las coordenadas originales de la nube de puntos, P 1 P_1PAG1son las coordenadas de la nube de puntos convertida.

    Adjunte parte del código Eigen:

    pcl::PointCloud<pcl::PointXYZI> cloud_undistorted;
    pcl::copyPointCloud(cloud_in, cloud_undistorted);
    
    for (int i = 0; i < cloud_undistorted.points.size(); i++)
    {
          
          
        double horizon_angle; // 化为角度为单位
        if (cloud_rgb.points[i].y < 0)
        {
          
          
            double theta = acos(cloud_undistorted.points[i].x / sqrt(cloud_undistorted.points[i].x * cloud_undistorted.points[i].x + cloud_undistorted.points[i].y * cloud_undistorted.points[i].y));
            horizon_angle = theta * 180.0 / M_PI;
        }
        else
        {
          
          
            double theta = acos(cloud_undistorted.points[i].x / sqrt(cloud_undistorted.points[i].x * cloud_undistorted.points[i].x + cloud_undistorted.points[i].y * cloud_undistorted.points[i].y));
            horizon_angle = (2 * M_PI - theta) * 180 / M_PI;
        }
        int id = int(horizon_angle / 360.0 * 1800);
        // std::cout << "horizon_angle: " << horizon_angle << ", id: " << id << std::endl;
    
        Eigen::Quaterniond q_slerp = lidar_q0.slerp((1.0 - horizon_angle / 360.0), lidar_q);
        Eigen::Matrix3d R_slerp = q_slerp.toRotationMatrix();
        Eigen::Matrix4d T_lidar_slerp;
        T_lidar_slerp << R_slerp(0, 0), R_slerp(0, 1), R_slerp(0, 2), 0,
            R_slerp(1, 0), R_slerp(1, 1), R_slerp(1, 2), 0,
            R_slerp(2, 0), R_slerp(2, 1), R_slerp(2, 2), 0,
            0, 0, 0, 1;
        Eigen::Vector4d Pi, Pj;
        Pi << cloud_undistorted.points[i].x, cloud_undistorted.points[i].y, cloud_undistorted.points[i].z, 1;
        Pj = T_lidar_slerp.inverse() * Pi;
        cloud_undistorted.points[i].x = Pj(0);
        cloud_undistorted.points[i].y = Pj(1);
        cloud_undistorted.points[i].z = Pj(2);
    }
    
  4. Efecto:

    Efecto de procesamiento de nube de puntos de fotograma único:
    inserte la descripción de la imagen aquí
    la nube de puntos blanca en la figura son los datos originales y la nube de puntos verde es el efecto de nube de puntos sin distorsión. De esta manera, el efecto real no se puede ver, y luego hay un ejemplo de construcción de un mapa para ilustrar.

    Entorno de pasillo exterior:
    inserte la descripción de la imagen aquí
    el rojo es la dirección de desplazamiento del lidar portátil. Uso de A-LOAM para el mapeo, sin construcción de nubes de puntos sin distorsión:
    inserte la descripción de la imagen aquí
    después de la eliminación de distorsión, la construcción de nubes de puntos sigue siendo tan estable como un perro viejo en un entorno grande con una gran rotación, el efecto es el siguiente:
    inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_41681988/article/details/124378390
Recomendado
Clasificación