ArduPilot源码阅读——上层运动模式篇(Guided for boat)

设置目标点

函数调用时机:这一目标点结束后调用;

如果目标点队列为[10,10];[20,20];[30,30],

首先调用set_desired_location([10,10], [20,20])

表示从原点(默认[0,0]),运动到[10,10],再运动到[20, 20],_fast_waypoint = 1

当运动到[10,10]后,再次调用该函数

set_desired_location([20,20], [30, 30])

表示从[10,10],运动到[20, 20],再运动到[30, 30],_fast_waypoint = 1

当运动到[20, 20]后,再调用

set_desired_location([30, 30], [])

表示[30,30]为终点,_fast_waypoint = 0

// set desired location and (optionally) next_destination
// next_destination should be provided if known to allow smooth cornering   
// 该函数在达到当前目标点(this_leg)后调用
bool AR_WPNav::set_desired_location(const Location& destination, Location next_destination)
{
    // 如果当前路径没有结束就调用该函数,则会先执行停止动作,以停止点为起点,重新开始计算路径
    if (!is_active() || !_reached_destination || (_nav_control_type != NavControllerType::NAV_SCURVE)) {
        if (!set_origin_and_destination_to_stopping_point()) {
            return false;
        }
        // clear scurves
        _scurve_prev_leg.init();
        _scurve_this_leg.init();
        _scurve_next_leg.init();
    }

    // shift this leg to previous leg
    _scurve_prev_leg = _scurve_this_leg;

    // initialise some variables
    _origin = _destination;
    _destination = destination;
    _orig_and_dest_valid = true;
    _reached_destination = false;

    update_distance_and_bearing_to_destination();

    // check if vehicle should pivot if vehicle stopped at previous waypoint
    // or journey to previous waypoint was interrupted or navigation has just started
    if (!_fast_waypoint) {  //_fast_waypoint下一个航路点是否快速通过(不停止)
        _pivot.deactivate();
        _pivot.check_activation((_reversed ? wrap_360_cd(oa_wp_bearing_cd() + 18000) : oa_wp_bearing_cd()) * 0.01, _pivot_at_next_wp);
    }

    // convert origin and destination to offset from EKF origin 
	// 把起点终点的经纬度转换成二维坐标系,用m为单位表示
    Vector2f origin_NE;
    Vector2f destination_NE;
    if (!_origin.get_vector_xy_from_origin_NE(origin_NE) ||
        !_destination.get_vector_xy_from_origin_NE(destination_NE)) {
        INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
        return false;
    }
    origin_NE *= 0.01f;
    destination_NE *= 0.01f;

    // calculate track to destination
    if (_fast_waypoint && !_scurve_next_leg.finished()) {   
	//下一点为快速点,并且下一段路径没有结束,表示下一段为连续路径,直接把计算好的路径拷贝当前路径
        // skip recalculating this leg by simply shifting next leg
        _scurve_this_leg = _scurve_next_leg;
    } else {
        _scurve_this_leg.
		calculate_track(Vector3f{origin_NE.x, origin_NE.y, 0.0f}, // origin
                        Vector3f{destination_NE.x, destination_NE.y, 0.0f},    // destination
                        _pos_control.get_speed_max(),
                        _pos_control.get_speed_max(),  // speed up (not used)      垂直方向 2D用不到
                        _pos_control.get_speed_max(),  // speed down (not used)    垂直方向 2D用不到
                        _pos_control.get_accel_max(),  // forward back acceleration
                        _pos_control.get_accel_max(),  // vertical accel (not used)垂直方向 2D用不到
                        AR_WPNAV_SNAP_MAX,             // snap 加加加速度
                        _pos_control.get_jerk_max());  // jerk 加加速度
    }

    // handle next destination
    _scurve_next_leg.init();
    _fast_waypoint = false;
    _pivot_at_next_wp = false;
    if (next_destination.initialised()) {   //检查下一个目标点是否有效?如果有效则计算下一路径
        // check if vehicle should pivot at next waypoint
        const float next_wp_yaw_change = get_corner_angle(_origin, destination, next_destination);
        _pivot_at_next_wp = _pivot.would_activate(next_wp_yaw_change);
        if (!_pivot_at_next_wp) {   //如果下一个目标点不进行原地转向,则计算路径
            // convert next_destination to offset from EKF origin
            Vector2f next_destination_NE;
            if (!next_destination.get_vector_xy_from_origin_NE(next_destination_NE)) {
                INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
                return false;
            }
            next_destination_NE *= 0.01f;
            _scurve_next_leg.
			calculate_track(Vector3f{destination_NE.x, destination_NE.y, 0.0f},
                            Vector3f{next_destination_NE.x, next_destination_NE.y, 0.0f},
                            _pos_control.get_speed_max(),
                            _pos_control.get_speed_max(),  // speed up (not used)
                            _pos_control.get_speed_max(),  // speed down (not used)
                            _pos_control.get_accel_max(),  // forward back acceleration
                            _pos_control.get_accel_max(),  // vertical accel (not used)
                            AR_WPNAV_SNAP_MAX,             // snap
                            _pos_control.get_jerk_max());

            // next destination provided so fast waypoint
            _fast_waypoint = true;	//同时把下一路径标记为快速路径
        }
    }

    // scurves used for navigation to destination
    _nav_control_type = NavControllerType::NAV_SCURVE;

    update_distance_and_bearing_to_destination();

    return true;
}

