APM_ArduCopter源码解析学习(一)——ArduCopter主程序

ArduCopter源码解析学习(一)——ArduCopter主程序


前言

由于项目经常需要做功能优化所以开源飞控APM和PX4作为最热门的飞控资源,需要针对性的进行梳理。

一、准备工作

参考https://blog.csdn.net/moumde/article/details/108814986 在网上查了很多资料,在阅读本文内容之前,请仔细阅读Ardupilot的官方手册,建立起一个大概的印象即可,这里便不再讲述一些基础知识。

Github源码戳这里~
Ardusub官方手册
Ardupilot官方手册
Ardupilot开发者手册

由于源码内容很多,此次我仅挑选 “ardupilot/Copter/” 路径下的Copter.cpp主文件进行解析,后续如果有时间再慢慢补充其他内容。如果自己下载源码,推荐使用Souce Insight或者understand进行阅读。

二、Copter.cpp解析

2.1 scheduler table

SCHED_TASK_CLASS和SCHED_TASK用来声明产生调度任务表,表中是除了被fast_loop()函数调用任务的其他常规任务。其中_rate_hz表示调用频率,_max_time_micros表示最长等待时间。

#define SCHED_TASK_CLASS(classname, classptr, func, _rate_hz, _max_time_micros)

 
 
  • 1
#define SCHED_TASK(func, rate_hz, max_time_micros) SCHED_TASK_CLASS(Sub, &sub, func, rate_hz, max_time_micros)

 
 
  • 1

注意到SCHED_TASK_CLASS可以指向不同类,而SCHED_TASK固定为Copter类中的函数实现,Copter为无人机类型。

2.2 scheduler

get_scheduler_tasks()

如果是master版本的,程序前面应该是get_scheduler_tasks()

void Copter::get_scheduler_tasks(const AP_Scheduler::Task *&tasks,
                                 uint8_t &task_count,
                                 uint32_t &log_bit)
{
    tasks = &scheduler_tasks[0];
    task_count = ARRAY_SIZE(scheduler_tasks);
    log_bit = MASK_LOG_PM;
}

该函数主要实现获取调度表其实地址,并且计算出任务数量。

2.3 fast_loop()

void Copter::fast_loop()
{
    // update INS immediately to get current gyro data populated
    ins.update();//AP_InertialSensor &ins()

    // run low level rate controllers that only require IMU data
    attitude_control->rate_controller_run();

    // send outputs to the motors library immediately
    motors_output();

    // run EKF state estimator (expensive)
    // --------------------
    read_AHRS();

#if FRAME_CONFIG == HELI_FRAME
    update_heli_control_dynamics();
    #if MODE_AUTOROTATE_ENABLED == ENABLED
        heli_update_autorotation();
    #endif
#endif //HELI_FRAME

    // Inertial Nav
    // --------------------
    read_inertia();

    // check if ekf has reset target heading or position
    check_ekf_reset();

    // run the attitude controllers
    update_flight_mode();

    // update home from EKF if necessary
    update_home_from_EKF();

    // check if we've landed or crashed
    update_land_and_crash_detectors();

#if HAL_MOUNT_ENABLED
    // camera mount's fast update
    camera_mount.update_fast();
#endif

    // log sensor health
    if (should_log(MASK_LOG_ANY)) {
        Log_Sensor_Health();
    }

    AP_Vehicle::fast_loop();
}

该函数为本文件中最重要的函数,以400Hz频率运行。
attitude_control.rate_controller_run()在libraries/AC_AttitudeControl/AC_AttitudeControl_Multi.cpp中。

void AC_AttitudeControl_Multi::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();

    _rate_target_ang_vel += _rate_sysid_ang_vel;

    Vector3f gyro_latest = _ahrs.get_gyro_latest();

    _motors.set_roll(get_rate_roll_pid().update_all(_rate_target_ang_vel.x, gyro_latest.x, _motors.limit.roll) + _actuator_sysid.x);
    _motors.set_roll_ff(get_rate_roll_pid().get_ff());

    _motors.set_pitch(get_rate_pitch_pid().update_all(_rate_target_ang_vel.y, gyro_latest.y, _motors.limit.pitch) + _actuator_sysid.y);
    _motors.set_pitch_ff(get_rate_pitch_pid().get_ff());

    _motors.set_yaw(get_rate_yaw_pid().update_all(_rate_target_ang_vel.z, gyro_latest.z, _motors.limit.yaw) + _actuator_sysid.z);
    _motors.set_yaw_ff(get_rate_yaw_pid().get_ff()*_feedforward_scalar);

    _rate_sysid_ang_vel.zero();
    _actuator_sysid.zero();

    control_monitor_update();
}

