fcu_mission 任务规划

fcu_mission 是 fcu_core_v2 中的任务规划模块。规划飞行任务前,需获取无人机的当前位姿。

该模块首先接收飞控发送的里程计(odom)数据:

ros::Subscriber odom001=nh.subscribe<nav_msgs::Odometry>("odom_global_001", 100, odom_global001_handler);

随后进行坐标变换,并将四元数转换为姿态角:

void odom_global001_handler(const nav_msgs::Odometry::ConstPtr& odom)
{
  pos_odom_001_x=(float)odom->pose.pose.position.x;// 将位置坐标转换为 FRU 坐标
  pos_odom_001_y=-(float)odom->pose.pose.position.y;
  pos_odom_001_z=(float)odom->pose.pose.position.z;
  float quaternion_odom[4]={(float)odom->pose.pose.orientation.w,
                            (float)odom->pose.pose.orientation.x,
                            (float)odom->pose.pose.orientation.y,
                            (float)odom->pose.pose.orientation.z};
  mavlink_quaternion_to_euler(quaternion_odom, &pos_odom_001_roll, &pos_odom_001_pitch, &pos_odom_001_yaw);
}

fcu_mission 提供四个飞行任务例程,用于演示轨迹规划。各例程均通过键盘指令启动,因此需订阅键盘指令话题:

  ros::Subscriber comm=nh.subscribe<std_msgs::Int16>("/fcu_command/command", 100, cmdHandler);

键盘指令由 fcu_command.cpp 发布。每个按键对应一个 case 分支,可参照现有分支添加自定义任务的启动按键。

