旋翼回收火箭系列博客3——控制系统设计(PX4火箭)

2023-05-16

绪论

为了缩短研制周期和提高产品可靠性,本系统采用商用开源自动驾驶仪PX4,实现旋翼空中展开并回收的功能。PX4是全球最为成熟的开源自动驾驶仪,可实现自动起飞、降落、执行航点等基本任务。然而此次火箭比赛要求能够实现自动展开和降落至指定区域,所以需要对现有飞行硬件进行改造。其困难和解决方案如下:

困难:

  1. 火箭发射过载一般大于5g,商用GPS会自动锁定,导致飞控无法解析出全球定位信息。这种情况下,自动飞行的功能将全部丧失,会导致整个飞行任务的失败;
  2. 火箭要求自动飞行,地面人员不能参与手动控制。然而对于一般的旋翼飞机来讲,为了保证安全,会要求有遥控器连接到飞控,以便随时切换回手动飞行。所以解除掉手动飞行的限制也是关键之一;
  3. 火箭要求达到最高点以后再展开机械臂,这就要求首先飞控输出相关的作动信号,然后飞机再进行螺旋桨的起旋,最后执行降落至指定区域。

解决方案:

  1. 针对GPS上锁问题,我们采用国产高精度、高动态特性的GPS模块,保证火箭起飞过程中的定位连续性和可靠性;
  2. 针对解除手动控制,应当对飞控源码进行更改,并时刻让飞控保持在offboard模式下,接受来自地面站的指令;
  3. 对于上述复杂功能,飞控自身是难以完成的,因此需要借助上位机的协助。为了减小火箭质量、简化系统复杂度,我们采用地面电脑作为上位机,实时向飞控发送指令。协议采用MAVLINK,使用MAVSDK进行开发。
    下面将针对硬件和软件两方面进行详细阐述:

硬件

飞控

采用CUAV公司开发生产的CUAV V5 Nano作为核心控制系统,它是PX4的FMU V5版本,因此具有强大的数据处理能力和多冗余系统,保证了飞行任务的可靠性和稳定性。还有一个原因是它尺寸极小,可以为模型火箭节省空间和质量。其外形如下图所示。

在这里插入图片描述

数传

飞控需要接受地面的实时指令,因此需要高带宽、低时延的通讯方式。我们采用CUAV公司开发生产的P9超远程数传,其最远传输距离达30KM,支持902-928MHZ的跳频传输,输出功率达1W。其外形及接口定义如图所示。
在这里插入图片描述

GPS模块

使用和芯星通公司专门针对弹载领域应用推出的高性能小型化卫星导航定位OEM板卡。支持GPSL1、GLOL1、BD2B1、B2、B3信号,使用RS232串口输出,尺寸仅38mm。热启动时间小于6秒,重启捕获时间小于2秒;轴向速度在2000m/s,加速度20~30g以下能保持良好的定位精度。水平和高程精度均小于10m,测速精度小于0.2m/s。其外形尺寸如图所示:

在这里插入图片描述

软件部分

自动驾驶仪 PX4

采用最新的PX4 V12 beta版本,此版本支持最新的MAVLINK拓展功能和取消起飞怠速功能。在Ubuntu18.0系统上对固件进行编译,然后通过USB烧写至飞控。由于上述GPS板卡输出格式与PX4内部格式不一致,需要进行源码的更改。(PX4无法识别标准NMEA格式GPS,在最新的V13版本上已得到解决)
火箭实物飞行成本高、危险性大,因此必要的仿真验证和室内测试是必要的。采用PX4 sitl+Gazebo的方式进行仿真,以验证程序代码的完整性,仿真过程如下图。其后进行室内测试,保证数据获取完整、相关硬件能够按照规定启动。

在这里插入图片描述
在这里插入图片描述

通讯协议 MAVSDK

采用轻量级通讯协议MAVLINK进行地面电脑与火箭上飞控的串口通讯,使用MAVSDK进行开发。MAVSDK是针对MAVLINK的开发套件,封装了常用的功能,包括数据获取、自动飞行等。所有的代码均已开源,详见个人Github主页
在软件中,其飞行时序如下:

