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 | |
Brian | a6553ed | 2014-04-02 21:26:46 -0700 | [diff] [blame] | 6 | #include "aos/common/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 | } |
| 38 | Eigen::Matrix<Scalar, 2, 1> parallel_vector; |
| 39 | Eigen::Matrix<Scalar, 2, 1> perpendicular_vector; |
| 40 | perpendicular_vector = K.transpose().normalized(); |
| 41 | parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0); |
| 42 | |
| 43 | Eigen::Matrix<Scalar, 4, 1> projectedh = region.static_H() * parallel_vector; |
| 44 | Eigen::Matrix<Scalar, 4, 1> projectedk = |
| 45 | region.static_k() - region.static_H() * perpendicular_vector * w; |
| 46 | |
| 47 | Scalar min_boundary = ::std::numeric_limits<Scalar>::lowest(); |
| 48 | Scalar max_boundary = ::std::numeric_limits<Scalar>::max(); |
| 49 | for (int i = 0; i < 4; ++i) { |
| 50 | if (projectedh(i, 0) > 0) { |
| 51 | max_boundary = |
| 52 | ::std::min(max_boundary, projectedk(i, 0) / projectedh(i, 0)); |
| 53 | } else { |
| 54 | min_boundary = |
| 55 | ::std::max(min_boundary, projectedk(i, 0) / projectedh(i, 0)); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | Eigen::Matrix<Scalar, 1, 2> vertices; |
| 60 | vertices << max_boundary, min_boundary; |
| 61 | |
| 62 | if (max_boundary > min_boundary) { |
| 63 | Scalar min_distance_sqr = 0; |
| 64 | Eigen::Matrix<Scalar, 2, 1> closest_point; |
| 65 | for (int i = 0; i < vertices.innerSize(); i++) { |
| 66 | Eigen::Matrix<Scalar, 2, 1> point; |
| 67 | point = parallel_vector * vertices(0, i) + perpendicular_vector * w; |
| 68 | const Scalar length = (R - point).squaredNorm(); |
| 69 | if (i == 0 || length < min_distance_sqr) { |
| 70 | closest_point = point; |
| 71 | min_distance_sqr = length; |
| 72 | } |
| 73 | } |
| 74 | if (is_inside) *is_inside = true; |
| 75 | return closest_point; |
| 76 | } else { |
| 77 | Eigen::Matrix<Scalar, 2, 4> region_vertices = region.StaticVertices(); |
| 78 | #ifdef __linux__ |
| 79 | CHECK_GT(region_vertices.outerSize(), 0); |
| 80 | #else |
| 81 | assert(region_vertices.outerSize() > 0); |
| 82 | #endif |
| 83 | Scalar min_distance = INFINITY; |
| 84 | int closest_i = 0; |
| 85 | for (int i = 0; i < region_vertices.outerSize(); i++) { |
| 86 | const Scalar length = ::std::abs( |
| 87 | (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0)); |
| 88 | if (i == 0 || length < min_distance) { |
| 89 | closest_i = i; |
| 90 | min_distance = length; |
| 91 | } |
| 92 | } |
| 93 | if (is_inside) *is_inside = false; |
| 94 | return (Eigen::Matrix<Scalar, 2, 1>() << region_vertices(0, closest_i), |
| 95 | region_vertices(1, closest_i)) |
| 96 | .finished(); |
| 97 | } |
| 98 | } |
| 99 | |
James Kuszmaul | fb0e0ae | 2014-03-25 07:04:47 -0700 | [diff] [blame] | 100 | } // namespace control_loops |
| 101 | } // namespace frc971 |
| 102 | |
| 103 | #endif // FRC971_CONTROL_LOOPS_COERCE_GOAL_H_ |