无人驾驶——1、GNSS+IMU定位(基于三阶互补算法)

       简单的搭建工作空间就不说了,从节点的编写开始,详细讲一下怎么进行多线程节点的读取和GNSS及IMU数据的处理以及飞控中三阶互补算法的实现过程。

1.编写我们自己的msg文件,在src目录下构建msg文件,里面创建一个data.msg用于自定义我们要传输的消息类型。这里我定义了2个double型的用于传输GNSS坐标点,3个float型用于传输IMU的数据。

       在编写完成之后不要忘记在Cmakelist里面加入声明message_generation

## is used, also find other catkin packages

find_package(catkin REQUIRED COMPONENTS

  roscpp

  std_msgs

  serial

  message_generation

)

       Serial是用于串口读取的,这一部分可以百度找到对应的配置说明。

## Generate messages in the 'msg' folder

 add_message_files(

   FILES

   data.msg

 )



 generate_messages(

   DEPENDENCIES

   std_msgs

 )

与此同时在package.xml中找到下面两句取消注释。

message_generation

message_runtime

       至此,消息配置完成。

       2.编写串口读取GNSS与IMU的节点cpp文件

       代码如下:(1)GNSS

#include 

#include   //ROS已经内置了的串口包

#include 

#include 

#include 

#include 

using namespace serial;

serial::Serial ser; //声明串口对象



float Naxis_buff[5]={0},Eaxis_buff[5]={0},Naxis_sum=0,Eaxis_sum=0;

unsigned char axis_num=0;



//滑动平均法

void slid_aver(float Naxis,float Eaxis)