运动模式

void ModeGuided::update()
{
    switch (_guided_mode) {
        case SubMode::WP:	{...}

        case SubMode::HeadingAndSpeed:	{...}

        case SubMode::TurnRateAndSpeed:	{...}

        case SubMode::Loiter:	{...}

        case SubMode::SteeringAndThrottle:	{...}

        case SubMode::Stop:
            stop_vehicle();
            break;

        default:
            gcs().send_text(MAV_SEVERITY_WARNING, "Unknown GUIDED mode");
            break;
    }
}

目标点模式——WayPoint(WP)

整体流程

case SubMode::WP:
        {
            // check if we've reached the destination
            if (!g2.wp_nav.reached_destination()) {
                // update navigation controller
                navigate_to_waypoint();
            } else {
                // send notification
                if (send_notification) {
                    send_notification = false;
                    rover.gcs().send_mission_item_reached_message(0);
                }

                // we have reached the destination so stay here
                if (rover.is_boat()) {
                    if (!start_loiter()) {
                        stop_vehicle();
                    }
                } else {
                    stop_vehicle();
                }
                // update distance to destination
                _distance_to_destination = \
					rover.current_loc.get_distance(g2.wp_nav.get_destination());
            }
            break;
        }

具体细节

void Mode::navigate_to_waypoint()
{
    /* .... */
    // 上面可以忽略,用遥控器调速

    // update navigation controller
    g2.wp_nav.update(rover.G_Dt);   //***核心更新,重点研读***

    _distance_to_destination = g2.wp_nav.get_distance_to_destination(); //更新离航向点的距离

#if AP_AVOIDANCE_ENABLED
    /* 避障相关 */
#endif

    // pass desired speed to throttle controller
    // do not do simple avoidance because this is already handled in the position controller
    calc_throttle(g2.wp_nav.get_speed(), false);    //从期望速度,计算油门	-- 输出1

    float desired_heading_cd = g2.wp_nav.oa_wp_bearing_cd();    //从控制器获取期望航向角
    if (g2.sailboat.use_indirect_route(desired_heading_cd)) {   //如果是帆船 跳过不看
        /* 帆船控制 忽略 */
    } else {
        // retrieve turn rate from waypoint controller
        float desired_turn_rate_rads = g2.wp_nav.get_turn_rate_rads();  //从控制器获取期望角速度

        // if simple avoidance is active at very low speed do not attempt to turn
#if AP_AVOIDANCE_ENABLED
        /* 避障相关 */
#endif

        // call turn rate steering controller
        calc_steering_from_turn_rate(desired_turn_rate_rads);   //从期望角速度,计算转向  -- 输出2
    }
}

