XTdrone进行PX4激光slam建图获取坐标及自主飞行仿真

最近在学习无人机的室内定位和自主飞行,xtdrone在这方面提供了丰富的模型和例程资源,可以快速实现相关算法的仿真验证和部署,下面分享下基于xtdrone的资源进行激光slam建图定位以及控制px4进行简单轨迹飞行的过程

本文内容主要参考xtdrone官方使用文档:XTDrone使用文档 · 语雀

1、XTdrone环境的部署

此部分不多赘述,可以参考官方使用文档进行平台部署,也可以自行搜索相关成品镜像文件进行使用

2、安装Cartographer

Cartographer是google推出的一套基于图优化的SLAM算法且提供了ROS的集成,因此使用apt安装即可,新建终端输入以下命令

sudo apt install ros-melodic-cartographer-ros
sudo apt install ros-melodic-cartographer-rviz

安装map server用于保存地图

sudo apt install ros-melodic-map-server

xtdrone已经为我们提供了相关的配置文件以及launch文件例程,复制到刚刚安装的catographer功能包目录下

roscd cartographer_ros
sudo cp -r  ~/XTDrone/sensing/slam/laser_slam/cartographer/* ./
3.仿真环境launch文件配置

xtdrone的仿真launch文件存放在路径/home/han/PX4_Firmware/launch中,官方文档中使用的是indoor3.launch这个文件

注意在启动仿真环境前更改对应launch文件的sdf设置

sdf文件保存在路径/home/han/PX4_Firmware/Tools/sitl_gazebo/model中,对应更改launch文件的相关参数即可变更仿真环境中所使用的飞行器模型

这里由于我们使用激光雷达slam进行定位,将sdf更改为改为带有激光雷达的模型iris_2d_lidar,我这里使用的是我自己的launch文件,其余文件同理,将标黄的部分修改为如下图所示

同理在编辑自己的launch文件时也可以通过修改读取地图文件的路径来更改仿真环境所使用的地图,xtdrone提供的地图保存在路径/home/han/PX4_Firmware/Tools/sitl_gazebo/worlds下

通过更改launch文件中的地图路径即可变更仿真环境所使用的地图,这里我使用我自己的地图hang30.world,官方文档中所使用的地图为indoor3.world

 

4、QGroundcontrol配置PX4飞控

启动仿真环境

roslaunch px4 indoor3x.launch 

 打开QGroundControl地面站此时会自动连接到仿真模型中的飞控,在参数中进行修改

将EKF2_AID_MASK修改为仅vision posithon和visionyaw fution进行数据融合定位

由于激光雷达位于飞行器顶部,因此我们还要限制其最大飞行速度以保证定位的稳定性,修改MPC_XY_VEL_MAX参数以限制飞行速度,我这里设置成0.2

5.启动仿真环境进行仿真

关闭仿真环境后重新启动以让刚刚修改的飞控参数生效

roslaunch px4 indoor3x.launch 

在新终端启动Cartographer

roslaunch cartographer_ros xtdrone_2d.launch

 此时打开地面站已经可以看到激光slam回传的位置信息了

官方文档中使用键盘进行飞行控制,我这里写了一个方形轨迹的功能包对无人机进行飞行控制因此没有启动通信和键盘控制脚本,键盘控制可以参照官方文档

将cartographer输出的2D位姿和高度值,转换为mavros话题

cd ~/XTDrone/sensing/slam/laser_slam/script/
python laser_transfer.py iris 0 cartographer

 下面是我编写的轨迹飞行功能包代码,功能包和代码都命名为offboard_multi_position,功能为借助激光雷达slam的位置信息进行正方形轨迹飞行并降落

//包含ROS和MAVROS相关头文件 
#include <string> 
#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>
#include <std_msgs/Bool.h>
#include <geometry_msgs/TwistStamped.h>
#include <mavros_msgs/PositionTarget.h>
#include <cmath>
#include <tf/transform_listener.h>
#include <nav_msgs/Odometry.h>
#include <mavros_msgs/CommandLong.h>   
#include <string>
#include <geometry_msgs/Twist.h>

#define ALTITUDE  2

int flag  = 1;

mavros_msgs::State current_state;
void state_cb(const mavros_msgs::State::ConstPtr& msg);

//定义变量,用于接收无人机的里程计信息
tf::Quaternion quat; 
double roll, pitch, yaw;
float init_position_x_take_off =0;
float init_position_y_take_off =0;
float init_position_z_take_off =0;
bool  flag_init_position = false;
nav_msgs::Odometry local_pos;
void local_pos_cb(const nav_msgs::Odometry::ConstPtr& msg);

void state_cb(const mavros_msgs::State::ConstPtr& msg)
{
    current_state = *msg;
}

//回调函数接收无人机的里程计信息
void local_pos_cb(const nav_msgs::Odometry::ConstPtr& msg)
{
    local_pos = *msg;
    if (flag_init_position==false && (local_pos.pose.pose.position.z!=0))
    {
		init_position_x_take_off = local_pos.pose.pose.position.x;
	    init_position_y_take_off = local_pos.pose.pose.position.y;
	    init_position_z_take_off = local_pos.pose.pose.position.z;
        flag_init_position = true;		    
    }
    tf::quaternionMsgToTF(local_pos.pose.pose.orientation, quat);	
	tf::Matrix3x3(quat).getRPY(roll, pitch, yaw);
}


int main(int argc, char **argv)
{
    ros::init(argc, argv, "offboard_multi_position");

    ros::NodeHandle nh;

    ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>("/iris_0/mavros/state", 10, state_cb);

    ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>("/iris_0/mavros/setpoint_position/local", 10);
    
    ros::Subscriber local_pos_sub = nh.subscribe<nav_msgs::Odometry>("/iris_0/mavros/local_position/odom", 10, local_pos_cb);

    ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>("/iris_0/mavros/cmd/arming");

    ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>("/iris_0/mavros/set_mode");

    //the setpoint publishing rate MUST be faster than 2Hz
    ros::Rate rate(20.0);

    // wait for FCU connection
    while(ros::ok() && !current_state.connected)
    {
        ros::spinOnce();
        rate.sleep();
    }

    geometry_msgs::PoseStamped pose;
    pose.pose.position.x =init_position_x_take_off + 0;
    pose.pose.position.y =init_position_y_take_off + 0;
    pose.pose.position.z =init_position_z_take_off + ALTITUDE;
     

    //send a few setpoints before starting
    for(int i = 100; ros::ok() && i > 0; --i)
    {
        local_pos_pub.publish(pose);
        ros::spinOnce();
        rate.sleep();
    }

    mavros_msgs::SetMode offb_set_mode;
    offb_set_mode.request.custom_mode = "OFFBOARD";

    mavros_msgs::CommandBool arm_cmd;
    arm_cmd.request.value = true;

    ros::Time last_request = ros::Time::now();
 
 	//此处满足一次请求进入offboard模式即可,官方例成循环切入offboard会导致无人机无法使用遥控器控制
    while(ros::ok())
    {
    	//请求进入OFFBOARD模式
        if( current_state.mode != "OFFBOARD" && (ros::Time::now() - last_request > ros::Duration(5.0)))
        {
            if( set_mode_client.call(offb_set_mode) && offb_set_mode.response.mode_sent)
            {
                ROS_INFO("Offboard enabled");
            }
           	last_request = ros::Time::now();
       	}
        else 
		{
			//请求解锁
			if( !current_state.armed && (ros::Time::now() - last_request > ros::Duration(5.0)))
			{
		        if( arming_client.call(arm_cmd) && arm_cmd.response.success)
		       	{
		            ROS_INFO("Vehicle armed");
		        }
		        	last_request = ros::Time::now();
			}
		}
	    
	    if(fabs(local_pos.pose.pose.position.z- init_position_z_take_off -ALTITUDE)<0.2)
		{	
			if(ros::Time::now() - last_request > ros::Duration(3.0))
			{
				break;
			}
		}
		//发布期望位置信息
		pose.pose.position.x =init_position_x_take_off + 0;
		pose.pose.position.y =init_position_y_take_off + 0;
		pose.pose.position.z =init_position_z_take_off + ALTITUDE;
		local_pos_pub.publish(pose);
        ros::spinOnce();
        rate.sleep();
    }   
    
    
    while(ros::ok())
    {
        if((flag == 1)  && (ros::Time::now() - last_request > ros::Duration(5.0)))
		{ 
			ROS_INFO("Position_1");
            pose.pose.position.x =init_position_x_take_off + 0;
			pose.pose.position.y =init_position_y_take_off + 0;
			pose.pose.position.z =init_position_z_take_off + ALTITUDE;                     
			last_request = ros::Time::now();
            flag=2;
        }

		if((flag ==2) && (ros::Time::now() - last_request > ros::Duration(8.0)))
		{
			ROS_INFO("Position_2 ");
		    pose.pose.position.x =init_position_x_take_off + 2;
			pose.pose.position.y =init_position_y_take_off + 0;
			pose.pose.position.z =init_position_z_take_off + ALTITUDE;     
			last_request = ros::Time::now();
			flag=3;
		}
		                   
		if((flag ==3) && (ros::Time::now() - last_request > ros::Duration(8.0)))
		{
		    ROS_INFO("Position_3 ");
		    pose.pose.position.x =init_position_x_take_off + 2;
			pose.pose.position.y =init_position_y_take_off + 2;
			pose.pose.position.z =init_position_z_take_off + ALTITUDE;      
		    last_request = ros::Time::now();
			flag=4;
		}

启动轨迹飞行功能包

 rosrun offboard_multi_position offboard_multi_position

 通过rviz和gazebo可以看到无人机通过激光slam定位完成了既定仿真任务

  • 34
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
激光SLAM(Simultaneous Localization and Mapping)是一种利用激光传感器进行建图和定位的技术。下面是激光SLAM的一般步骤: 1. 激光数据采集:使用激光传感器(如激光雷达)获取环境中的激光数据。激光传感器会发射激光束,并测量激光束在环境中的反射情况,从而得到距离和角度信息。 2. 特征提取:从激光数据中提取特征点。特征点通常是环境中的显著点,例如墙角、边缘等。提取出的特征点可以用于后续的定位和建图。 3. 运动估计:通过对连续的激光数据进行匹配,估计机器人的运动。这可以通过计算两帧激光数据之间的位姿差异来实现。常用的方法包括ICP(Iterative Closest Point)算法和Scan Matching算法。 4. 地图更新:根据运动估计的结果,将当前帧的特征点添加到地图中。这样,随着时间的推移,地图会不断更新并完善。 5. 闭环检测与校正:在建图的过程中,由于误差累积或机器人回头等原因,可能会出现闭环。为了解决这个问题,需要检测闭环,并对地图进行校正,以减小误差。 6. 位姿估计:通过不断的运动估计和地图更新,可以实时地估计机器人的位姿(位置和姿态)。这可以用于导航、路径规划等应用。 以上是激光SLAM建图的一般步骤,不同的算法和方法可能会有所不同。具体实现时需要根据具体的需求和场景进行选择和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值