APM程序分析

  1. 主程序在ArduCopter.cpp的loop()函数。

/// advance_wp_target_along_track - move target location along track from origin to destination

设起始点O,目标点D,飞机所在当前点C,OC在OD上的投影为OH。该函数完成沿航迹更新中间目标点。航迹指的是OD。切记,中间目标点都是在OD线段上的。函数0.01s执行一次,即M点0.01s更新一次,两个中间目标点的距离是_limited_speed_cms *0.01,是非常小的。

bool AC_WPNav::advance_wp_target_along_track(float dt)

_track_leash_length  _track_speed  _track_accel是沿航迹OD的最大限制距离、速度、加速度。是该函数上一次更新时计算出来的量。

{

float track_covered;        // distance (in cm) along the track that the vehicle has traveled.  Measured by drawing a perpendicular line from the track to the vehicle.OC在OD的投影OH

Vector3f track_error;       // distance error (in cm) from the track_covered position (i.e. closest point on the line to the vehicle) and the vehicle 向量OC减去向量OD,即CH

float track_desired_max;    // the farthest distance (in cm) along the track that the leash will allow   WP控制器本次更新飞机能在OD上跑与O的最远距离哦,即OMAX

float track_leash_slack;    // additional distance (in cm) along the track from our track_covered position that our leash will allow  WP控制器本次更新飞机能在能在OD上跑的最远距离,注意是飞机本次跑的距离。即HMAX

bool reached_leash_limit = false;   // true when track has reached leash limit and we need to slow down the target point

// get current location

Vector3f curr_pos = _inav.get_position();

// calculate terrain adjustments

float terr_offset = 0.0f;

if (_terrain_alt && !get_terrain_offset(terr_offset)) {_terrain_alt=true目标点和起始点的高度是高于地形高度,false是高于起始点高度。get_terrain_offset(terr_offset) terr_offset等于海拔高度减去测距仪测量高度,如果有则返回true

return false;

}

// calculate 3d vector from segment's origin

Vector3f curr_delta = (curr_pos - Vector3f(0,0,terr_offset)) - _origin;我们求得curr_pos是基于地理坐标系,Z是海拔高度,这里我们应该减去地形高度,得到距离当地地面的高度

// calculate how far along the track we are

track_covered = curr_delta.x * _pos_delta_unit.x + curr_delta.y * _pos_delta_unit.y + curr_delta.z * _pos_delta_unit.z; 线段OH

Vector3f track_covered_pos = _pos_delta_unit * track_covered; 向量OH

track_error = curr_delta - track_covered_pos; 向量CH

// calculate the horizontal error

float track_error_xy = norm(track_error.x, track_error.y);向量CH在oxy水平面的投影

// calculate the vertical error

float track_error_z = fabsf(track_error.z);

// get position control leash lengths

float leash_xy = _pos_control.get_leash_xy();位置控制器在水平方向最大限制距离leash_xy=_speed_cms/kp 或者=0.5_accel_cmss/kp/kp +0.5_speed_cms^2/_accel_cmss

_speed_cms是水平方向最大速度5m/s,accel_cmss是水平方向最大加速度1m/s。我们可以设置他们的大小。Kp是位置控制环的P。

float leash_z;

if (track_error.z >= 0) {

leash_z = _pos_control.get_leash_up_z();

}else{

leash_z = _pos_control.get_leash_down_z();

}飞机向上和向下的加速度、速度不一样,所以位置控制器向上和向下的最大限制距离也不一样,要分开讨论。

// calculate how far along the track we could move the intermediate target before reaching the end of the leash

track_leash_slack = MIN(_track_leash_length*(leash_z-track_error_z)/leash_z, _track_leash_length*(leash_xy-track_error_xy)/leash_xy);如图,track_error是向量CH,track_error越大,说明飞机离航迹OD越远,比如图中蓝色的C,这时我们应该让飞机少飞一点,所以用leash- track_error。_track_leash_length=MIN(leash_z/pos_delta_unit_z, _pos_control.get_leash_xy()/pos_delta_unit_xy)是沿航迹的最大限制距离。这里相当于是根据飞机离航迹的偏差error对_track_leash_length求了一个百分比。比如水平方向上的百分比,(水平方向上最大限制距离-水平方向上的误差)/水平方向上最大限制距离

if (track_leash_slack < 0) {说明偏差比最大限制距离还大,即飞机离航迹很远

track_desired_max = track_covered; 飞机不跑

}else{

track_desired_max = track_covered + track_leash_slack;飞机能飞的最大距离是:沿航迹OD的最大限制距离的百分比。即OMAX

}

// check if target is already beyond the leash

if (_track_desired > track_desired_max) {_track_desired是函数上一次执行计算的飞机应该沿OD飞机距离,函数0.01s执行一次,中间目标点0.01s更新一次。_track_desired += _limited_speed_xy_cms * dt;理论上两次中间目标点的距离是_limited_speed_xy_cms * 0.01s,是非常小的,所以我们可以用上一次的目标点来做这样的判断。所以如果_track_desired > track_desired_max说明本次函数算出来飞行的最大距离还没有上一次算出来应该飞行距离的大。

reached_leash_limit = true;

}

// get current velocity

const Vector3f &curr_vel = _inav.get_velocity();

// get speed along track

float speed_along_track = curr_vel.x * _pos_delta_unit.x + curr_vel.y * _pos_delta_unit.y + curr_vel.z * _pos_delta_unit.z;飞机当前速度在OD上的投影

// calculate point at which velocity switches from linear to sqrt

float linear_velocity = _wp_speed_cms;

float kP = _pos_control.get_pos_xy_kP();

if (kP >= 0.0f) {   // avoid divide by zero

linear_velocity = _track_accel/kP; 沿航迹OD的最大速度

}

// let the limited_speed_xy_cms be some range above or below current velocity along track

if (speed_along_track < -linear_velocity) {飞机当前速度在OD上的投影是负的,说明飞机正沿着OD相反的方向飞行,而且该速度还大于沿航迹的最大速度。

// we are traveling fast in the opposite direction of travel to the waypoint so do not move the intermediate point

_limited_speed_xy_cms = 0;

}else{

// increase intermediate target point's velocity if not yet at the leash limit

if(dt > 0 && !reached_leash_limit) {

_limited_speed_xy_cms += 2.0f * _track_accel * dt; _limited_speed_xy_cms非常重要,是我们认为的飞机沿航迹OD的理论速度。如果飞机还没有到达最终目的地,把_limited_speed_xy_cms增加2at,a是沿航迹的最大加速度。

}

// do not allow speed to be below zero or over top speed

_limited_speed_xy_cms = constrain_float(_limited_speed_xy_cms, 0.0f, _track_speed);不能大于沿航迹OD的最大速度

// check if we should begin slowing down

if (!_flags.fast_waypoint) {

float dist_to_dest = _track_length - _track_desired; _track_length即OD,_track_desired是函数上一次执行计算的飞机应该沿OD飞行距离,即OM。两个中间目标点的距离是非常小的,因此可以用上一次的中间目标点做判断。

if (!_flags.slowing_down && dist_to_dest <= _slow_down_dist) {

_flags.slowing_down = true;

}_slow_down_dist = _track_speed * _track_speed / (4.0f*_track_accel) 路程s=v^2/2a,也就是当飞机飞了0.5s时,就该减速了。是上一次计算的。

// if target is slowing down, limit the speed

if (_flags.slowing_down) {

_limited_speed_xy_cms = MIN(_limited_speed_xy_cms, get_slow_down_speed(dist_to_dest, _track_accel));等于4 dist_to_dest* _track_accel开根号

}

}

// if our current velocity is within the linear velocity range limit the intermediate point's velocity to be no more than the linear_velocity above or below our current velocity

if (fabsf(speed_along_track) < linear_velocity) {

_limited_speed_xy_cms = constrain_float(_limited_speed_xy_cms,speed_along_track-linear_velocity,speed_along_track+linear_velocity);再次限幅

}

}

// advance the current target

if (!reached_leash_limit) {

_track_desired += _limited_speed_xy_cms * dt;算OM

// reduce speed if we reach end of leash

if (_track_desired > track_desired_max) { 如果此次算出来的OM大于了OMAX

_track_desired = track_desired_max;

_limited_speed_xy_cms -= 2.0f * _track_accel * dt; 减速

if (_limited_speed_xy_cms < 0.0f) {

_limited_speed_xy_cms = 0.0f;

}

}

}

// do not let desired point go past the end of the track unless it's a fast waypoint

if (!_flags.fast_waypoint) {

_track_desired = constrain_float(_track_desired, 0, _track_length);不要超过OD

} else {

_track_desired = constrain_float(_track_desired, 0, _track_length + WPNAV_WP_FAST_OVERSHOOT_MAX); 可以超过OD 2m

}

// recalculate the desired position

Vector3f final_target = _origin + _pos_delta_unit * _track_desired; 这时候的_track_desired才是我们算出来的真正的OM啦,乘以_pos_delta_unit把它变成向量。注意final_target是一个坐标哦。

// convert final_target.z to altitude above the ekf origin

final_target.z += terr_offset; 加上地面的海拔高度,才是中间目标点真正的地理坐标。

_pos_control.set_pos_target(final_target); 作为期望给位置控制器

// check if we've reached the waypoint

if( !_flags.reached_destination ) {

if( _track_desired >= _track_length ) { OM大于等于OD

// "fast" waypoints are complete once the intermediate point reaches the destination

if (_flags.fast_waypoint) {

_flags.reached_destination = true; 如果是fast_waypoint则认为到达最终目标点

}else{

// regular waypoints also require the copter to be within the waypoint radius

Vector3f dist_to_dest = (curr_pos - Vector3f(0,0,terr_offset)) - _destination;

if( dist_to_dest.length() <= _wp_radius_cm ) {

_flags.reached_destination = true;

}算当前点到目标点的距离,如果在半径内,则认为到达目标点。

}

}

}

// successfully advanced along track

return true;

}