主要是用set_xxx()方法接收roll/pitch/yaw的-1~1的值,这些值表示的是期望尽快往哪个方向运动的意思。具体查看这里

这里主要讲一下最主要的姿态控制函数update_flight_mode()。

2.3.1 mode.cpp

update_flight_mode()定义于Copter文件路径下的mode.cpp,该函数通常以100Hz及以上频率被调用。内部的 flightmode 定义于mode.h中,用来表示不同的飞行状态,共计27种模式。

在mode.cpp中update_flight_mode()的功能函数如下:
void Copter::update_flight_mode()
{
    surface_tracking.invalidate_for_logging();  // invalidate surface tracking alt, flight mode will set to true if used

    flightmode->run();
}

在mode.h中定义了各个飞行模式的具体含义内容:

    // Auto Pilot Modes enumeration
    enum class Number : uint8_t {
        STABILIZE =     0,  // manual airframe angle with manual throttle
        ACRO =          1,  // manual body-frame angular rate with manual throttle
        ALT_HOLD =      2,  // manual airframe angle with automatic 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
        LOITER =        5,  // automatic horizontal acceleration with automatic throttle
        RTL =           6,  // automatic return to launching point
        CIRCLE =        7,  // automatic circular flight with automatic throttle
        LAND =          9,  // automatic landing with horizontal position control
        DRIFT =        11,  // semi-automous position, yaw and throttle control
        SPORT =        13,  // manual earth-frame angular rate control with manual throttle
        FLIP =         14,  // automatically flip the vehicle on the roll axis
        AUTOTUNE =     15,  // automatically tune the vehicle's roll and pitch gains
        POSHOLD =      16,  // automatic position hold with manual override, with automatic throttle
        BRAKE =        17,  // full-brake using inertial/GPS system, no pilot input
        THROW =        18,  // throw to launch mode using inertial/GPS system, no pilot input
        AVOID_ADSB =   19,  // automatic avoidance of obstacles in the macro scale - e.g. full-sized aircraft
        GUIDED_NOGPS = 20,  // guided mode but only accepts attitude and altitude
        SMART_RTL =    21,  // SMART_RTL returns to home by retracing its steps
        FLOWHOLD  =    22,  // FLOWHOLD holds position with optical flow without rangefinder
        FOLLOW    =    23,  // follow attempts to follow another vehicle or ground station
        ZIGZAG    =    24,  // ZIGZAG mode is able to fly in a zigzag manner with predefined point A and point B
        SYSTEMID  =    25,  // System ID mode produces automated system identification signals in the controllers
        AUTOROTATE =   26,  // Autonomous autorotation
    };

update_flight_mode()中对应的flightmode->run ()函数则是在同路径下的mode_xxx.cpp中定义。下面以ModeStabilize::run()为例进行讲解,因此我们需要进入Mode_stabilize.cpp中。

2.3.2 Mode_stabilize.cpp

注意到内部包含Mode_stabilize.cpp文件只有一个ModeStabilize::run()函数。

/*
 * Init and run calls for stabilize flight mode
 */

// stabilize_run - runs the main stabilize controller
// should be called at 100hz or more
void ModeStabilize::run()
{
    // apply simple mode transform to pilot inputs
    update_simple_mode();

    // convert pilot input to lean angles
    float target_roll, target_pitch;
    get_pilot_desired_lean_angles(target_roll, target_pitch, copter.aparm.angle_max, copter.aparm.angle_max);

    // get pilot's desired yaw rate
    float target_yaw_rate = get_pilot_desired_yaw_rate(channel_yaw->get_control_in());

    if (!motors->armed()) {
        // Motors should be Stopped
        motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::SHUT_DOWN);
    } else if (copter.ap.throttle_zero) {
        // Attempting to Land
        motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
    } else {
        motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
    }

    switch (motors->get_spool_state()) {
    case AP_Motors::SpoolState::SHUT_DOWN:
        // Motors Stopped
        attitude_control->set_yaw_target_to_current_heading();
        attitude_control->reset_rate_controller_I_terms();
        break;

    case AP_Motors::SpoolState::GROUND_IDLE:
        // Landed
        attitude_control->set_yaw_target_to_current_heading();
        attitude_control->reset_rate_controller_I_terms();
        break;

    case AP_Motors::SpoolState::THROTTLE_UNLIMITED:
        // clear landing flag above zero throttle
        if (!motors->limit.throttle_lower) {
            set_land_complete(false);
        }
        break;

    case AP_Motors::SpoolState::SPOOLING_UP:
    case AP_Motors::SpoolState::SPOOLING_DOWN:
        // do nothing
        break;
    }

    // call attitude controller
    attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw(target_roll, target_pitch, target_yaw_rate);

    // output pilot's throttle
    attitude_control->set_throttle_out(get_pilot_desired_throttle(),
                                       true,
                                       g.throttle_filt);
}

