James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 1 | #ifndef FRC971_CONTROL_LOOPS_COERCE_GOAL_H_ |
| 2 | #define FRC971_CONTROL_LOOPS_COERCE_GOAL_H_ |
| 3 | |
| 4 | #include "Eigen/Dense" |
| 5 | |
John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 6 | #include "aos/controls/polytope.h" |
James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 7 | |
| 8 | namespace frc971 { |
| 9 | namespace control_loops { |
| 10 | |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 11 | template <typename Scalar = double> |
| 12 | Eigen::Matrix<Scalar, 2, 1> DoCoerceGoal( |
| 13 | const aos::controls::HVPolytope<2, 4, 4, Scalar> ®ion, |
| 14 | const Eigen::Matrix<Scalar, 1, 2> &K, Scalar w, |
| 15 | const Eigen::Matrix<Scalar, 2, 1> &R, bool *is_inside); |
Brian Silverman | 6dd2c53 | 2014-03-29 23:34:39 -0700 | [diff] [blame] | 16 | |
James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 17 | // Intersects a line with a region, and finds the closest point to R. |
| 18 | // Finds a point that is closest to R inside the region, and on the line |
| 19 | // defined by K X = w. If it is not possible to find a point on the line, |
| 20 | // finds a point that is inside the region and closest to the line. |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 21 | template <typename Scalar = double> |
| 22 | static inline Eigen::Matrix<Scalar, 2, 1> CoerceGoal( |
| 23 | const aos::controls::HVPolytope<2, 4, 4, Scalar> ®ion, |
| 24 | const Eigen::Matrix<Scalar, 1, 2> &K, Scalar w, |
| 25 | const Eigen::Matrix<Scalar, 2, 1> &R) { |
Brian Silverman | 6dd2c53 | 2014-03-29 23:34:39 -0700 | [diff] [blame] | 26 | return DoCoerceGoal(region, K, w, R, nullptr); |
| 27 | } |
James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 28 | |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 29 | template <typename Scalar> |
| 30 | Eigen::Matrix<Scalar, 2, 1> DoCoerceGoal( |
| 31 | const aos::controls::HVPolytope<2, 4, 4, Scalar> ®ion, |
| 32 | const Eigen::Matrix<Scalar, 1, 2> &K, Scalar w, |
| 33 | const Eigen::Matrix<Scalar, 2, 1> &R, bool *is_inside) { |
| 34 | if (region.IsInside(R)) { |
| 35 | if (is_inside) *is_inside = true; |
| 36 | return R; |
| 37 | } |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 38 | const Scalar norm_K = K.norm(); |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 39 | Eigen::Matrix<Scalar, 2, 1> parallel_vector; |
| 40 | Eigen::Matrix<Scalar, 2, 1> perpendicular_vector; |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 41 | // Calculate a vector that is perpendicular to the line defined by K * x = w. |
| 42 | perpendicular_vector = K.transpose() / norm_K; |
| 43 | // Calculate a vector that is parallel to the line defined by K * x = w. |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 44 | parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0); |
| 45 | |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 46 | // Calculate the location along the K x = w line where each boundary of the |
| 47 | // polytope would intersect. |
| 48 | // I.e., we want to calculate the distance along the K x = w line, as |
| 49 | // represented by |
| 50 | // parallel_vector * dist + perpendicular_vector * w / norm(K), |
| 51 | // such that it intersects with H_i * x = k_i. |
| 52 | // This gives us H_i * (parallel * dist + perp * w / norm(K)) = k_i. |
| 53 | // dist = (k_i - H_i * perp * w / norm(K)) / (H_i * parallel) |
| 54 | // projectedh is the numerator, projectedk is the denominator. |
| 55 | // The case where H_i * parallel is zero indicates a scenario where the given |
| 56 | // boundary of the polytope is parallel to the line, and so there is no |
| 57 | // meaningful value of dist to return. |
| 58 | // Note that the sign of projectedh will also indicate whether the distance is |
| 59 | // an upper or lower bound. If since valid points satisfy H_i * x < k_i, then |
| 60 | // if H_i * parallel is less than zero, then dist will be a lower bound and |
| 61 | // if it is greater than zero, then dist will be an upper bound. |
| 62 | const Eigen::Matrix<Scalar, 4, 1> projectedh = |
| 63 | region.static_H() * parallel_vector; |
| 64 | const Eigen::Matrix<Scalar, 4, 1> projectedk = |
| 65 | region.static_k() - region.static_H() * perpendicular_vector * w / norm_K; |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 66 | |
| 67 | Scalar min_boundary = ::std::numeric_limits<Scalar>::lowest(); |
| 68 | Scalar max_boundary = ::std::numeric_limits<Scalar>::max(); |
| 69 | for (int i = 0; i < 4; ++i) { |
| 70 | if (projectedh(i, 0) > 0) { |
| 71 | max_boundary = |
| 72 | ::std::min(max_boundary, projectedk(i, 0) / projectedh(i, 0)); |
Lee Mracek | 6568614 | 2020-01-10 22:21:39 -0500 | [diff] [blame] | 73 | } else if (projectedh(i, 0) != 0) { |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 74 | min_boundary = |
| 75 | ::std::max(min_boundary, projectedk(i, 0) / projectedh(i, 0)); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | Eigen::Matrix<Scalar, 1, 2> vertices; |
| 80 | vertices << max_boundary, min_boundary; |
| 81 | |
| 82 | if (max_boundary > min_boundary) { |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 83 | // The line goes through the region (if the line just intersects a single |
| 84 | // vertex, then we fall through to the next clause), but R is not |
| 85 | // inside the region. The returned point will be one of the two points where |
| 86 | // the line intersects the edge of the region. |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 87 | Scalar min_distance_sqr = 0; |
| 88 | Eigen::Matrix<Scalar, 2, 1> closest_point; |
| 89 | for (int i = 0; i < vertices.innerSize(); i++) { |
| 90 | Eigen::Matrix<Scalar, 2, 1> point; |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 91 | point = |
| 92 | parallel_vector * vertices(0, i) + perpendicular_vector * w / norm_K; |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 93 | const Scalar length = (R - point).squaredNorm(); |
| 94 | if (i == 0 || length < min_distance_sqr) { |
| 95 | closest_point = point; |
| 96 | min_distance_sqr = length; |
| 97 | } |
| 98 | } |
| 99 | if (is_inside) *is_inside = true; |
| 100 | return closest_point; |
| 101 | } else { |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 102 | // The line does not pass through the region; identify the vertex closest to |
| 103 | // the line. |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 104 | Eigen::Matrix<Scalar, 2, 4> region_vertices = region.StaticVertices(); |
| 105 | #ifdef __linux__ |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 106 | CHECK_GT(reinterpret_cast<ssize_t>(region_vertices.outerSize()), 0); |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 107 | #else |
| 108 | assert(region_vertices.outerSize() > 0); |
| 109 | #endif |
| 110 | Scalar min_distance = INFINITY; |
| 111 | int closest_i = 0; |
| 112 | for (int i = 0; i < region_vertices.outerSize(); i++) { |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 113 | // Calculate the distance of the vertex from the line. The closest vertex |
| 114 | // will be the one to return. |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 115 | const Scalar length = ::std::abs( |
James Kuszmaul | 0ee1fce | 2020-01-11 16:59:21 -0800 | [diff] [blame] | 116 | (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0) - |
| 117 | w); |
Austin Schuh | bcce26a | 2018-03-26 23:41:24 -0700 | [diff] [blame] | 118 | if (i == 0 || length < min_distance) { |
| 119 | closest_i = i; |
| 120 | min_distance = length; |
| 121 | } |
| 122 | } |
| 123 | if (is_inside) *is_inside = false; |
| 124 | return (Eigen::Matrix<Scalar, 2, 1>() << region_vertices(0, closest_i), |
| 125 | region_vertices(1, closest_i)) |
| 126 | .finished(); |
| 127 | } |
| 128 | } |
| 129 | |
James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 130 | } // namespace control_loops |
| 131 | } // namespace frc971 |
| 132 | |
| 133 | #endif // FRC971_CONTROL_LOOPS_COERCE_GOAL_H_ |