// update navigation
void AR_WPNav_OA::update(float dt)
{
#if AP_OAPATHPLANNER_ENABLED
    /* 避障算法 该章节暂时不考虑 */
#endif  // AP_OAPATHPLANNER_ENABLED

    // call parent update
    AR_WPNav::update(dt);	//***核心更新,重点研读***
}

AR_WPNav::update

更新位置,方位角;更新最大速度;根据当前位置更新下一迭代期望位置

输出throttle和steer控制信号

// update navigation
void AR_WPNav::update(float dt)
{
    // exit immediately if no current location, origin or destination
    Location current_loc;
    float speed;
    if (!hal.util->get_soft_armed() || !_orig_and_dest_valid || !AP::ahrs().get_location(current_loc) || !_atc.get_forward_speed(speed)) {
        _desired_speed_limited = _atc.get_desired_speed_accel_limited(0.0f, dt);
        _desired_lat_accel = 0.0f;
        _desired_turn_rate_rads = 0.0f;
        _cross_track_error = 0;
        return;
    }

    // if no recent calls initialise desired_speed_limited to current speed
    if (!is_active()) {
        _desired_speed_limited = speed;
    }
    _last_update_ms = AP_HAL::millis();

    update_distance_and_bearing_to_destination();   //根据目前位置计算到目标点的距离和方位

    // handle change in max speed
    update_speed_max();     //更新最大速度

    // advance target along path unless vehicle is pivoting
    if (!_pivot.active()) {
        switch (_nav_control_type) {
        case NavControllerType::NAV_SCURVE:
            advance_wp_target_along_track(current_loc, dt);	//核心代码
            break;
        case NavControllerType::NAV_PSC_INPUT_SHAPING:
            update_psc_input_shaping(dt);
            break;
        }
    }

    // update_steering_and_speed
    update_steering_and_speed(current_loc, dt);
}

advance_wp_target_along_track

根据当前位置速度,缩放dt

根据advance_target_along_track输出位置,速度,加速度,传入位置控制器