时间/秒动作
00:00检测过载超过3g时,触发计时
05:00打开降落伞,将火箭拉直
06:00折叠机构展开
07:00螺旋桨起旋
07:30开始自动飞向目标点上空
unknow启动自动着陆
unknow降落后飞行器上锁

代码

/*************************Summary***************************
Function: Rocket recycle throught MAVSDK
1. Connect to PX4 through Serial serial:///dev/ttyUSB0:57600;
2. Check the overload of PX4, if it is over 3g, start timing;
3. 00:05:00, fired the parachute;
3. 00:06:00, unfold the arms;
4. 00:07:00, armed the vehicle;
5. 00:07:30, started the return mission;
6. after the mission, disarmed the vehicle;
Author: YDM <1779876755@qq.com>
************************************************************/

/*****************Subscribe Message Frequency*******************
        NAME          Frequency            Rate Function            Callback
LOCAL_POSITION_NED      1HZ     set_rate_position_velocity_ned  subscribe_position_velocity_ned
GLOBAL_POSITION_INT     1HZ     set_rate_position               subscribe_position
HIGHRES_IMU             5HZ     set_rate_imu                    subscribe_imu
***************************************************************/

/*****************Advertise Message Frequency*******************
        NAME          Frequency            Rate Function            Callback
LOCAL_POSITION_NED      1HZ     set_rate_position_velocity_ned  subscribe_position_velocity_ned
***************************************************************/

/*****************Heard file and Namespace*********************/
#include <chrono>
#include <cstdint>
#include <functional>
#include <iostream>
#include <future>
#include <memory>
#include <thread>
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/mission/mission.h>
#include <mavsdk/plugins/telemetry/telemetry.h>


using namespace mavsdk;
using namespace std::placeholders; // for `_1`
using namespace std::chrono; // for seconds(), milliseconds()
using namespace std::this_thread; // for sleep_for()

#define ERROR_CONSOLE_TEXT "\033[31m" // Turn text on console red
#define TELEMETRY_CONSOLE_TEXT "\033[34m" // Turn text on console blue
#define NORMAL_CONSOLE_TEXT "\033[0m" // Restore normal console colour
/**************************************************************/

/**************************Exit Function*********************/
// Handles Action's result
inline void handle_action_err_exit(Action::Result result, const std::string& message);
// Handles Mission's result
inline void handle_mission_err_exit(Mission::Result result, const std::string& message);
// Handles Connection result
inline void handle_connection_err_exit(ConnectionResult result, const std::string& message);
/**************************************************************/

static Mission::MissionItem make_mission_item(
    double latitude_deg,
    double longitude_deg,
    float relative_altitude_m,
    float speed_m_s,
    bool is_fly_through,
    float gimbal_pitch_deg,
    float gimbal_yaw_deg,
    Mission::MissionItem::CameraAction camera_action);

/*************************How to use it************************/
void usage(std::string bin_name)
{
    std::cout << NORMAL_CONSOLE_TEXT << "Usage : " << bin_name << " <connection_url>" << std::endl
              << "Connection URL format should be :" << std::endl
              << " For TCP : tcp://[server_host][:server_port]" << std::endl
              << " For UDP : udp://[bind_host][:bind_port]" << std::endl
              << " For Serial : serial:///path/to/serial/dev[:baudrate]" << std::endl
              << "For example, to connect to the simulator use URL: udp://:14540" << std::endl;
}