switch(buf[0]){
      case 'p':
        printf("执行路径\n");
        cmd.data=0;
        command.publish(cmd);
        break;
      case 'a':
        printf("解锁\n");
        cmd.data=1;
        command.publish(cmd);
        break;
      case 'd':
        printf("锁定\n");
        cmd.data=2;
        command.publish(cmd);
        break;
      case 't':
        printf("起飞\n");
        cmd.data=3;
        command.publish(cmd);
        break;
      case 'l':
        printf("降落\n");
        cmd.data=4;
        command.publish(cmd);
        break;
      ***

fcu_mission 中的键盘指令处理函数同样使用 switch/case 语句,根据指令选择相应任务:

void cmdHandler(const std_msgs::Int16::ConstPtr& cmd){
  switch(cmd->data){
    case 0:
        enable_path=true;
        break;
    ***

飞行任务规划例程

绕圆飞行

if(enable_track){
      theta+=M_PI/20/200;

      px1=1.0*cosf(theta)+2;
      py1=1.0*sinf(theta)+2;
      pz1=0.6;

      px2=1.0*cosf(theta+M_PI*2/6)+2;
      py2=1.0*sinf(theta+M_PI*2/6)+2;
      pz2=0.6;

      px3=1.0*cosf(theta+M_PI*4/6)+2;
      py3=1.0*sinf(theta+M_PI*4/6)+2;
      pz3=0.6;

      px4=1.0*cosf(theta+M_PI*6/6)+2;
      py4=1.0*sinf(theta+M_PI*6/6)+2;
      pz4=0.6;

      px5=1.0*cosf(theta+M_PI*8/6)+2;
      py5=1.0*sinf(theta+M_PI*8/6)+2;
      pz5=0.6;

      px6=1.0*cosf(theta+M_PI*10/6)+2;
      py6=1.0*sinf(theta+M_PI*10/6)+2;
      pz6=0.6;
    }

将无人机放置在 theta==0 对应的初始位置,启动并建立连接,确认收到心跳包后,依次输入 a(解锁)、t(起飞)、r(绕圆飞行);输入 s 可停止绕圆飞行。

轨迹飞行

轨迹飞行的处理方式与绕圆飞行类似,具体实现如下:

switch (path_track_status)
      {
        case ReadyToGoal:
          switch(goal_point)
          {
            case 0:
            SetGoal(1,0.5,0.5,0);
            break;
            case 1:
            SetGoal(1,1.0,1.0,0);
            break;
            case 2:
            SetGoal(1,1.5,1.0,0);
            break;
            case 3:
            SetGoal(1,2.0,1.0,0);
            break;
            case 4:
            SetGoal(1,2.5,1.0,0);
            break;
            case 5:
            SetGoal(1,2.5,2.0,0);
            break;
            case 6:
            SetGoal(1,2.0,2.0,0);
            break;
            case 7:
            SetGoal(1,1.5,2.0,0);
            break;
            case 8:
            SetGoal(1,1.0,2.0,0);
            break;
            case 9:
            SetGoal(1,0.5,2.0,0);
            break;
          }
          path_track_status = ExecutingGoal;
          break;
        case ExecutingGoal:
          if(IsReachGoal(1,0.1))
          {
            path_track_status = ReadyToGoal;
            goal_point++;
            break;
          }
          // std::cout << "Not Finish yet..." << std::endl;
          break;

将无人机放置在 goal_point==0 对应的初始位置,启动并建立连接,确认收到心跳包后,依次输入 a(解锁)、t(起飞)、p(执行路径);输入 s 可停止执行。

定点飞行

本节为键盘控制例程,目标位置由起飞位置与设定偏移量相加得到。RViz 插件航点列表中填写的是所用全局坐标系下的绝对坐标,具体操作参见 rviz_plugin 的使用。

        switch(enable_pos){
          case 0:
              px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
              px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
              px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
              px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
              px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
              px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
              break;
          case 1:
              px=1.0;
              py=1.0;
              pz=1.0;

              px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
              px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
              px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
              px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
              px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
              px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;

              break;

放置无人机,启动并建立连接,确认收到心跳包后,依次输入 a(解锁)、t(起飞),再输入 1 至 4 中的目标点编号,执行对应的定点飞行任务。

定点飞行通过 fcu_command 中的 enable_pos 指令选择目标点。pos_takeoff_001_x 和 pos_takeoff_001_y 记录起飞位置,px 和 py 表示相对于起飞点的偏移量。将偏移量与起飞位置相加后,得到全局坐标系中的绝对目标位置;最终发布的目标点均为绝对坐标。

追踪飞行

追踪飞行包括前视追踪和下视追踪,分别适用于搭载机载电脑并配备前视摄像机或下视摄像机的无人机。机载电脑需运行 fcu_core_v2 和 ArUco 检测程序。 在远程电脑上按下对应按键,即可向飞控发送追踪指令。 图片 飞控将消息转发至机载电脑,由机载电脑启动追踪并下发追踪控制指令。具体流程如下图所示: 图片

定时器执行函数

fcu_mission 通过定时器周期性发布规划目标点的位置和姿态数据。

  ros::Timer timer_mission_001 = nh.createTimer(ros::Duration(0.1),execute_mission_001,false);

定时器周期为 100 ms,每个周期调用一次 execute_mission_001 函数:

void execute_mission_001(const ros::TimerEvent &event){
  if(get_pos_cmd){
      return;
  }
  if(set_goal&&!use_goal_001){
    return;
  }
  // 发布 mission 数据
  mission_001.layout.dim.push_back(std_msgs::MultiArrayDimension());
  mission_001.layout.dim[0].label = "mission_001";
  mission_001.layout.dim[0].size = 11;
  mission_001.layout.dim[0].stride = 1;
  mission_001.data.resize(11);
  mission_001.data[0]=yaw;//rad
  mission_001.data[1]=yaw_rate;//rad/s
  mission_001.data[2]=px1;//x
  mission_001.data[3]=py1;//y
  mission_001.data[4]=pz1;//z
  mission_001.data[5]=vx;//vx
  mission_001.data[6]=vy;//vy
  mission_001.data[7]=vz;//vz
  mission_001.data[8]=ax;//ax
  mission_001.data[9]=ay;//ay
  mission_001.data[10]=az;//az
  mission_pub_001.publish(mission_001);
  use_goal_001=false;     
}

该函数负责填充并发布任务数据。publish 将规划目标点通过 ROS 话题发送至 fcu_bridge,再由 fcu_bridge 转发至飞控。


返回教程目录 · 上一章:配置与项目架构 · 下一章:rviz_plugin 的使用