bool AC_WPNav::update_wpnav()

{

// calculate dt

float dt = _pos_control.time_since_last_xy_update();上一次更新XY到现在的时间间隔

bool ret = true;

// update at poscontrol update rate

if (dt >= _pos_control.get_dt_xy()) { dt_xy可以设置,是固定值,如0.02s

// allow the accel and speed values to be set without changing

// out of auto mode. This makes it easier to tune auto flight

_pos_control.set_accel_xy(_wp_accel_cms); wpnav任务期间的水平加速度

_pos_control.set_jerk_xy_to_default();jeck加速度等于17平方米每秒

_pos_control.set_accel_z(_wp_accel_z_cms); wpnav任务期间的垂直加速度

// sanity check dt

if (dt >= 0.2f) {

dt = 0.0f;

}时间间隔不能太长

// advance the target if necessary

if (!advance_wp_target_along_track(dt)) {更新中间点,注意,中间点始终在起始点和目标点的直线上。它调用了函数_pos_control.set_pos_target(final_target);也就是把中间点的坐标给了位置控制器作为位置控制器的期望值。

// To-Do: handle inability to advance along track (probably because of missing terrain data)

ret = false;

}

// freeze feedforwards during known discontinuities

// TODO: why always consider Z axis discontinuous?

if (_flags.new_wp_destination) {位置控制器收到一个新的目标点(期望值)

_flags.new_wp_destination = false;

_pos_control.freeze_ff_xy();有新的目标点则freeze前馈控制

}

_pos_control.freeze_ff_z();freeze速度到加速度环的前馈控制

_pos_control.update_xy_controller(AC_PosControl::XY_MODE_POS_ONLY, 1.0f, false);更新水平控制器,包括位置到速度环pos_to_rate_xy(),速度到加速度环rate_to_accel_xy(),再将加速度转化为期望的欧拉角度accel_to_lean_angles().飞机就会飞向我们设置的中间目标点啦,不断更新中间目标点,这样慢慢我们就会飞到最终目标点咯。

check_wp_leash_length();检查wp_leash_length,_pos_control.update_xy_controller更新后,position controller 的leash lengths可能已经改变了,所以必须调用该函数,来重新调整计算wp_leash_length

_wp_last_update = AP_HAL::millis();

}

return ret;

}

