ArduSub程序学习(2)--ArduSub主程序讲解

前言

本次对于ardusub主程序的讲解以sub4.1版本为主,每个版本的程序会有些差距,在讲解的过程中我会对比不同版本的进行学习,大家可以在github里面直接观看程序学习。ardupilot/ArduSub at Sub-4.1 · ArduPilot/ardupilot (github.com)

ardusub.cpp

首先打开ArduPilot/ArduSub/ArduSub.cpp,开头是#include "Sub.h"一个包含头文件的处理,

然后#define SCHED_TASK(func, rate_hz, max_time_micros) SCHED_TASK_CLASS(Sub, &sub, func, rate_hz, max_time_micros)是一个关于任务调度函数的宏定义。

1.头文件包含和宏定义

#include "Sub.h"//包含Sub.h文件

#define SCHED_TASK(func, rate_hz, max_time_micros) SCHED_TASK_CLASS(Sub, &sub, func, rate_hz, max_time_micros)
//定义了一个名为 SCHED_TASK 的宏。这个宏接受五个参数:func(函数名)、rate_hz(调用频率)、max_time_micros(最大执行时间)它使用了另一个名为 SCHED_TASK_CLASS 的宏来展开,后面会解释。

2.调度任务表格

接下来开始进行任务调度,这是一个 scheduler_tasks 数组的定义,它是 AP_Scheduler::Task 类型的数组。数组中的每个元素都是一个调度任务,每个任务由 SCHED_TASK 宏展开成相应的参数。关于任务调度方面的学习,大家可以去了解一下freertos,能快速的学习关于一些嵌入式的知识。

const AP_Scheduler::Task Sub::scheduler_tasks[] = {
    SCHED_TASK(fifty_hz_loop,         50,     75),
    SCHED_TASK_CLASS(AP_GPS, &sub.gps, update, 50, 200),
    // 更多任务项...
    SCHED_TASK(read_airspeed,          10,    100),
};

3.SCHED_TASK_CLASS 宏

SCHED_TASK 宏将任务的函数名、调用频率和最大执行时间传递给 SCHED_TASK_CLASS 宏。根据代码的上下文推测,SCHED_TASK_CLASS 宏可能是一个用于将任务与特定类的成员函数绑定的宏。例如,可能的实现是类似这样的:

#define SCHED_TASK_CLASS(cls, obj, func, rate_hz, max_time_micros) \
    { &cls::func, obj, rate_hz, max_time_micros }

这里的 SCHED_TASK_CLASS 宏会返回一个结构体或类的实例,表示一个调度任务,包括函数指针、对象指针、调用频率和最大执行时间。

4.任务定义

scheduler_tasks 数组中,每个 SCHED_TASK 宏都对应一个具体的任务。例如:

SCHED_TASK(fifty_hz_loop, 50, 75)

表示一个名为 fifty_hz_loop 的函数,调用频率为 50Hz,最大执行时间为 75微秒。

5.条件编译