// move target location along track from origin to destination using SCurves navigation
void AR_WPNav::advance_wp_target_along_track(const Location &current_loc, float dt)
{
    // exit immediately if no current location, destination or disarmed
    Vector2f curr_pos_NE;
    Vector3f curr_vel_NED;
    if (!AP::ahrs().get_relative_position_NE_origin(curr_pos_NE) || !AP::ahrs().get_velocity_NED(curr_vel_NED)) {
        return;
    }

    // exit immediately if we can't convert waypoint origin to offset from ekf origin
    Vector2f origin_NE;
    if (!_origin.get_vector_xy_from_origin_NE(origin_NE)) { //获取origin航点相对于起始位置的的坐标(NE)
        return;
    }
    // convert from cm to meters
    origin_NE *= 0.01f; //以米为单位

    // use _track_scalar_dt to slow down S-Curve time to prevent target moving too far in front of vehicle
    Vector2f curr_target_vel = _pos_control.get_desired_velocity(); //获取期望速度
    float track_scaler_dt = 1.0f;
    if (is_positive(curr_target_vel.length())) {
        Vector2f track_direction = curr_target_vel.normalized();
        const float track_error = _pos_control.get_pos_error().tofloat().dot(track_direction);
        float track_velocity = curr_vel_NED.xy().dot(track_direction);
        // set time scaler to be consistent with the achievable vehicle speed with a 5% buffer for short term variation.
        const float time_scaler_dt_max = _overspeed_enabled ? AR_WPNAV_OVERSPEED_RATIO_MAX : 1.0f;
        track_scaler_dt = constrain_float(0.05f + (track_velocity - _pos_control.get_pos_p().kP() * track_error) / curr_target_vel.length(), 0.0f, time_scaler_dt_max);
    }
    // change s-curve time speed with a time constant of maximum acceleration / maximum jerk
    float track_scaler_tc = 1.0f;
    if (is_positive(_pos_control.get_jerk_max())) {
        track_scaler_tc = _pos_control.get_accel_max() / _pos_control.get_jerk_max();
    }
    _track_scalar_dt += (track_scaler_dt - _track_scalar_dt) * (dt / track_scaler_tc);

    // target position, velocity and acceleration from straight line or spline calculators
    Vector3f target_pos_3d_ftype{origin_NE.x, origin_NE.y, 0.0f};
    Vector3f target_vel, target_accel;

    // update target position, velocity and acceleration
    const float wp_radius = MAX(_radius, _turn_radius);
    bool s_finished = _scurve_this_leg.advance_target_along_track(_scurve_prev_leg, _scurve_next_leg, wp_radius, 
        _pos_control.get_lat_accel_max(), _fast_waypoint, _track_scalar_dt * dt, 
        target_pos_3d_ftype, target_vel, target_accel);

    // pass new target to the position controller
    init_pos_control_if_necessary();
    Vector2p target_pos_ptype{target_pos_3d_ftype.x, target_pos_3d_ftype.y};
    _pos_control.set_pos_vel_accel_target(target_pos_ptype, target_vel.xy(), target_accel.xy());

    // check if we've reached the waypoint
    if (!_reached_destination && s_finished) {
        // "fast" waypoints are complete once the intermediate point reaches the destination
        if (_fast_waypoint) {
            _reached_destination = true;
        } else {
            // regular waypoints also require the vehicle to be within the waypoint radius or past the "finish line"
            const bool near_wp = current_loc.get_distance(_destination) <= _radius;
            const bool past_wp = current_loc.past_interval_finish_line(_origin, _destination);
            _reached_destination = near_wp || past_wp;
        }
    }
}

advance_target_along_track

核心函数,根据时间计算在路径上每一点的位置,速度,加速度

// move target location along path from origin to destination
// prev_leg and next_leg are the paths before and after this path
// wp_radius is max distance from the waypoint at the apex of the turn
// fast_waypoint should be true if vehicle will not stop at end of this leg
// dt is the time increment the vehicle will move along the path
// target_pos should be set to this segment's origin and it will be updated to the current position target
// target_vel and target_accel are updated with new targets
// returns true if vehicle has passed the apex of the corner
// 按dt为时间间隔,根据上一条路径和当前路径计算路径上每一时间点的路径位置,路径速度,路径加速度
// 输入参数: wp_radius 判定转弯开始的最大距离 fast_waypoint 标识下一个路径点为快速通过点(不停止通过)
// 输入输出参数:target_pos输入为当前路径(current_leg)的起始点(origin), 输出为当前累计时间点时的位置
// 输入输出参数:target_vel target_accel同上target_pos
// 返回参数: 真——当前路径结束
bool SCurve::advance_target_along_track(SCurve &prev_leg, SCurve &next_leg, float wp_radius, float accel_corner, bool fast_waypoint, float dt, Vector3f &target_pos, Vector3f &target_vel, Vector3f &target_accel)
{
    // 根据前路径信息,更新当前路径的位置速度加速度
    prev_leg.move_to_pos_vel_accel(dt, target_pos, target_vel, target_accel);
    move_from_pos_vel_accel(dt, target_pos, target_vel, target_accel);  // 根据现路径信息,更新当前路径的位置速度加速度
    bool s_finished = finished();   //现路径结束判断——时间,路径长度

    // check for change of leg on fast waypoint
    const float time_to_destination = get_time_remaining(); 
    /**
     * 判断何时进行转弯
     * fast_waypoint 下一点为快速通过 
     * is_zero(next_leg.get_time_elapsed()) 下一段路径尚未开始
     * (get_time_elapsed() >= time_turn_out() - next_leg.time_turn_in()) 允许转弯的时间段为 加加速完成-匀速结束
     * (position_sq >= 0.25 * track.length_squared()) 当前路径已经经过总路径长度的四分之一
     * 
     * 满足上诉所有条件,方可进入转弯计算
     */
    if (fast_waypoint 
        && is_zero(next_leg.get_time_elapsed()) 
        && (get_time_elapsed() >= time_turn_out() - next_leg.time_turn_in()) 
        && (position_sq >= 0.25 * track.length_squared())) {

        Vector3f turn_pos = -get_track();
        Vector3f turn_vel, turn_accel;
        move_from_time_pos_vel_accel(get_time_elapsed() + time_to_destination * 0.5f, turn_pos, turn_vel, turn_accel);
        //进入转弯计算时,已经开始下一段路径
        next_leg.move_from_time_pos_vel_accel(time_to_destination * 0.5f, turn_pos, turn_vel, turn_accel);
        const float speed_min = MIN(get_speed_along_track(), next_leg.get_speed_along_track());
        if ((get_time_remaining() < next_leg.time_end() * 0.5f) && (turn_pos.length() < wp_radius) &&
             (Vector2f{turn_vel.x, turn_vel.y}.length() < speed_min) &&
             (Vector2f{turn_accel.x, turn_accel.y}.length() < accel_corner)) {
            next_leg.move_from_pos_vel_accel(dt, target_pos, target_vel, target_accel);
        }
    } else if (!is_zero(next_leg.get_time_elapsed())) { // 计算转弯后,已经开始下一段路径
        next_leg.move_from_pos_vel_accel(dt, target_pos, target_vel, target_accel);
        if (next_leg.get_time_elapsed() >= get_time_remaining()) { // 如果下一路径的时间进度超过当前路径的剩余时间,则判定当前路径完成
            s_finished = true;
        }
    }

    return s_finished;
}