计算waypoint controller 在航迹方向上的leash_length。该leash_length就是pos controller水平方向上的leash_length在航迹方向上的投影和垂直方向上的leash_length在航迹方向上的投影取较小值。同时,该函数还更新了沿航迹的_track_accle _track_speed

void AC_WPNav::calculate_wp_leash_length()

{

// length of the unit direction vector in the horizontal

float pos_delta_unit_xy = norm(_pos_delta_unit.x, _pos_delta_unit.y);

float pos_delta_unit_z = fabsf(_pos_delta_unit.z);

_pos_delta_unit是起点到目标点的方向向量,始终都是固定的。

float speed_z;

float leash_z;

if (_pos_delta_unit.z >= 0.0f) {目标点在当前点上方

speed_z = _wp_speed_up_cms; _wp_speed_up_cms是一个固定值,2.5m/s

leash_z = _pos_control.get_leash_up_z();获取pos controller 的leash z

}else{

speed_z = _wp_speed_down_cms;1.5m/s

leash_z = _pos_control.get_leash_down_z();

}

// calculate the maximum acceleration, maximum velocity, and leash length in the direction of travel计算航迹方向上最大的加速度,速度,leash length

if(is_zero(pos_delta_unit_z) && is_zero(pos_delta_unit_xy)){ 方向向量为0,说明没有目标点

_track_accel = 0;

_track_speed = 0;

_track_leash_length = WPNAV_LEASH_LENGTH_MIN;

}else if(is_zero(_pos_delta_unit.z)){在Z方向为零,说明目标点和当前点在同一高度,飞机只需要水平飞行

_track_accel = _wp_accel_cms/pos_delta_unit_xy; _wp_accel_cms也是固定值,1m/s2,

_track_speed = _wp_speed_cms/pos_delta_unit_xy; _wp_speed_cms是固定值,5m/s

这里说一下,以_wp_开头的加速度,速度都是wp controller控制器的固定值参数。_wp_accel_cms/pos_delta_unit_xy表示把水平方向的加速度投影的航迹上面来。_track_accel是沿航迹的最大加速度。

_track_leash_length = _pos_control.get_leash_xy()/pos_delta_unit_xy;把pos controller获得的水平方向上的leash_length投影的航迹上面来,作为航迹上的最大限制距离leash_length。也就是_track_leash_length是pos controller的leash_length在航迹上的投影。

}else if(is_zero(pos_delta_unit_xy)){目标点恰好在当前点的正上方或者正下方

_track_accel = _wp_accel_z_cms/pos_delta_unit_z; 1m/s2

_track_speed = speed_z/pos_delta_unit_z; 2.5m/s或者1.5m/s

_track_leash_length = leash_z/pos_delta_unit_z;

}else{

_track_accel = MIN(_wp_accel_z_cms/pos_delta_unit_z, _wp_accel_cms/pos_delta_unit_xy);

_track_speed = MIN(speed_z/pos_delta_unit_z, _wp_speed_cms/pos_delta_unit_xy);

_track_leash_length = MIN(leash_z/pos_delta_unit_z, _pos_control.get_leash_xy()/pos_delta_unit_xy);

}

沿航迹的加速度,速度,限制最大距离都取水平方向、垂直方向较小的

// calculate slow down distance (the distance from the destination when the target point should begin to slow down)

calc_slow_down_distance(_track_speed, _track_accel);

_slow_down_dist = _track_speed * _track_speed / (4.0f*_track_accel) 路程s=v^2/2a,也就是当飞机飞了0.5s时,就该减速了。

// set recalc leash flag to false

_flags.recalc_wp_leash = false;

}

