PX4矩阵飞行仿真
根据官方提供的offboard程序,在此基础上进行修改实现了对矩阵进行一个飞行。在此之前我们需要先了解mavros的话题以及消息类型,可以参考这篇文章:PX4自动控制中常用的几个话题_px4的话题类型-CSDN博客
首先创建我们的pacakge,运行以下指令
cd catkin_ws/src
catkin_create_pkg quater_pkg rospy rospp std_msgs
创建一个新的quater_node.cpp文件
以下是对我们整个代码的一个解说:整体代码思路很简单:切换到offboard模式——arm解锁桨叶——飞四个点位【进行一个误差的判断】——到最后一个点位进行降落。
首先是对我们头文件的一个包含,是消息这个类下面的某个属性的.h文件
#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>
然后在主函数内对我们的ROS进行初始化以及定义飞机状态、飞机位置话题的订阅,定义两个服务类型分别是offboard模式飞行以及请求解锁桨叶的服务指令。以及定一个发布者,发布位置信息,给予飞控接受指令。
ros::init(argc, argv, "square_node");//初始化ROS
ros::NodeHandle nh;//创建节点句柄
//创建订阅者,订阅话题mavros/state
ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
("mavros/state", 10, state_cb);
//创建一个发布者,发布"mavros/setpoint_position/local"话题
ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
("mavros/setpoint_position/local", 10);
//创建一个客户端,该客户端用来请求PX4无人机的解锁
ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
("mavros/cmd/arming");
//创建一个客户端,该客户端用来请求进入offboard模式
ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
("mavros/set_mode");
//创建一个订阅者,订阅位置信息
ros::Subscriber position_receive=nh.subscribe<geometry_msgs::PoseStamped>
("mavros/local_position/pose",10,pose_cb);
接着等待飞控的连接
while(ros::ok() && !current_state.connected){
ros::spinOnce();
rate.sleep();
}
在开始飞行之前给无人机发布一定的位置信息
for(int i = 100; ros::ok() && i > 0; --i){
local_pos_pub.publish(pose);
ros::spinOnce();
rate.sleep();
}
设置两个客户端分别是OFFBOARD与ARM解锁模式,并将当前系统时间记录下来
mavros_msgs::SetMode offb_set_mode;
offb_set_mode.request.custom_mode = "OFFBOARD";
mavros_msgs::CommandBool arm_cmd;
arm_cmd.request.value = true;//并将其bool返回值赋为1,解锁为真
ros::Time last_request = ros::Time::now();//记录当前系统时间
请求将飞机飞行模式切换为OFFBOARD模式,并且判断是否解锁,解锁成功则跳出循环【解锁成功之后必须再一次发布位置信息不然飞机将不会正常进行飞行】。
while(ros::ok()){
//if语句循环请求进入OFFBOARD模式,进入以后则会进入else语句请求对无人机解锁。
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
{//已经进入OFFBOARD模式,判断是否解锁,若解锁则break;
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");
break;
}
last_request = ros::Time::now();
}
}
//解锁之后再一次发布位置指令,若不发布位置质量,则飞机便会一直呆在(0,0,0)点。
pose_goal(0,0,1.5);
local_pos_pub.publish(pose);
ros::spinOnce();
rate.sleep();
}
定义两个回调函数分别是飞机状态的回调与位置消息的回调函数用两个常量指针作为参数进行传递。
//定义消息类型变量
mavros_msgs::State current_state;
geometry_msgs::PoseStamped pose;
//接受飞机状态的回调函数
void state_cb(const mavros_msgs::State::ConstPtr& msg){
current_state = *msg;
}
//接受飞机位置的回调函数;
double pose_position[3];//定义全局变量的数组
void pose_cb(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
pose_position[0]=msg->pose.position.x;
pose_position[1]=msg->pose.position.y;
pose_position[2]=msg->pose.position.z;
}
定义绝对值函数与传入目标值的函数,简化编程
//设定目标位置
void pose_goal(double x,double y,double z)
{
pose.pose.position.x = x;
pose.pose.position.y = y;
pose.pose.position.z = z;
}
//编写误差函数,返回值类型为double
double fab_return[3];//定义返回误差的数组
double err;//定义一个全局变量
int count=0;//定义一个标志位
double fabs_pose(double pose_goal,double pose_real)
{
err= fabs(pose_goal-pose_real);
return err;
}
编写main函数中选择发布位置的函数,通过判断目标值与实际值的误差来进行下一个目标点的飞行
while (ros::ok())
{
fab_return[0]=fabs_pose(pose.pose.position.x,pose_position[0]);
fab_return[1]=fabs_pose(pose.pose.position.y,pose_position[1]);
fab_return[2]=fabs_pose(pose.pose.position.z,pose_position[2]);
if (fab_return[0]<0.1&&fab_return[1]<0.1&&fab_return[2]<0.1&&((ros::Time::now() - last_request )> ros::Duration(3.0)))
{
ROS_INFO("当前位置x:%.2f,y:%.2f,z:%.2f\n",pose.pose.position.x,pose.pose.position.y,pose.pose.position.z);
count++;
last_request = ros::Time::now();
switch (count)
{
//case 0:pose_goal(0,0,1.5);count ++;break;
case 0:pose_goal(0,1.5,1.5);break;
case 1:pose_goal(1.5,1.5,1.5);break;
case 2:pose_goal(1.5,0,1.5);break;
case 3:pose_goal(0,0,1.5);break;
case 4:pose_goal(0,0,0);break;
default:
break;
}
}
else
{
local_pos_pub.publish(pose);//一直发布这个目标值
// ROS_WARN("当前位置x:%.2f,y:%.2f,z:%.2f\n",pose.pose.position.x,pose.pose.position.y,pose.pose.position.z);
}
ros::spinOnce();
rate.sleep();
}
整体代码如下:
//相关头文件包含
#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>
mavros_msgs::State current_state;
geometry_msgs::PoseStamped pose;
//设定目标位置
void pose_goal(double x,double y,double z)
{
pose.pose.position.x = x;
pose.pose.position.y = y;
pose.pose.position.z = z;
}
//接受飞机状态的回调函数
void state_cb(const mavros_msgs::State::ConstPtr& msg){
current_state = *msg;
}
//接受飞机位置的回调函数;
double pose_position[3];
double fab_return[3];
double err;
int count=0;
void pose_cb(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
pose_position[0]=msg->pose.position.x;
pose_position[1]=msg->pose.position.y;
pose_position[2]=msg->pose.position.z;
}
double fabs_pose(double pose_goal,double pose_real)
{
err= fabs(pose_goal-pose_real);
return err;
}
int main(int argc, char **argv)
{
setlocale(LC_ALL,"");
//初始化节点
ros::init(argc, argv, "square_node");
//创建节点句柄
ros::NodeHandle nh;
//创建订阅者,订阅话题mavros/state
ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
("mavros/state", 10, state_cb);
//创建一个发布者,发布"mavros/setpoint_position/local"话题
ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
("mavros/setpoint_position/local", 10);
//创建一个客户端,该客户端用来请求PX4无人机的解锁
ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
("mavros/cmd/arming");
//创建一个客户端,该客户端用来请求进入offboard模式
ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
("mavros/set_mode");
//创建一个订阅者,订阅位置信息
ros::Subscriber position_receive=nh.subscribe<geometry_msgs::PoseStamped>
("mavros/local_position/pose",10,pose_cb);
//搭配rate.sleep()函数,实现50Hz频率的循环;
ros::Rate rate(50.0);
// 等待飞控连接
while(ros::ok() && !current_state.connected){
ros::spinOnce();
rate.sleep();
}
//预发布期望位置信息,给无人机目标点
//send a few setpoints before starting
for(int i = 100; ros::ok() && i > 0; --i){
local_pos_pub.publish(pose);
ros::spinOnce();
rate.sleep();
}
//设置客户端请求无人机进入“OFFBOARD”模式
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();
while(ros::ok()){
//if语句循环请求进入OFFBOARD模式,进入以后则会进入else语句请求对无人机解锁。
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");
break;
}
last_request = ros::Time::now();
}
}
pose_goal(0,0,1.5);
local_pos_pub.publish(pose);
ros::spinOnce();
rate.sleep();
}
last_request =ros::Time::now();
while (ros::ok())
{
fab_return[0]=fabs_pose(pose.pose.position.x,pose_position[0]);
fab_return[1]=fabs_pose(pose.pose.position.y,pose_position[1]);
fab_return[2]=fabs_pose(pose.pose.position.z,pose_position[2]);
if (fab_return[0]<0.1&&fab_return[1]<0.1&&fab_return[2]<0.1&&((ros::Time::now() - last_request )> ros::Duration(3.0)))
{
ROS_INFO("当前位置x:%.2f,y:%.2f,z:%.2f\n",pose.pose.position.x,pose.pose.position.y,pose.pose.position.z);
count++;
last_request = ros::Time::now();
switch (count)
{
//case 0:pose_goal(0,0,1.5);count ++;break;
case 0:pose_goal(0,1.5,1.5);break;
case 1:pose_goal(1.5,1.5,1.5);break;
case 2:pose_goal(1.5,0,1.5);break;
case 3:pose_goal(0,0,1.5);break;
case 4:pose_goal(0,0,0);break;
default:
break;
}
}
else
{
local_pos_pub.publish(pose);
// ROS_WARN("当前位置x:%.2f,y:%.2f,z:%.2f\n",pose.pose.position.x,pose.pose.position.y,pose.pose.position.z);
}
ros::spinOnce();
rate.sleep();
}
return 0;
}
最后别忘了添加编译环境