/*************************Main Function**************************/
int main(int argc, char** argv)
{
    // Initialing variable
    Mavsdk mavsdk;
    std::string connection_url;
    ConnectionResult connection_result;
    Telemetry::AccelerationFrd imu_current;
    float acc_x = 0.0;
    float acc_y = 0.0;
    float acc_z = 0.0;

/**************************Connecting the vehicle*********************************/

    if (argc == 2) {
        connection_url = argv[1];
        connection_result = mavsdk.add_any_connection(connection_url);
    } else {
        usage(argv[0]);
        return 1;
    }

    if (connection_result != ConnectionResult::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Connection failed: " << connection_result
                  << NORMAL_CONSOLE_TEXT << std::endl;
        return 1;
    }

    std::cout << "Waiting to discover system..." << std::endl;
    auto prom = std::promise<std::shared_ptr<System>>{};
    auto fut = prom.get_future();

    mavsdk.subscribe_on_new_system([&mavsdk, &prom]() {
        auto system = mavsdk.systems().back();

        if (system->has_autopilot()) {
            std::cout << "Discovered autopilot" << std::endl;

            // Unsubscribe again as we only want to find one system.
            mavsdk.subscribe_on_new_system(nullptr);
            prom.set_value(system);
        }
    });

    // We usually receive heartbeats at 1Hz, therefore we should find a
    // system after around 3 seconds.
    if (fut.wait_for(seconds(3)) == std::future_status::timeout) {
        std::cout << ERROR_CONSOLE_TEXT << "No autopilot found, exiting." << NORMAL_CONSOLE_TEXT
                  << std::endl;
        return 1;
    }

    auto system = fut.get();

    auto telemetry = Telemetry{system};
    auto action = Action{system};
    auto mission = Mission(system);

/***********************************************************************************/

/**************************Subscribe Message and Callback***************************/

    // We want to listen to the altitude of the drone at 1 Hz in local coordinate system.
    const Telemetry::Result set_local_position_rate_result = telemetry.set_rate_position_velocity_ned(1.0);
    if (set_local_position_rate_result != Telemetry::Result::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Setting rate failed:" << set_local_position_rate_result
                  << NORMAL_CONSOLE_TEXT << std::endl;
        return 1;
    }

    // Set up callback to monitor altitude while the vehicle is in flight
    telemetry.subscribe_position_velocity_ned([](Telemetry::PositionVelocityNed local_position) {
        std::cout << TELEMETRY_CONSOLE_TEXT // set to blue
                  << "Local Altitude: " << local_position.position.down_m << " m"
                  << NORMAL_CONSOLE_TEXT // set to default color again
                  << std::endl;
    });

    // We want to listen to the altitude of the drone at 1 Hz in global coordinate system.
    const Telemetry::Result set_rate_result = telemetry.set_rate_position(1.0);
    if (set_rate_result != Telemetry::Result::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Setting rate failed:" << set_rate_result
                  << NORMAL_CONSOLE_TEXT << std::endl;
        return 1;
    }

    // Set up callback to monitor altitude while the vehicle is in flight
    telemetry.subscribe_position([](Telemetry::Position position) {
        std::cout << TELEMETRY_CONSOLE_TEXT // set to blue
                  << "Global Altitude: " << position.relative_altitude_m << " m"
                  << NORMAL_CONSOLE_TEXT // set to default color again
                  << std::endl;
    });

    // We want to listen to the acceleration of the drone at 5 Hz in body coordinate system(frd).
    const Telemetry::Result set_imu_rate_result = telemetry.set_rate_imu(5.0);
    if (set_imu_rate_result != Telemetry::Result::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Setting rate failed:" << set_imu_rate_result
                  << NORMAL_CONSOLE_TEXT << std::endl;
        return 1;
    }

    // Set up callback to monitor altitude while the vehicle is in flight
    telemetry.subscribe_imu([&acc_x, &acc_y, &acc_z](Telemetry::Imu imu) {
        acc_x = imu.acceleration_frd.forward_m_s2 ;
        acc_y = imu.acceleration_frd.right_m_s2 ;
        acc_z = imu.acceleration_frd.down_m_s2 ;
        // std::cout << TELEMETRY_CONSOLE_TEXT // set to blue
        //           << "ACC_X: " << acc_x << " m/s^2"
        //           << "ACC_Y: " << acc_y << " m/s^2"
        //           << "ACC_Z: " << acc_z << " m/s^2"
        //           << NORMAL_CONSOLE_TEXT // set to default color again
        //           << std::endl;

    });

/***********************************************************************************/

/********************************Flight Timing*************************************/
    // Check if vehicle is ready to arm
    // while (telemetry.health_all_ok() != true) {
    //     std::cout << "Vehicle is getting ready to arm" << std::endl;
    //     sleep_for(seconds(1));
    // }

/*************************Check for the fire signal**********************************/
    while(sqrt(acc_x * acc_x + acc_y * acc_y + acc_z * acc_z) < 15)
    {
        sleep_for(std::chrono::milliseconds(200));
        std::cout << "Waiting for launch..." << std::endl;
    }
/********************************Unfold Arms*************************************/
    std::cout << "Sleeping for 10 seconds..." << std::endl;
    sleep_for(seconds(10));

    std::cout << "Unfolding Arms...\n";
    const Action::Result set_actuator0_result = action.set_actuator(1, 1.0);

    if (set_actuator0_result != Action::Result::Success) {
        std::cerr << "Setting actuator failed:" << set_actuator0_result << '\n';
        return 1;
    }
    std::cout << "Unfolded Arms !!!\n";
/*************************Arm vehicle**********************************/
    std::cout << "Sleeping for 2 seconds..." << std::endl;
    sleep_for(seconds(2));
    std::cout << "Arming..." << std::endl;
    const Action::Result arm_result = action.arm();

    if (arm_result != Action::Result::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Arming failed:" << arm_result << NORMAL_CONSOLE_TEXT
                  << std::endl;
        return 1;
    }

/********************************Fire Parachute*************************************/
    std::cout << "Sleeping for 1 seconds..." << std::endl;
    sleep_for(seconds(1));

    std::cout << "Firing Parachute...\n";
    const Action::Result set_actuator1_result = action.set_actuator(2, 0.8);

    if (set_actuator1_result != Action::Result::Success) {
        std::cerr << "Setting actuator failed:" << set_actuator1_result << '\n';
        return 1;
    }
    std::cout << "Fired Parachute !!!\n";

/***************************Started Mission***************************************/
{
    std::cout << "Creating and uploading mission" << std::endl;

    std::vector<Mission::MissionItem> mission_items;

    mission_items.push_back(make_mission_item(
        47.398570327054473,
        8.5459490218639658,
        0.0f,
        5.0f,
        false,
        20.0f,
        60.0f,
        Mission::MissionItem::CameraAction::None));

    {
        std::cout << "Uploading mission..." << std::endl;
        // We only have the upload_mission function asynchronous for now, so we wrap it using
        // std::future.
        auto prom = std::make_shared<std::promise<Mission::Result>>();
        auto future_result = prom->get_future();
        Mission::MissionPlan mission_plan{};
        mission_plan.mission_items = mission_items;
        mission.upload_mission_async(
            mission_plan, [prom](Mission::Result result) { prom->set_value(result); });

        const Mission::Result result = future_result.get();
        if (result != Mission::Result::Success) {
            std::cout << "Mission upload failed (" << result << "), exiting." << std::endl;
            return 1;
        }
        std::cout << "Mission uploaded." << std::endl;
    }


    // Before starting the mission, we want to be sure to subscribe to the mission progress.
    mission.subscribe_mission_progress([](Mission::MissionProgress mission_progress) {
        std::cout << "Mission status update: " << mission_progress.current << " / "
                  << mission_progress.total << std::endl;
    });

    {
        std::cout << "Starting mission." << std::endl;
        auto prom = std::make_shared<std::promise<Mission::Result>>();
        auto future_result = prom->get_future();
        mission.start_mission_async([prom](Mission::Result result) {
            prom->set_value(result);
            std::cout << "Started mission." << std::endl;
        });

        const Mission::Result result = future_result.get();
        handle_mission_err_exit(result, "Mission start failed: ");
    }


    while (!mission.is_mission_finished().second) {
        sleep_for(seconds(1));
    }
}
    /**************************************************************/

    std::cout << "Landing..." << std::endl;
    const Action::Result land_result = action.land();
    if (land_result != Action::Result::Success) {
        std::cout << ERROR_CONSOLE_TEXT << "Land failed:" << land_result << NORMAL_CONSOLE_TEXT
                  << std::endl;
        return 1;
    }

    // Check if vehicle is still in air
    while (telemetry.in_air()) {
        std::cout << "Vehicle is landing..." << std::endl;
        sleep_for(seconds(1));
    }
    std::cout << "Landed!" << std::endl;

    // We are relying on auto-disarming but let's keep watching the telemetry for a bit longer.
    sleep_for(seconds(3));
    std::cout << "Finished..." << std::endl;

    return 0;
}