从起始点飞到目标点需要的航向角。该航向角是与地理北的夹角哟,即yaw,直接作为姿态环的航向角期望值。

float AC_WPNav::get_bearing_cd(const Vector3f &origin, const Vector3f &destination) const

{

float bearing = 9000 + atan2f(-(destination.x-origin.x), destination.y-origin.y) * 5729.57795f;

if (bearing < 0) {

bearing += 36000;

}

return bearing;

}

WP controller 和spline controller 两个控制器第一次被调用前需要初始化。

void AC_WPNav::wp_and_spline_init()

{

// check _wp_accel_cms is reasonable

if (_wp_accel_cms <= 0) {

_wp_accel_cms.set_and_save(WPNAV_ACCELERATION);

} _wp_accel_cms在执行wp controller任务时的水平最大加速度。1m/s

// also limit the accel using the maximum lean angle. This

// prevents the navigation controller from trying to move the

// target point at an unachievable rate防止飞机已经达到最大倾斜角,依然达不到最大水平加速度

float accel_limit_cms = GRAVITY_MSS * 100 * tanf(radians(_attitude_control.lean_angle_max()*0.01f));

if (_wp_accel_cms > accel_limit_cms) {

_wp_accel_cms.set(accel_limit_cms);

}

accel_limit_cms飞机最大倾斜角时的水平加速度

// initialise position controller

_pos_control.init_xy_controller();

// initialise position controller speed and acceleration

_pos_control.set_speed_xy(_wp_speed_cms);

_pos_control.set_accel_xy(_wp_accel_cms);

_pos_control.set_jerk_xy_to_default();

_pos_control.set_speed_z(-_wp_speed_down_cms, _wp_speed_up_cms);

_pos_control.set_accel_z(_wp_accel_z_cms);

_pos_control.calc_leash_length_xy();

_pos_control.calc_leash_length_z();

}

设置目标点的思路:

  1. 我们给出的目标点肯定是经纬度的、高度的话可能是高于地形高度或者海拔高度。我们要计算就要把该目标点转换为基于地理坐标系的坐标。所以我们用目标点的经纬度减去地理坐标系原点的经纬度,得到了坐标x,y。z分了两种,一种是高于地理坐标系原点高度,也就是基于地理坐标系的z;一种是高于地形的,是地面高度,也就是不是基于地理坐标系的。
  2. 飞机起始点的设置,这里的起始点是我们WP controller里面的起始点,也就是文章开头图里面的O。O的设置也是两种,一种是WP controller控制器已经运行了,我们就把上一次的目标点设为O,注意此时O的坐标z是高于地形高度,一种是WP controller控制器没运行,我们直接把当前飞机位置设为O,注意此时O坐标是基于地理坐标系的,z是高于原点高度,但它减了origin_terr_offset,它最终还是z为高于地形高度。

也就是,正常情况下,起始点和目标点的水平坐标是基于地理坐标系的,z必须是高于地形高度。如果_terrain_alt=flase,advance_wp_target_along_track(float dt)不会更新的!也就是_terrain_alt=true是WP controller执行的前提!我们可以默认去掉讨论_terrain_alt=flase的情况了,也就认为z是高于地形高度。也就是我们给目标点的时候必须给地形高度!!!!