run()函数则是实现了具体的控制器功能,该函数应该以100Hz及以上频率被调用。

  • run()内部首先调用update_simple_mode()这个函数是进行通道的转换,目前理解不够深刻。

// update_simple_mode - rotates pilot input if we are in simple mode
void Copter::update_simple_mode(void)
{
    float rollx, pitchx;

    // exit immediately if no new radio frame or not in simple mode
    if (simple_mode == SimpleMode::NONE || !ap.new_radio_frame) {
        return;
    }

    // mark radio frame as consumed
    ap.new_radio_frame = false;

    if (simple_mode == SimpleMode::SIMPLE) {
        // rotate roll, pitch input by -initial simple heading (i.e. north facing)
        rollx = channel_roll->get_control_in()*simple_cos_yaw - channel_pitch->get_control_in()*simple_sin_yaw;
        pitchx = channel_roll->get_control_in()*simple_sin_yaw + channel_pitch->get_control_in()*simple_cos_yaw;
    }else{
        // rotate roll, pitch input by -super simple heading (reverse of heading to home)
        rollx = channel_roll->get_control_in()*super_simple_cos_yaw - channel_pitch->get_control_in()*super_simple_sin_yaw;
        pitchx = channel_roll->get_control_in()*super_simple_sin_yaw + channel_pitch->get_control_in()*super_simple_cos_yaw;
    }

    // rotate roll, pitch input from north facing to vehicle's perspective
    channel_roll->set_control_in(rollx*ahrs.cos_yaw() + pitchx*ahrs.sin_yaw());
    channel_pitch->set_control_in(-rollx*ahrs.sin_yaw() + pitchx*ahrs.cos_yaw());
}

接下来get_pilot_desired_lean_angles()用于将打杆量映射成角度。