代码中还包含了一些条件编译的部分(例如 #if#ifdef),这些部分根据预定义的宏(如 OPTFLOW, AC_FENCE, CAMERA 等)来决定是否包含某些特定的调度任务。

6.获取任务调度信息

void Sub::get_scheduler_tasks这段代码不一样的版本会有所不同,有的(sub3.5)上来直接是void Sub::setup()一个初始化函数。

void Sub::get_scheduler_tasks(const AP_Scheduler::Task *&tasks,//tasks 是一个指向 AP_Scheduler::Task 类型数组的常量指针引用。该函数通过这个参数返回调度任务数组的首地址。
                              uint8_t &task_count,//task_count 是一个 uint8_t 类型的引用,用来返回调度任务数组中的任务数量
                              uint32_t &log_bit)//log_bit 是一个 uint32_t 类型的引用,用来返回日志位掩码的值。
{
    tasks = &scheduler_tasks[0];//tasks = &scheduler_tasks[0]; 将 tasks 设置为指向 scheduler_tasks 数组的第一个元素的地址。
    task_count = ARRAY_SIZE(scheduler_tasks);//将 task_count 设置为 scheduler_tasks 数组的元素个数。
    log_bit = MASK_LOG_PM;//log_bit = MASK_LOG_PM; 将 log_bit 设置为 MASK_LOG_PM 的值。
}

7.fast_loop()

这段代码的作用是在主循环中以400Hz的频率执行各种任务和控制器,包括更新惯性导航系统、执行姿态控制、发送电机输出、运行状态估计器(EKF)、处理惯性导航数据、更新航向控制等。

里面很多函数是比较重要的,建议大家去跳转到函数定义去阅读以下函数定义。

// Main loop - 400hz
void Sub::fast_loop()
{
   //用于更新惯性导航系统(INS),以获取当前陀螺仪数据。
    ins.update();

   //如果 control_mode 不是手动模式(MANUAL)或电机检测模式(MOTOR_DETECT),则调用姿态控制对象 attitude_control 的 rate_controller_run() 方法来运行低级速率控制器,这些控制器只需使用IMU数据。
    if (control_mode != MANUAL && control_mode != MOTOR_DETECT) {
      
        attitude_control.rate_controller_run();
    }

     //调用 motors_output() 函数,将输出发送到电机库,这可能是根据控制器计算得出的电机输出。
    motors_output();

    //调用 read_AHRS() 函数,用于运行扩展卡尔曼滤波(EKF)状态估计器,这是一个计算量较大的操作,用于估计飞行器的状态。
    read_AHRS();

    //调用 read_inertia() 函数,执行惯性导航相关的处理,可能包括获取加速度计和陀螺仪的数据来计算位置和速度等信息。
    read_inertia();

     //调用 check_ekf_yaw_reset() 函数,检查 EKF 是否重置了目标航向。
    check_ekf_yaw_reset();

    //执行姿态控制器相关的更新。
    update_flight_mode();

    // 根据 EKF 的信息更新家庭位置。
    update_home_from_EKF();

    // 检查是否到达水面或底部。
    update_surface_and_bottom_detector();

#if HAL_MOUNT_ENABLED
    //用于快速更新相机挂载状态。
    camera_mount.update_fast();
#endif

    // 如果需要记录任何传感器日志,调用 Log_Sensor_Health() 函数,记录传感器的健康状态。
    if (should_log(MASK_LOG_ANY)) {
        Log_Sensor_Health();
    }
    //最后,调用基类 AP_Vehicle 的 fast_loop() 方法,执行可能与飞行器或车辆通用的其他处理。
    AP_Vehicle::fast_loop();
}

里面需要注意的是这段代码

    //don't run rate controller in manual or motordetection modes
    if (control_mode != MANUAL && control_mode != MOTOR_DETECT) {
        // run low level rate controllers that only require IMU data
        attitude_control.rate_controller_run();
    }

我们转到函数定义的地方去观察一下libraries/AC_AttitudeControl/AC_AttitudeControl.cpp

void AC_AttitudeControl_Sub::rate_controller_run()
{
    // move throttle vs attitude mixing towards desired (called from here because this is conveniently called on every iteration)
    update_throttle_rpy_mix();

    // 获取最新的陀螺仪数据
    Vector3f gyro_latest = _ahrs.get_gyro_latest();

    // 使用角速率PID控制器更新每个轴的控制输出
    _motors.set_roll(get_rate_roll_pid().update_all(_rate_target_ang_vel.x, gyro_latest.x, _motors.limit.roll));
    _motors.set_pitch(get_rate_pitch_pid().update_all(_rate_target_ang_vel.y, gyro_latest.y, _motors.limit.pitch));
    _motors.set_yaw(get_rate_yaw_pid().update_all(_rate_target_ang_vel.z, gyro_latest.z, _motors.limit.yaw));

    // 更新控制监视器
    control_monitor_update();
}
  • 对于每个轴(横滚、俯仰、偏航),使用相应的角速率PID控制器更新控制输出:
    • get_rate_roll_pid().update_all():使用横滚角速率PID控制器,传入目标角速率、当前陀螺仪测量值和限制范围,更新横滚轴的输出。
    • get_rate_pitch_pid().update_all():使用俯仰角速率PID控制器,传入目标角速率、当前陀螺仪测量值和限制范围,更新俯仰轴的输出。
    • get_rate_yaw_pid().update_all():使用偏航角速率PID控制器,传入目标角速率、当前陀螺仪测量值和限制范围,更新偏航轴的输出。
  • _motors 可能是控制电机或执行器的对象,通过 set_roll()set_pitch()set_yaw() 方法设置每个轴的控制输出。
  • set_roll()、set_pitch()、set_yaw():接受 -4500 范围内的横滚、俯仰和偏航值 ~ 4500. 这些不是理想的角度或甚至速率,而只是 一个值。例如,set_roll(-4500) 表示向左滚动为 越快越好。

 ArduSub.cpp其余函数解释

  • fifty_hz_loop()

    • 作用: 按照50Hz的频率执行一系列任务,主要涉及故障检查和RC输入输出的更新。
    • 具体步骤:
      • failsafe_pilot_input_check():检查飞行员输入的故障。
      • failsafe_crash_check():检查是否发生了碰撞。
      • failsafe_ekf_check():检查扩展卡尔曼滤波器(EKF)的状态。
      • failsafe_sensors_check():检查传感器的状态。
      • rc().read_input():更新遥控器输入。
      • SRV_Channels::output_ch_all():更新所有输出通道。
  • update_batt_compass()

    • 作用: 按照10Hz的频率读取电池和罗盘数据。
    • 具体步骤:
      • battery.read():读取电池状态。
      • 如果罗盘被启用 (AP::compass().enabled()),则执行以下操作:
        • compass.set_throttle(motors.get_throttle()):设置罗盘的油门值,用于罗盘电机干扰补偿。
        • compass.read():读取罗盘数据。
  • ten_hz_logging_loop()

    • 作用: 按照10Hz的频率记录日志数据。
    • 具体步骤:
      • 根据日志需求记录姿态数据、PID控制器信息、电机电池数据、遥控器输入输出、位置控制器相关数据以及IMU的振动信息。
  • twentyfive_hz_logging()

    • 作用: 按照25Hz的频率记录日志数据。
    • 具体步骤:
      • 根据日志需求记录姿态数据、PID控制器信息以及IMU的数据。
  • three_hz_loop()

    • 作用: 按照3.3Hz的频率执行一系列故障检查和事件更新。
    • 具体步骤:
      • 执行泄漏检测、内部压力和温度检查、地面站连接检查、地形数据丢失检查,以及如果启用了电子围栏则执行围栏检查。
  • one_hz_loop()

    • 作用: 按照1Hz的频率执行一系列初始化和状态更新操作。
    • 具体步骤:
      • 执行预启动检查、更新姿态传感器方向、更新电机油门范围、启用辅助舵机通道、更新位置控制器的高度限制、记录地形数据等。
  • read_AHRS()

    • 作用: 执行IMU计算并获取姿态信息。
    • 具体步骤:
      • 调用 ahrs.update(true)ahrs_view.update(true) 方法来更新姿态信息,参数 true 表示跳过INS更新,因为在 fast_loop() 方法中已经执行了。
  • update_altitude()

    • 作用: 按照10Hz的频率更新高度相关数据。
    • 具体步骤:
      • 调用 read_barometer() 读取气压计数据,并根据日志需求记录控制调谐和陀螺仪数据。
  • control_check_barometer()

    • 作用: 检查气压计的状态。
    • 具体步骤:
      • 如果没有深度传感器或者传感器健康状态失败,则发送警告信息。
  • get_wp_distance_m()get_wp_bearing_deg()get_wp_crosstrack_error_m()

    • 作用: 提供特定于车辆的航点信息的帮助方法。
    • 具体步骤:
      • 提供到目标航点的距离、方位角和横跨误差。
  • AP_HAL_MAIN_CALLBACKS(&sub);

    • 作用: 注册主回调函数。
    • 具体步骤:
      • &sub 注册为主回调函数,这表明 sub 对象的方法将作为主回调在整个程序执行期间被调用。

flight_mode.cpp 

对于fast_loop里面的update_flight_mode();我们转到函数定义去看看ArduSub/flight_mode.cpp

void Sub::update_flight_mode()
{
    switch (control_mode) {
    case ACRO:
        acro_run();
        break;

    case STABILIZE:
        stabilize_run();
        break;

    case ALT_HOLD:
        althold_run();
        break;

    case AUTO:
        auto_run();
        break;

    case CIRCLE:
        circle_run();
        break;

    case GUIDED:
        guided_run();
        break;

    case SURFACE:
        surface_run();
        break;

#if POSHOLD_ENABLED == ENABLED
    case POSHOLD:
        poshold_run();
        break;
#endif

    case MANUAL:
        manual_run();
        break;

    case MOTOR_DETECT:
        motordetect_run();
        break;

    default:
        break;
    }
}
  • switch-case 结构:

    • switch (control_mode) 开始了一个根据 control_mode 变量值的多分支选择结构。
  • 不同的飞行模式:

    • 每个 case 分支代表了一个可能的飞行模式,例如 ACROSTABILIZEALT_HOLD 等。
    • 每个分支中调用对应的 *_run() 方法来执行特定飞行模式的逻辑。
    • 下面是每个飞行模式对应的处理函数示例:
      • acro_run():执行Acro(特技)模式的逻辑。
      • stabilize_run():执行Stabilize(稳定)模式的逻辑。
      • althold_run():执行Altitude Hold(高度保持)模式的逻辑。
      • auto_run():执行Auto(自动)模式的逻辑。
      • circle_run():执行Circle(环绕)模式的逻辑。
      • guided_run():执行Guided(引导)模式的逻辑。
      • surface_run():执行Surface(上升)模式的逻辑。
      • poshold_run():执行PosHold(位置保持)模式的逻辑(如果启用)。
      • manual_run():执行Manual(手动)模式的逻辑。
      • motordetect_run():执行Motor Detect(电机检测)模式的逻辑。
  • default 分支:

    • 如果 control_mode 的值不在上述列举的任何一个飞行模式中,则执行 default: 分支。
    • 默认情况下,update_flight_mode() 方法不执行任何操作,直接跳出 switch-case 结构。

各个模式宏定义于defines.h中

// Auto Pilot Modes enumeration
enum control_mode_t : uint8_t {
    STABILIZE =     0,  // manual angle with manual depth/throttle
    ACRO =          1,  // manual body-frame angular rate with manual depth/throttle
    ALT_HOLD =      2,  // manual angle with automatic depth/throttle
    AUTO =          3,  // fully automatic waypoint control using mission commands
    GUIDED =        4,  // fully automatic fly to coordinate or fly at velocity/direction using GCS immediate commands
    CIRCLE =        7,  // automatic circular flight with automatic throttle
    SURFACE =       9,  // automatically return to surface, pilot maintains horizontal control
    POSHOLD =      16,  // automatic position hold with manual override, with automatic throttle
    MANUAL =       19,  // Pass-through input with no stabilization
    MOTOR_DETECT = 20   // Automatically detect motors orientation
};

 

  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ardusub是一个基于开源硬件平台的水下机器人控制系统,而Raspbian是一种基于Debian的树莓派操作系统。ardusub raspbian则是指使用树莓派操作系统来运行ardusub水下机器人控制系统。 首先,树莓派是一种廉价、小巧且易于使用的计算机,非常适合作为嵌入式系统的控制器。树莓派上运行的Raspbian操作系统提供了丰富的开发工具和软件库,可以轻松地进行系统配置和管理。 ardusub是一个基于ArduPilot开源飞行控制系统的水下机器人控制系统。它支持多种传感器和执行器,可以实现水下机器人的定位、控制和任务执行等功能。通过与树莓派上运行的Raspbian操作系统相结合,可以更方便地对水下机器人进行控制和管理。 在ardusub raspbian系统中,树莓派可以作为主控制器来运行ardusub的软件代码,并接收传感器数据并发送控制信号给执行器。树莓派上的Raspbian操作系统可以提供图形界面和网络连接等功能,方便用户对水下机器人进行远程控制和监测。 此外,通过在树莓派上安装ardusub raspbian系统,用户还可以利用树莓派的扩展性,结合其他硬件和软件来扩展水下机器人的功能。例如,可以通过添加摄像头、传感器和通信模块等来提高水下机器人的感知能力和通信能力。 总之,ardusub raspbian提供了一个方便、灵活和可定制的水下机器人控制系统解决方案,利用树莓派的优势和Raspbian操作系统的丰富功能,可以实现水下机器人的高效控制和管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值