求目标点到起始点的向量。水平方向上是通过该点减去起始点的经纬度算出了x,y的距离。如果该点是地形高度,则z是该点的地形高度,如果该点不是地形高度,则z是该点高于起始点高度。注意,这里的起始点origin不是wp controller里面的origin,是ekf里面的origin,是地理坐标系原点。

bool AC_WPNav::get_vector_NEU(const Location_Class &loc, Vector3f &vec, bool &terrain_alt)

{

// convert location to NEU vector3f

Vector3f res_vec;

if (!loc.get_vector_xy_from_origin_NEU(res_vec)) {

return false;

}

res_vec.x = (lat-ekf_origin.lat) * LATLON_TO_CM;纬度相减,1纬度对应111km

res_vec.y = (lng-ekf_origin.lng) * LATLON_TO_CM * longitude_scale(ekf_origin);经度相减,1经度对应111*cosa千米。a为起始点所在的纬度,不同纬度1经度对应的距离是不一样的。

EKF融合后输出的是点的经纬度,我们要把它转换为目标点到起始点的具体距离的向量。

// convert altitude

一个点的高度分为高于地形高度、高于HOME高度,高于起始点高度。

if (loc.get_alt_frame() == Location_Class::ALT_FRAME_ABOVE_TERRAIN) {

int32_t terr_alt;

if (!loc.get_alt_cm(Location_Class::ALT_FRAME_ABOVE_TERRAIN, terr_alt)) {

return false;

}

vec.z = terr_alt;

terrain_alt = true;如果loc本身就是高于地形高度,就把高度给vec.z。

} else {

terrain_alt = false;

int32_t temp_alt;

if (!loc.get_alt_cm(Location_Class::ALT_FRAME_ABOVE_ORIGIN, temp_alt)) {

return false;

}

vec.z = temp_alt;高于起始点高度

terrain_alt = false;

}

// copy xy (we do this to ensure we do not adjust vector unless the overall conversion is successful

vec.x = res_vec.x;

vec.y = res_vec.y;

return true;

}

该函数是把给定目标点D转化为基于地理坐标系坐标,z为高于地面坐标。

bool AC_WPNav::set_wp_destination(const Location_Class& destination)

{

bool terr_alt;

Vector3f dest_neu;

// convert destination location to vector

if (!get_vector_NEU(destination, dest_neu, terr_alt)) {

return false;  dest_neu的坐标x,y基于地理坐标系,z是高于地形高度

}

// set target as vector from EKF origin

return set_wp_destination(dest_neu, terr_alt);

}

该函数主要是算起始点O的坐标。

bool AC_WPNav::set_wp_destination(const Vector3f& destination, bool terrain_alt)

{

Vector3f origin;

// if waypoint controller is active use the existing position target as the origin

if ((AP_HAL::millis() - _wp_last_update) < 1000) {

origin = _pos_control.get_pos_target();  如果WP controller已经运行,让上一次目标点作为起始点O

} else {

// if waypoint controller is not active, set origin to reasonable stopping point (using curr pos and velocity)

_pos_control.get_stopping_point_xy(origin);

_pos_control.get_stopping_point_z(origin); 如果WP controller没有运行,让飞机当前位置推测出来的停止位置作为起始点O

}

// convert origin to alt-above-terrain

if (terrain_alt) {

float origin_terr_offset;

if (!get_terrain_offset(origin_terr_offset)) {

return false;

}

origin.z -= origin_terr_offset; 把z变成高于地形高度

}

// set origin and destination

return set_wp_origin_and_destination(origin, destination, terrain_alt);

}

bool AC_WPNav::set_wp_origin_and_destination(const Vector3f& origin, const Vector3f& destination, bool terrain_alt)