// get_pilot_desired_angle - transform pilot's roll or pitch input into a desired lean angle
// returns desired angle in centi-degrees
void Mode::get_pilot_desired_lean_angles(float &roll_out, float &pitch_out, float angle_max, float angle_limit) const
{
    // throttle failsafe check
    if (copter.failsafe.radio || !copter.ap.rc_receiver_present) {
        roll_out = 0;
        pitch_out = 0;
        return;
    }
    // fetch roll and pitch inputs
    roll_out = channel_roll->get_control_in();
    pitch_out = channel_pitch->get_control_in();

    // limit max lean angle
    angle_limit = constrain_float(angle_limit, 1000.0f, angle_max);

    // scale roll and pitch inputs to ANGLE_MAX parameter range
    float scaler = angle_max/(float)ROLL_PITCH_YAW_INPUT_MAX;
    roll_out *= scaler;
    pitch_out *= scaler;

    // do circular limit
    float total_in = norm(pitch_out, roll_out);
    if (total_in > angle_limit) {
        float ratio = angle_limit / total_in;
        roll_out *= ratio;
        pitch_out *= ratio;
    }

    // do lateral tilt to euler roll conversion
    roll_out = (18000/M_PI) * atanf(cosf(pitch_out*(M_PI/18000))*tanf(roll_out*(M_PI/18000)));

    // roll_out and pitch_out are returned
}
  • 在执行完上述两部分内容后,首先判断电机是否已经准备就绪,如果还没有,就进入if中的代码段

    • 首先是通过set_desired_spool_state()函数设置为期望的轴状态,此处设置为GROUOND_IDLE,即将所有电机闲置(lobraries/AP_Motors/AP_Motors_Class.h中SpoolState声明了3种轴状态:SHUT_DOWN关闭、GROUND_IDLE空闲和THROTTLE_UNLIMITED取消限制)。
  • 如果电机已经准备好了,那么就不进入if()的代码段中,首先设置轴状态为取消限制。get_pilot_desired_lean_angles(float roll_in, float pitch_in, float &roll_out, float &pitch_out, float angle_max)函数将获取roll和pitch的输入量然后将其转换为对应的以度单位表示的角度输出传输给在run()最开始设置的target_roll和target_pitch。get_pilot_desired_yaw_rate()函数将获取输入的yaw期望并且将其转换为°/s为单位yaw角速度输出并保存在target_yaw_rate中。

  • 然后进入姿态控制器,更新姿态至期望值。

    2.3.3 AC_AttitudeControl.cpp

    最后来看一下在libraries库中AC_AttitudeControl下AC_AttitudeControl.cpp中的input_euler_angle_roll_pitch_euler_rate_yaw()方法,其实大家看源码也能看出大概的意思了。

    这边也简单说一下,函数内部先把相应的角度转换为弧度制,然后计算出对于的期望欧拉角,并在欧拉角的roll向上增加侧倾调整以补偿推力,然后判断是否开启了前馈。是的话,还需要进行一系列计算,例如将摇杆期望欧拉角与前馈目标欧拉角进行差值计算以得到前馈角速度,还有将目标欧拉角速度转换为机体角速度等等;如果没有开启前馈,那么就直接将欧拉角输入到目标中,并根据目标欧拉角转换得出目标四元数,然后前馈率归零。最后调用四元数姿态控制器。

    总的来说,这个函数的功能就是通过角速度前馈和平滑来控制欧拉角和俯仰角以及欧拉偏航率。对于前面mode_stabilize.cpp中所用的input_euler_angle_roll_pitch_euler_rate_yaw则是通过角速度前馈和平滑控制欧拉角,俯仰角和偏航角。如果有兴趣继续研究,详看官方的手册

  • void AC_AttitudeControl::input_euler_angle_roll_pitch_euler_rate_yaw(float euler_roll_angle_cd, float euler_pitch_angle_cd, float euler_yaw_rate_cds)
    {
        // Convert from centidegrees on public interface to radians
        float euler_roll_angle = radians(euler_roll_angle_cd * 0.01f);
        float euler_pitch_angle = radians(euler_pitch_angle_cd * 0.01f);
        float euler_yaw_rate = radians(euler_yaw_rate_cds * 0.01f);
    
        // calculate the attitude target euler angles
        _attitude_target_quat.to_euler(_attitude_target_euler_angle.x, _attitude_target_euler_angle.y, _attitude_target_euler_angle.z);
    
        // Add roll trim to compensate tail rotor thrust in heli (will return zero on multirotors)
        euler_roll_angle += get_roll_trim_rad();
    
        if (_rate_bf_ff_enabled) {
            // translate the roll pitch and yaw acceleration limits to the euler axis
            Vector3f euler_accel = euler_accel_limit(_attitude_target_euler_angle, Vector3f(get_accel_roll_max_radss(), get_accel_pitch_max_radss(), get_accel_yaw_max_radss()));
    
            // When acceleration limiting and feedforward are enabled, the sqrt controller is used to compute an euler
            // angular velocity that will cause the euler angle to smoothly stop at the input angle with limited deceleration
            // and an exponential decay specified by smoothing_gain at the end.
            _attitude_target_euler_rate.x = input_shaping_angle(wrap_PI(euler_roll_angle - _attitude_target_euler_angle.x), _input_tc, euler_accel.x, _attitude_target_euler_rate.x, _dt);
            _attitude_target_euler_rate.y = input_shaping_angle(wrap_PI(euler_pitch_angle - _attitude_target_euler_angle.y), _input_tc, euler_accel.y, _attitude_target_euler_rate.y, _dt);
    
            // When yaw acceleration limiting is enabled, the yaw input shaper constrains angular acceleration about the yaw axis, slewing
            // the output rate towards the input rate.
            _attitude_target_euler_rate.z = input_shaping_ang_vel(_attitude_target_euler_rate.z, euler_yaw_rate, euler_accel.z, _dt);
    
            // Convert euler angle derivative of desired attitude into a body-frame angular velocity vector for feedforward
            euler_rate_to_ang_vel(_attitude_target_euler_angle, _attitude_target_euler_rate, _attitude_target_ang_vel);
            // Limit the angular velocity
            ang_vel_limit(_attitude_target_ang_vel, radians(_ang_vel_roll_max), radians(_ang_vel_pitch_max), radians(_ang_vel_yaw_max));
            // Convert body-frame angular velocity into euler angle derivative of desired attitude
            ang_vel_to_euler_rate(_attitude_target_euler_angle, _attitude_target_ang_vel, _attitude_target_euler_rate);
        } else {
            // When feedforward is not enabled, the target euler angle is input into the target and the feedforward rate is zeroed.
            _attitude_target_euler_angle.x = euler_roll_angle;
            _attitude_target_euler_angle.y = euler_pitch_angle;
            _attitude_target_euler_angle.z += euler_yaw_rate * _dt;
            // Compute quaternion target attitude
            _attitude_target_quat.from_euler(_attitude_target_euler_angle.x, _attitude_target_euler_angle.y, _attitude_target_euler_angle.z);
    
            // Set rate feedforward requests to zero
            _attitude_target_euler_rate = Vector3f(0.0f, 0.0f, 0.0f);
            _attitude_target_ang_vel = Vector3f(0.0f, 0.0f, 0.0f);
        }
    
        // Call quaternion attitude controller
        attitude_controller_run_quat();
    }
    

    2.4 其他

    无。

    三、总结

    以上便是本次的全部内容,下一篇打算讲讲Copter内部的底层电机控制原理。


    第一次更新:2020/12/1


    第二次更新:2020/12/2

  • 7
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值