inline void handle_action_err_exit(Action::Result result, const std::string& message)
{
    if (result != Action::Result::Success) {
        std::cerr << ERROR_CONSOLE_TEXT << message << result << NORMAL_CONSOLE_TEXT << std::endl;
        exit(EXIT_FAILURE);
    }
}

inline void handle_mission_err_exit(Mission::Result result, const std::string& message)
{
    if (result != Mission::Result::Success) {
        std::cerr << ERROR_CONSOLE_TEXT << message << result << NORMAL_CONSOLE_TEXT << std::endl;
        exit(EXIT_FAILURE);
    }
}

// Handles connection result
inline void handle_connection_err_exit(ConnectionResult result, const std::string& message)
{
    if (result != ConnectionResult::Success) {
        std::cerr << ERROR_CONSOLE_TEXT << message << result << NORMAL_CONSOLE_TEXT << std::endl;
        exit(EXIT_FAILURE);
    }
}

Mission::MissionItem make_mission_item(
    double latitude_deg,
    double longitude_deg,
    float relative_altitude_m,
    float speed_m_s,
    bool is_fly_through,
    float gimbal_pitch_deg,
    float gimbal_yaw_deg,
    Mission::MissionItem::CameraAction camera_action)
{
    Mission::MissionItem new_item{};
    new_item.latitude_deg = latitude_deg;
    new_item.longitude_deg = longitude_deg;
    new_item.relative_altitude_m = relative_altitude_m;
    new_item.speed_m_s = speed_m_s;
    new_item.is_fly_through = is_fly_through;
    new_item.gimbal_pitch_deg = gimbal_pitch_deg;
    new_item.gimbal_yaw_deg = gimbal_yaw_deg;
    new_item.camera_action = camera_action;
    return new_item;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

旋翼回收火箭系列博客3——控制系统设计(PX4火箭) 的相关文章

随机推荐

  • Linux下使用U盘

    第一步 xff1a 插入U盘 xff0c 如果能够识别出U盘 xff0c 则会打印出一些信息 xff1b 第二步 xff1a 查看U盘系统分配给U盘的设备名 xff1b 输入如下命令进行查看 xff1a fdisk l dev sda 如果
  • (Deep Learning)交叉验证(Cross Validation)

    交叉验证 xff08 Cross Validation xff09 交叉验证 xff08 Cross Validation xff09 是一种评估模型泛化性能的统计学方法 xff0c 它比单次划分训练集和测试集的方法更加稳定 全面 交叉验证
  • (Linux)在Ubuntu系统中添加新用户并授予root权限

    向Ubuntu系统中添加新用户并为其授予root权限的步骤如下 打开终端Terminal 输入命令 sudo su 以 root 身份登录 注 sudo su 切换root身份 不携带当前用户环境变量 sudo su 切换root身份 携带
  • (深度学习)类别不平衡数据集中IOU和mIOU的选择

    测试集上的mIOU很高 xff0c 但是实际的分割结果很差 xff0c 几乎没有分割出前景 xff0c 主要是因为要分割的目标占总面积之比太少 xff0c 即出现样本不均衡的问题 此时 xff0c 前景所占的比例太小 xff0c 背景所占的
  • 系统调用,API,运行库函数和C标准库函数的区别

    1 为什么用户程序不能直接访问系统内核模式提供的服务 xff1f 在linux中 xff0c 将程序的运行空间分为内核与用户空间 xff08 内核态和用户态 xff09 xff0c 在逻辑上它们之间是相互隔离的 xff0c 因此用户程序不能
  • 学习四旋翼(二):控制方法之串级PID与卡尔曼滤波(含MATLAB示例)

    暑假期间 xff0c 对于四旋翼有一点兴趣 xff0c 没有亲手做 xff0c 但是看了一些资料 这个系列文章只是对自己看的东西的记录 xff0c 对于想要学习了解相关知识的同学没有任何参考价值 xff01 本篇是系列的第二部分 xff1a
  • Git tag标签与branch分支 区别

    Git中的分支和标签有点类似 xff0c 都是引用或者说指针 关于Git引用可以参阅Git References一章节 一 相似的地方 xff1a 图示如下 xff1a heads和tags文件夹存储的是具体分支和标签 xff1a tags
  • 关于字符串结束符'\0'

    字符串结束符 xff1a 39 0 39 xff0c 其本质就是8位的 0000 0000 xff0c 而字符类型中并没有这个字符 xff08 注意与ASCLL码区别 xff0c 在ASCLL中000 代表NULL xff09 所以用0的转
  • extern “C”的作用详解

    extern 34 C 34 的主要作用就是为了能够正确实现C 43 43 代码调用其他C语言代码 加上extern 34 C 34 后 xff0c 会指示编译器这部分代码按C语言的进行编译 xff0c 而不是C 43 43 的 由于C 4
  • Lab2 p3 围棋吃子的算法实现

    简单介绍下框架 xff1a 1 xff0e 声明一维数组block 作为一个临时变量记录一个块的大小 xff0c 声明一个整型blockLength记录这个块的长度 2 xff0e kill 为吃子的主函数 recersion int i
  • Python爬取皮皮虾视频

    背景 xff1a 今天闲着没事做 xff0c 然后想着刷刷视频 xff0c 然后发现前段时间学习了一下网络爬虫的一些基本应用 xff0c 就想着利用爬虫到网上去爬取一点视频来模拟人为的点击 下载操作 因为皮皮虾是手机端的app xff0c
  • C语言——全局变量的定义与声明

    转自 xff1a https www cnblogs com amanlikethis p 3319744 html C语言中全局变量的定义与声明困扰着许多C语言初学者 本文讲述了全局变量定义与声明的用法 xff0c 而且本为也将阐述这种用
  • ResourceNotFound:xxx roslaunch找不到包

    执行命令 xff1a roslaunch xxx 出现如下错误 错误原因 xff1a 这里错误的原因可能有两个 原因1 xff1a ROS path n 没有你的包所在的路径 解决方法 xff1a 对ros path 进行配置 1 xff1
  • 单片机对底层寄存器的操作

    最近项目用到了国产的一款单片机 xff0c 没有例程的支持 xff0c 需要自己从头开始写底层 又感受到了自己本科刚学习51的时候的浮躁 xff0c 不懂得如何操作底层的寄存器 xff0c 只是一味的抄写别人的例程 xff0c 然后进行简单
  • PyQt5自学记录(1)——PyQt5多线程实现详解

    PyQt5自学记录 xff08 1 xff09 PyQt5中多线程实现详解 最近想用PyQt5完成图像识别的一个GUI系统 xff0c 在调用算法模型进行识别的时候 xff0c 界面会卡住没有反应 xff0c 所以想学习一下多线程解决这个问
  • 编写程序的步骤

    编写 C 语言程序的7个步骤 1 定义程序的目标 资深程序员需要养成的良好的思考习惯 在动手写程序之前 xff0c 要在脑中有清晰的思路 想要程序去做什么 1 首先自己要明确自己想做什么 xff0c 2 思考你的程序需要哪些信息 xff0c
  • 看懂英文数据手册、搭建电路

    阅读数据手册是一个工程师的必备技能 xff0c 拿到一份数据手册 xff0c 特别是英文数据手册 xff0c 如何去读 xff0c 才能更快更好的找到自己想要的东西 xff1f 坚信 xff1a 阅读英文手册 xff0c 并没有想象的那么难
  • 英语四级重点短语

    devote to 将 致力于 derive from 61 originate from 61 stem from 源自于 instant adj 立即的 速溶的 instant coffee速溶咖啡 instant noodle 方便面
  • stm32串口通信的一个小总结(从底层进行理解)

    从底层理解stm32USART串口通信 以前学串口通信踩过很多坑 xff0c 过了一段时间又有些忘了 xff0c 现在问了几个很强很强的人差不多弄懂了 xff0c 现在写一写总结 xff0c 免得以后又忘了 基本知识 xff1a 1 TDR
  • 旋翼回收火箭系列博客3——控制系统设计(PX4火箭)

    绪论 为了缩短研制周期和提高产品可靠性 xff0c 本系统采用商用开源自动驾驶仪PX4 xff0c 实现旋翼空中展开并回收的功能 PX4是全球最为成熟的开源自动驾驶仪 xff0c 可实现自动起飞 降落 执行航点等基本任务 然而此次火箭比赛要