{

// store origin and destination locations

_origin = origin;

_destination = destination;

_terrain_alt = terrain_alt;

Vector3f pos_delta = _destination - _origin;

_track_length = pos_delta.length(); // get track length 航迹长度

// calculate each axis' percentage of the total distance to the destination

if (is_zero(_track_length)) {

// avoid possible divide by zero

_pos_delta_unit.x = 0;

_pos_delta_unit.y = 0;

_pos_delta_unit.z = 0;

}else{

_pos_delta_unit = pos_delta/_track_length; 航迹的单位方向向量

}

// calculate leash lengths

calculate_wp_leash_length(); 航迹最大限制距离

// initialise yaw heading

if (_track_length >= WPNAV_YAW_DIST_MIN) { 航迹长度大于2m才改变航向角

_yaw = get_bearing_cd(_origin, _destination); 获取飞机应该的航向角

} else {

// set target yaw to current heading target

_yaw = _attitude_control.get_att_target_euler_cd().z; 不改变航向角

}

// get origin's alt-above-terrain

float origin_terr_offset = 0.0f;

if (terrain_alt) {

if (!get_terrain_offset(origin_terr_offset)) {

return false;  获取起始点的地形距离海拔高度

}

}

// initialise intermediate point to the origin

_pos_control.set_pos_target(origin + Vector3f(0,0,origin_terr_offset));  把起始点O作为期望位置给位置控制器

_track_desired = 0;             // target is at beginning of track

_flags.reached_destination = false;

_flags.fast_waypoint = false;   // default waypoint back to slow

_flags.slowing_down = false;    // target is not slowing down yet

_flags.segment_type = SEGMENT_STRAIGHT;

_flags.new_wp_destination = true;   // flag new waypoint so we can freeze the pos controller's feed forward and smooth the transition  有新的目标点则不使用前馈控制

// initialise the limited speed to current speed along the track

const Vector3f &curr_vel = _inav.get_velocity();

// get speed along track (note: we convert vertical speed into horizontal speed equivalent)

float speed_along_track = curr_vel.x * _pos_delta_unit.x + curr_vel.y * _pos_delta_unit.y + curr_vel.z * _pos_delta_unit.z; 当前速度在航迹上面的投影

_limited_speed_xy_cms = constrain_float(speed_along_track,0,_wp_speed_cms);不能大于航迹最大速度

return true;

}

/// shift_wp_origin_to_current_pos - shifts the origin and destination so the origin starts at the current position 平移起始点和目标点,让起始点在飞机当前位置。

///     used to reset the position just before takeoff

///     relies on set_wp_destination or set_wp_origin_and_destination having been called first

void AC_WPNav::shift_wp_origin_to_current_pos()

{

// return immediately if vehicle is not at the origin

if (_track_desired > 0.0f) {

return;

} 飞机不在起始点则退出

// get current and target locations

const Vector3f curr_pos = _inav.get_position();

const Vector3f pos_target = _pos_control.get_pos_target(); 这是我们当初设置的起始点

// calculate difference between current position and target

Vector3f pos_diff = curr_pos - pos_target;

// shift origin and destination

_origin += pos_diff;

_destination += pos_diff;

// move pos controller target and disable feed forward

_pos_control.set_pos_target(curr_pos);

_pos_control.freeze_ff_xy();

_pos_control.freeze_ff_z();

}

void AC_WPNav::calc_loiter_desired_velocity(float nav_dt, float ekfGndSpdLimit)

