版权声明:本文为博主原创博文,未经允许不得转载,若要转载,请说明出处并给出博文链接
现如今,四旋翼飞行器已经从几年前的遥控航模变成真正可以超视距操控的无人机,离不开伟大的通信协议mavlink和最近几年大火的4G与5G。但是这些都要依托一个强大的飞控,开源飞控项目ArduPilot的分支ArduCopter就是一个非常优秀的多旋翼飞控,但是要真正实现无人机的航线规划而后自主飞行,就要着实学习下本博客涉及的航点导航的内容,另外还有一篇ArduCopter——ArduPilot——航点导航WPNav(二)——Spline Navigation
①下边代码是航点导航的具体实现,被用于上层结构的调用。我们这里不关心上层如何调用,只看如何实现
/// update_wpnav - run the wp controller - should be called at 100hz or higher
bool AC_WPNav::update_wpnav()
{
bool ret = true;
// calculate dt
float dt = _pos_control.time_since_last_xy_update();
if (dt >= 0.2f) {
dt = 0.0f;
}
// 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_cmss);
_pos_control.set_accel_z(_wp_accel_z_cmss);
// advance the target if necessary
if (!advance_wp_target_along_track(dt)) { //航点导航的关键函数
// To-Do: handle inability to advance along track (probably because of missing terrain data)
ret = false;
}
// freeze feedforwards during known discontinuities
if (_flags.new_wp_destination) {
_flags.new_wp_destination = false;
_pos_control.freeze_ff_z();
}
_pos_control.update_xy_controller(1.0f);
check_wp_leash_length();
_wp_last_update = AP_HAL::millis();
return ret;
}
②函数advance_wp_target_along_track是航点导航实现的关键函数,主要是添加目标航点与origin航线中的中间航点还有track_leash的一些限制。
/// advance_wp_target_along_track - move target location along track from origin to destination
bool AC_WPNav::advance_wp_target_along_track(float dt)
{
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.
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
float track_desired_max; // the farthest distance (in cm) along the track that the leash will allow
float track_leash_slack; // additional distance (in cm) along the track from our track_covered position that our leash will allow
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)) {
return false;
}
// calculate 3d vector from segment's origin 计算从origin到当前位置的三维向量
Vector3f curr_delta = (curr_pos - Vector3f(0,0,terr_offset)) - _origin;
// 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;
// calculate the point closest to the vehicle on the segment from origin to destination 将上述距离投射到XYZ三个方向上
Vector3f track_covered_pos = _pos_delta_unit * track_covered;
// calculate the distance vector from the vehicle to the closest point on the segment from origin to destination 计算飞机到最近点的距离从origin到目的地
track_error = curr_delta - track_covered_pos;
// calculate the horizontal error
_track_error_xy = norm(track_error.x, track_error.y);
// calculate the vertical error
float track_error_z = fabsf(track_error.z);
// get up leash if we are moving up, down leash if we are moving down
float leash_z = track_error.z >= 0 ? _pos_control.get_leash_up_z() : _pos_control.get_leash_down_z();
// use pythagoras's theorem calculate how far along the track we could move the intermediate target before reaching the end of the leash
// track_desired_max is the distance from the vehicle to our target point along the track. It is the "hypotenuse" which we want to be no longer than our leash (aka _track_leash_length)
// track_error is the line from the vehicle to the closest point on the track. It is the "opposite" side
// track_leash_slack is the line from the closest point on the track to the target point. It is the "adjacent" side. We adjust this so the track_desired_max is no longer than the leash
// 运用 “勾股定理” 计算飞机在触及leash尾端之前可以移动到的中间目标点的距离
// track_desired_max 是飞机到目标航点的距离,是最长的斜边
// track_error 是飞机到最近点的直线,是对边
// track_leash_slack 是最近点到目标航点的距离,是邻边。我们只需要调整邻边,就可以保证斜边不超过leash
float track_leash_length_abs = fabsf(_track_leash_length);
float track_error_max_abs = MAX(_track_leash_length*track_error_z/leash_z, _track_leash_length*_track_error_xy/_pos_control.get_leash_xy());
track_leash_slack = (track_leash_length_abs > track_error_max_abs) ? safe_sqrt(sq(_track_leash_length) - sq(track_error_max_abs)) : 0;
track_desired_max = track_covered + track_leash_slack;
// check if target is already beyond the leash
if (_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;
// calculate point at which velocity switches from linear to sqrt
float linear_velocity = _wp_speed_cms;
float kP = _pos_control.get_pos_xy_p().kP();
if (is_positive(kP)) { // avoid divide by zero
linear_velocity = _track_accel/kP; //kP可以理解为1/dt
}
// let the limited_speed_xy_cms be some range above or below current velocity along track
if (speed_along_track < -linear_velocity) {
// 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; //这里的_limited_speed_xy_cms 可以理解为将中间目标点推向最终航点的速度
}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;
}
// 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);
// check if we should begin slowing down
if (!_flags.fast_waypoint) {
float dist_to_dest = _track_length - _track_desired;
if (!_flags.slowing_down && dist_to_dest <= _slow_down_dist) {
_flags.slowing_down = true;
}
// 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));
}
}
// 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;
// reduce speed if we reach end of leash 如果已经到达了leash的末端就减速
if (_track_desired > track_desired_max) {
_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);
} else {
_track_desired = constrain_float(_track_desired, 0, _track_length + WPNAV_WP_FAST_OVERSHOOT_MAX);
}
// recalculate the desired position
Vector3f final_target = _origin + _pos_delta_unit * _track_desired;
// 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 通过reached_destination标志来判断是否到达了航点
if( !_flags.reached_destination ) {
if( _track_desired >= _track_length ) {
// "fast" waypoints are complete once the intermediate point reaches the destination
if (_flags.fast_waypoint) {
_flags.reached_destination = true;
}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;
}
}
}
}
// update the target yaw if origin and destination are at least 2m apart horizontally
// 如果到达目的地的距离超过2m那么就要更新当前目标航向角
if (_track_length_xy >= WPNAV_YAW_DIST_MIN) {
if (_pos_control.get_leash_xy() < WPNAV_YAW_DIST_MIN) {
// if the leash is short (i.e. moving slowly) and destination is at least 2m horizontally, point along the segment from origin to destination
set_yaw_cd(get_bearing_cd(_origin, _destination));
} else {
Vector3f horiz_leash_xy = final_target - curr_pos;
horiz_leash_xy.z = 0;
if (horiz_leash_xy.length() > MIN(WPNAV_YAW_DIST_MIN, _pos_control.get_leash_xy()*WPNAV_YAW_LEASH_PCT_MIN)) {
set_yaw_cd(RadiansToCentiDegrees(atan2f(horiz_leash_xy.y,horiz_leash_xy.x)));
}
}
}
// successfully advanced along track
return true;
}
③最后这段代码就是根据位置控制器里面输出的是否需要重新计算leash长度,如果不需要就返回,需要就将位置控制器计算好的leash_xy和leash_z,与根据_pos_delta_unit的水平xy与高度z的相比较的最小数值来计算leash
// check_wp_leash_length - check if waypoint leash lengths need to be recalculated
// should be called after _pos_control.update_xy_controller which may have changed the position controller leash lengths
void AC_WPNav::check_wp_leash_length()
{
// exit immediately if recalc is not required
if (_flags.recalc_wp_leash) {
calculate_wp_leash_length();
}
}
/// calculate_wp_leash_length - calculates horizontal and vertical leash lengths for waypoint controller
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);
float speed_z;
float leash_z;
if (_pos_delta_unit.z >= 0.0f) {
speed_z = _wp_speed_up_cms;
leash_z = _pos_control.get_leash_up_z();
}else{
speed_z = _wp_speed_down_cms;
leash_z = _pos_control.get_leash_down_z();
}
// calculate the maximum acceleration, maximum velocity, and leash length in the direction of travel
if(is_zero(pos_delta_unit_z) && is_zero(pos_delta_unit_xy)){
_track_accel = 0;
_track_speed = 0;
_track_leash_length = WPNAV_LEASH_LENGTH_MIN;
}else if(is_zero(_pos_delta_unit.z)){
_track_accel = _wp_accel_cmss/pos_delta_unit_xy;
_track_speed = _wp_speed_cms/pos_delta_unit_xy;
_track_leash_length = _pos_control.get_leash_xy()/pos_delta_unit_xy;
}else if(is_zero(pos_delta_unit_xy)){
_track_accel = _wp_accel_z_cmss/pos_delta_unit_z;
_track_speed = speed_z/pos_delta_unit_z;
_track_leash_length = leash_z/pos_delta_unit_z;
}else{
_track_accel = MIN(_wp_accel_z_cmss/pos_delta_unit_z, _wp_accel_cmss/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);
// set recalc leash flag to false
_flags.recalc_wp_leash = false;
}
④ 这里给出pos_delta_unit的由来,其实就是目的地与origin距离的三维向量与该距离的比值
Vector3f pos_delta = _destination - _origin;
_track_length = pos_delta.length(); // get track length
_track_length_xy = safe_sqrt(sq(pos_delta.x)+sq(pos_delta.y)); // get horizontal track length (used to decide if we should update yaw)
// 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;
}