{

       unsigned char i=0;

       if(axis_num<5)

       {

              Naxis_buff[axis_num]=Naxis;Naxis_sum+=Naxis;

              Eaxis_buff[axis_num]=Eaxis;Eaxis_sum+=Eaxis;

              axis_num++;

       }

       else

       {

              Naxis_sum-=Naxis_buff[0];Eaxis_sum-=Eaxis_buff[0];

              for(i=0;i("GNSS", 100);

       try

       {

              //设置串口属性,并打开串口

              ser.setPort("/dev/ttyUSB0");

              ser.setBaudrate(115200);

              serial::Timeout to = serial::Timeout::simpleTimeout(100);

              ser.setTimeout(to);

             

              ser.open();

       }

       catch (serial::IOException& e)

       {

              ROS_ERROR_STREAM("Unable to open port ");

              return -1;

       }

       //检测串口是否已经打开,并给出提示信息

       if (ser.isOpen())

       {

              ROS_INFO_STREAM("Serial Port initialized");

       }

       else

       {

              return -1;

       }

       //指定循环的频率

       ros::Rate loop_rate(1);

       while (ros::ok())

       {

              data_fusion::data msg2;

              size_t n = ser.available();

              std::string gstart = "$GNRMC",gend = "\r\n";

              std::string ss,cut;

              int i = 0, sta = -1, nd = -1;

              if (n) {

                     ss=ser.read(ser.available());

                         //截取GNSS对应区域的字符串

                     sta = ss.find("A,"); nd = ss.find(",N");

                     cut=ss.substr(sta+2,nd);

                     std::cout<

       这里要注意,serial读出来的GNSS数据我们以string格式接收,GNSS输出格式为NEMA格式,因此GNRMC~\r\n的区域就是我们的坐标存的区域,再从其中截取N坐标和E坐标。

截取的方式可以采用find来找到对应的区域,并用substr进行截取,最后再采用atof()函数将字符串转化为浮点数,这里atof()函数由于有效位数的原因,后几位数会不太准确,如果想要准确读到全部位数可以自己写一个转化函数。

最后采用滑动平均法来对获得的坐标数据进行滤波

效果如下:

无人驾驶——1、GNSS+IMU定位(基于三阶互补算法)_第1张图片

 

       (2)IMU

#include 

#include   //ROS已经内置了的串口包

#include 

#include 

#include 

#include 

#include 

using namespace serial;

serial::Serial ser; //声明串口对象





int main(int argc, char** argv)

{

       unsigned char i=0,j=0,k=0,send_buff[]={0x01,0x03,0x00,0x02,0x00,0x06,0x64,0x08};

       unsigned char buff[20]={0};

       int Z_rate=0,Y_acc=0,Z_angle=0;float Z=0.41,Y=1.514,A=5.12;

       serial::parity_t parity = parity_even;

       //初始化节点

       ros::init(argc, argv, "imu");

       //声明节点句柄

       ros::NodeHandle nh;

       //发布主题

       ros::Publisher IMU_pub = nh.advertise("IMU", 100);

       try

       {

              //设置串口属性,并打开串口

              ser.setPort("/dev/ttyUSB1");

              ser.setParity(parity);   //偶校验

              ser.setBaudrate(115200);

              serial::Timeout to = serial::Timeout::simpleTimeout(100);

              ser.setTimeout(to);

             

              ser.open();

       }

       catch (serial::IOException& e)

       {

              ROS_ERROR_STREAM("Unable to open port ");

              return -1;

       }

       //检测串口是否已经打开,并给出提示信息

       if (ser.isOpen())

       {

              ROS_INFO_STREAM("Serial Port initialized");

       }

       else

       {

              return -1;

       }

       //指定循环的频率

       ros::Rate loop_rate(2);

       while (ros::ok())

       {

              data_fusion::data msg1;

              size_t n = ser.available();

              ser.write(send_buff,8);

              if (n) {

                    

                     ser.read(buff,n);

                     Z_rate=buff[6]*pow(16,6)+buff[5]*pow(16,4)+buff[4]*pow(16,2)+buff[3];Z=(float)(Z_rate-150000)/100;

                     Y_acc=buff[10]*pow(16,6)+buff[9]*pow(16,4)+buff[8]*pow(16,2)+buff[7];Y=(float)(Y_acc-20000)/1000;

                     Z_angle=buff[14]*pow(16,6)+buff[13]*pow(16,4)+buff[12]*pow(16,2)+buff[11];A=(float)(Z_angle-18000)/100;

                     printf("Z_rate=%.2f°/s,Y_acc=%.3fg,Z_angle=%.2f°\n",Z,Y,A);

                     msg1.Z_rate=Z;msg1.Y_acc=Y;msg1.Z_angle=A;

                     IMU_pub.publish(msg1);

                     }

              ros::spinOnce();

              loop_rate.sleep();

       }

       return 0;

}

       这里我采用的IMU是TL740款,存在偶校验码串口形式,对应配置即可。最后取出来的数据进行相应转化,得到Z轴偏转角度,Y轴的前进加速度,Z轴偏转速率。

 

       3.3阶互补滤波实现定位

       3阶互补滤波的实现框图如下:

无人驾驶——1、GNSS+IMU定位(基于三阶互补算法)_第2张图片

       通过读取IMU的数值进行转化,得到相应的X轴(对应E),Y轴(对应N)两个方向的加速度,进行积分后得到对应的移动距离,这个移动距离与GNSS读取的坐标点的距离进行比较,能够得到相应误差,将这种误差反馈回IMU加速度、速度、距离三个分量,从而实现互补,由于存在积分,系数的关系:Ka

       对应代码如下:

      

#include 

#include 

#include 

#include 

float ay[2]={0},ax[2]={0},vy[2]={0},vx[2]={0};

double N_axis[2]={0},E_axis[2]={0};

uint8_t IMU_init=0,GNSS_init=0,GNSS=0;



//回调函数

void IMU_callback(const data_fusion::data::ConstPtr& msg1)

{

       uint8_t T_imu=0.5,ka=0.1,kv=0.2,ks=0.3;//互补滤波程度

       float delta_ns=0,delta_es=0;

       //IMU初始化

if(IMU_init==0){ay[1]=msg1->Y_acc;ax[1]=msg1->Y_acc*tan(msg1->Z_angle);ay[0]=ay[1];ax[0]=ax[1];vy[1]=ay[1]/2*T_imu;vx[1]=ax[1]/2*T_imu;}

       //

       else

       {

              vy[0]=vy[1];vx[0]=vx[1];

              ay[0]=ay[1];ax[0]=ax[1];

              ay[1]=msg1->Y_acc;ax[1]=msg1->Y_acc*tan(msg1->Z_angle);

              vy[1]=vy[0]+ay[0]*T_imu;vx[1]=vx[0]+ax[0]*T_imu;

       }

       //GNSS更新数据

       if(GNSS_init&&GNSS==1)

       {

              delta_ns=N_axis[0]-N_axis[1];delta_es=E_axis[0]-E_axis[1];

              N_axis[1]=N_axis[0];E_axis[1]=E_axis[0];

              GNSS=0;

       }

       else

       {

              N_axis[1]=N_axis[0]+vy[0]*T_imu+0.5*ay[0]*T_imu*T_imu+delta_ns*ks*T_imu;  E_axis[1]=E_axis[0]+vx[0]*T_imu+0.5*ax[0]*T_imu*T_imu+delta_es*ks*T_imu;

              N_axis[0]=N_axis[1];E_axis[0]=E_axis[1];

       }

      

       ay[1]+=delta_ns*ka*T_imu;ax[1]+=delta_es*ka*T_imu;//取积分,互补更新ax和ay的数值

       vy[1]+=delta_ns*kv*T_imu;vx[1]+=delta_es*kv*T_imu;//取积分,互补更新xv和vy的数值





       std::cout<<"N_axis="<("order_comple", 100);

       ros::Rate loop_rate(2);

       while(ros::ok())

       {

              data_fusion::data msg3;

              if(IMU_init==1)

              {

                     msg3.N_axis=N_axis[1];msg3.E_axis=E_axis[1];

                     order_pub.publish(msg3);

              }

              ros::spinOnce();

              loop_rate.sleep();

       }

       return 0;

}

       最后,别忘记配置Cmakelists文件,在末尾添加对应项:

add_executable(GNSS src/GNSS.cpp)

add_dependencies(GNSS ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(GNSS

  ${catkin_LIBRARIES}

)



add_executable(IMU src/IMU.cpp)

add_dependencies(IMU ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(IMU

  ${catkin_LIBRARIES}

)



add_executable(order_comple src/order_comple.cpp)

add_dependencies(order_comple ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(order_comple

  ${catkin_LIBRARIES}

)

 

你可能感兴趣的:(无人驾驶——1、GNSS+IMU定位(基于三阶互补算法))