{

// calculate a loiter speed limit which is the minimum of the value set by the WPNAV_LOITER_SPEED

// parameter and the value set by the EKF to observe optical flow limits

float gnd_speed_limit_cms = MIN(_loiter_speed_cms,ekfGndSpdLimit*100.0f);悬停最大水平速度,5m/s. ekfGndSpdLimit光流传感器速度不能太快,大概3m/s或者7m/s

gnd_speed_limit_cms = MAX(gnd_speed_limit_cms, 10.0f);要大于0.1m/s,gnd_speed_limit_cms相当于一个综合的最大水平速度。

// range check nav_dt

if( nav_dt < 0 ) {

return;

}

// check loiter speed and avoid divide by zero

if(gnd_speed_limit_cms < WPNAV_LOITER_SPEED_MIN) {

gnd_speed_limit_cms = WPNAV_LOITER_SPEED_MIN; 最小1m/s

}

_pos_control.set_speed_xy(gnd_speed_limit_cms);最大水平速度

_pos_control.set_accel_xy(_loiter_accel_cmss); 最大悬停加速度2.5m/s^2

_pos_control.set_jerk_xy(_loiter_jerk_max_cmsss); _loiter_jerk_max_cmsss加速度变化率10,这个是给pos控制器作为最大水平速度、加速度

// rotate pilot input to lat/lon frame

Vector2f desired_accel;

desired_accel.x = (_pilot_accel_fwd_cms*_ahrs.cos_yaw() - _pilot_accel_rgt_cms*_ahrs.sin_yaw());_pilot_accel_fwd_cms机体坐标系飞机期望向前飞的加速度,注意这个向前飞,是我们摇杆上的前进后退,是机体坐标系X轴在水平面的投影,所以它本质上并不是基于机体坐标系的。pilot_accel_fwd_cms是摇杆对应的输出值。

如上图所示,飞机向前飞的加速度和地理坐标系Xe Ye的加速度关系只跟yaw角有关。

desired_accel.y = (_pilot_accel_fwd_cms*_ahrs.sin_yaw() + _pilot_accel_rgt_cms*_ahrs.cos_yaw());飞机向前飞和向右飞期望加速度转换到地理坐标系下的期望加速度

// calculate the difference

Vector2f des_accel_diff = (desired_accel - _loiter_desired_accel); _loiter_desired_accel地理坐标系下期望悬停加速度

// constrain and scale the desired acceleration

float des_accel_change_total = norm(des_accel_diff.x, des_accel_diff.y);

float accel_change_max = _loiter_jerk_max_cmsss * nav_dt;能改变的最大加速度

if (_loiter_jerk_max_cmsss > 0.0f && des_accel_change_total > accel_change_max && des_accel_change_total > 0.0f) {

des_accel_diff.x = accel_change_max * des_accel_diff.x/des_accel_change_total;

des_accel_diff.y = accel_change_max * des_accel_diff.y/des_accel_change_total;

}当我们期望改变的加速度大于能改变的加速度的最大值时,我们只能取能改变的最大加速度,赋予方向

// adjust the desired acceleration

_loiter_desired_accel += des_accel_diff;

// get pos_control's feed forward velocity

const Vector3f &desired_vel_3d = _pos_control.get_desired_velocity();

Vector2f desired_vel(desired_vel_3d.x,desired_vel_3d.y);

// add pilot commanded acceleration

desired_vel.x += _loiter_desired_accel.x * nav_dt;

desired_vel.y += _loiter_desired_accel.y * nav_dt;

float desired_speed = desired_vel.length();

if (!is_zero(desired_speed)) {

Vector2f desired_vel_norm = desired_vel/desired_speed;

float drag_speed_delta = -_loiter_accel_cmss*nav_dt*desired_speed/gnd_speed_limit_cms;一次时间间隔变化的最大速度取百分比

if (_pilot_accel_fwd_cms == 0 && _pilot_accel_rgt_cms == 0) {

drag_speed_delta = MIN(drag_speed_delta,MAX(-_loiter_accel_min_cmss*nav_dt, -2.0f*desired_speed*nav_dt));

}当遥控器没有输出时,也就是飞机要悬停在当前点时

desired_speed = MAX(desired_speed+drag_speed_delta,0.0f); 适当把速度变小一点

desired_vel = desired_vel_norm*desired_speed;

}

// Apply EKF limit to desired velocity -  this limit is calculated by the EKF and adjusted as required to ensure certain sensor limits are respected (eg optical flow sensing)

float horizSpdDem = sqrtf(sq(desired_vel.x) + sq(desired_vel.y));

if (horizSpdDem > gnd_speed_limit_cms) {

desired_vel.x = desired_vel.x * gnd_speed_limit_cms / horizSpdDem;

desired_vel.y = desired_vel.y * gnd_speed_limit_cms / horizSpdDem;

}速度不能超过最大速度

// Limit the velocity to prevent fence violations

if (_avoid != NULL) {

_avoid->adjust_velocity(_pos_control.get_pos_xy_kP(), _loiter_accel_cmss, desired_vel);

}

// send adjusted feed forward velocity back to position controller

_pos_control.set_desired_velocity_xy(desired_vel.x,desired_vel.y);

}

遥控器打到哪,飞机悬停到哪。摇杆没有值时,就悬停在当前点。过程:通过摇杆值对应的roll角,_pilot_accel_rgt_cms = control_roll * _loiter_accel_cmss / 4500.0f;算出摇杆值期望的加速度,将该加速度转为地理坐标系的期望加速度,用期望加速度算出期望的速度,用期望速度算出期望位置。Pos_control控制该期望位置。当摇杆值没有输出时,飞机就悬停在当前点。

void AC_WPNav::update_loiter(float ekfGndSpdLimit, float ekfNavVelGainScaler)

{

// calculate dt

float dt = _pos_control.time_since_last_xy_update();现在到上次更新水平位置的时间间隔

// run at poscontrol update rate.

// TODO: run on user input to reduce latency, maybe if (user_input || dt >= _pos_control.get_dt_xy())    _dt_xy time difference (in seconds) between update_xy_controller and update_vel_controller_xyz calls 0.02s

if (dt >= _pos_control.get_dt_xy()) {现在到上次更新水平位置的时间间隔大于0.02s,才执行

// sanity check dt

if (dt >= 0.2f) {

dt = 0.0f;

}

// initialise ekf position reset check

check_for_ekf_position_reset();重置xy位置

// initialise pos controller speed and acceleration

_pos_control.set_speed_xy(_loiter_speed_cms);

_pos_control.set_accel_xy(_loiter_accel_cmss);

_pos_control.set_jerk_xy(_loiter_jerk_max_cmsss);

calc_loiter_desired_velocity(dt,ekfGndSpdLimit);

_pos_control.update_xy_controller(AC_PosControl::XY_MODE_POS_LIMITED_AND_VEL_FF, ekfNavVelGainScaler, true);用期望的速度算出pos_target,然后再控制pos_target,XY_MODE_POS_LIMITED_AND_VEL_FF模式,根据期望位置算出来的期望速度不能大于2m/s

}

}