MATLAB仿真

way_points = [10,10;20,30;30,30]; %路径点队列

wp_controller = WPNav();

wp_controller.speed_max = 5;    %限制最大速度
wp_controller.accel_max = 2;    %限制最大加速度
wp_controller.jerk_max = 4;     %限制最大加加速度
wp_controller.snap_max = 8;     %限制最大加加加速度
wp_radius = 2;                  %到达终点最大感知距离
accel_corner = 2;               %横向加速度(向心加速度)

dt = 0.1;   %时间间隔0.1s

plot_vel =[];
plot_accel = [];
plot_pos = [];

for i = 1:size(way_points, 1)
    
    %处理路径点
    if i < size(way_points, 1)
        way_point = way_points(i, :);
        next_point = way_points(i+1, :);
    else
        way_point = way_points(i, :);
        next_point = NaN;
    end

    if (i == 1)||(wp_controller.reached_destination)
        wp_controller.set_desired_location(way_point, next_point); %设置路径点
        while ~wp_controller.reached_destination
            %迭代计算路径中每一点的信息,直到该路径结束
            target_vel = zeros(1,2);
            target_accel = zeros(1,2);
            target_pos = wp_controller.origin;
            [s_finished, prev_leg, next_leg, target_pos, target_vel, target_accel] = ...
			wp_controller.scurve_this_leg.
advance_target_along_track(wp_controller.scurve_prev_leg, wp_controller.scurve_next_leg, ...
wp_radius, accel_corner, wp_controller.fast_waypoint, dt, target_pos, target_vel, target_accel);
            %记录每一点信息,方便作图
            plot_accel = [plot_accel;target_accel];
            plot_vel = [plot_vel;target_vel];
            plot_pos = [plot_pos;target_pos];
            %检测是否达到目标点
            if (~wp_controller.reached_destination && s_finished)
                if wp_controller.fast_waypoint
                    wp_controller.reached_destination = true;
                else
                    wp_controller.reached_destination = norm(way_point - target_pos) <= wp_radius;
                end
            end
        end
    end

end

山和山不相遇,人与人要相逢