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