void AC_WPNav::check_for_ekf_position_reset()

{

// check for position shift

Vector2f pos_shift;

uint32_t reset_ms = _ahrs.getLastPosNorthEastReset(pos_shift);返回上一次重置的时间

if (reset_ms != _loiter_ekf_pos_reset_ms) {上一次记录的重置的系统时间

_pos_control.shift_pos_xy_target(pos_shift.x * 100.0f, pos_shift.y * 100.0f);移动xy目标值,此处应该为0

_loiter_ekf_pos_reset_ms = reset_ms;

}

}

APM程序分析-AC_WPNav.cpp的更多相关文章

  1. APM程序分析-ArduCopter.cpp

    该文件是APM的主文件. #define SCHED_TASK(func, rate_hz, max_time_micros) SCHED_TASK_CLASS(Copter, &copter ...

  2. APM程序分析-Control_rtl.cpp

    bool Copter::rtl_init(bool ignore_checks) { if (position_ok() || ignore_checks) { rtl_build_path(!fa ...

  3. 二进制程序分析工具Pin在Windows系统中的安装和使用方法

    这篇日志其实很弱智,也是因为换了新电脑,实验环境不全(当然,做这个实验我是在虚拟机里,因为接下来想拿些恶意代码的数据),所以这里记录一下在Windows下怎么安装和使用Pin这个程序分析领域最常用的工 ...

  4. 对Java数组中去除重复项程序分析

    我作为一个Java菜鸟,只会用简单的办法来处理这个问题.如果有大神看到,请略过,感激不尽! 所以首先先分析这道题目:数组中重复的数据进行删除,并且要让数组里的数据按原来的顺序排列,中间不能留空. 既然 ...

  5. (IOS)BaiduFM 程序分析

    本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源app BaiduFM的研究心得. 项目地址:https://github.com/belm/BaiduFM-Swift 一.项 ...

  6. Linux程序分析工具:ldd和nm

    ldd和nm是Linux下两个非常实用的程序分析工具.其中,ldd是用来分析程序运行时需要依赖的动态链接库的工具,nm是用来查看指定程序中的符号表信息的工具. 1 ldd 格式:ldd [option ...

  7. C#程序分析

    一.程序及问题 阅读下面程序,请回答如下问题: 问题1:这个程序要找的是符合什么条件的数? 问题2:这样的数存在么?符合这一条件的最小的数是什么? 问题3:在电脑上运行这一程序,你估计多长时间才能输出 ...

  8. linux程序分析工具

    ldd和nm是Linux下两个非常实用的程序分析工具.ldd是用来分析程序运行时需要依赖的动态链接库的工具,nm是用来查看指定程序中的符号表信息的工具,objdump用来查看源代码与汇编代码,-d只查 ...

  9. caffe源代码分析--softmax_layer.cpp

    caffe源代码分析--softmax_layer.cpp // Copyright 2013 Yangqing Jia // #include <algorithm> #include ...

随机推荐

  1. OleDbDataReader快速数据读取方式

    查询得到OleDbDataReader后,有三种方式支持数据读取,如下: //方法一**速度中等 OleDbDataReader reader = command.ExecuteReader(); w ...

  2. 【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组

    E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...

  3. 【bzoj1415】 Noi2005—聪聪和可可

    http://www.lydsy.com/JudgeOnline/problem.php?id=1415 (题目链接) 题意 一张图,聪聪想吃可可.每单位时间聪聪可以先移动两次:可可后移动一次或停在原 ...

  4. Java加载资源文件几种方法

    from: http://andyzhu.blog.51cto.com/4386758/775836/ import java.net.URL; import org.springframework. ...

  5. Chrome 开发工具之Console

    前段时间看git的相关,记的笔记也大致写到了博客上,还有些因为运用不熟,或者还有一些疑惑点,暂时也不做过多纠缠,之后在实践中多运用得出结论再整理分享吧. 工欲善其事,必先利其器.要想做好前端的工作,也 ...

  6. 【转载】STL之priority_queue

    参考资料:传送门先回顾队列的定义:队列(queue)维护了一组对象,进入队列的对象被放置在尾部,下一个被取出的元素则取自队列的首部.priority_queue特别之处在于,允许用户为队列中存储的元素 ...

  7. 遍历hashMap的两种方式

    第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...

  8. 你想要了解但是却羞于发问的有关SSL的一切

    Everything You Ever Wanted to Know About SSL (but Were Afraid to Ask) Or perhaps more accurately, &q ...

  9. Python_查询手机供应商

    def Seach_Phone_Nmuber_operator(): CN_mobile = [134,135,136,137,138,139,150,151,152,157,158,159,182, ...

  10. Python PIP安装

    https://zhidao.baidu.com/question/550936793.html 按图做