blob: 0a22700a97a6f284c0d77832780aabe5775a005f [file] [log] [blame]
James Kuszmaulb1b2d8e2020-02-21 21:11:46 -08001#include "y2020/control_loops/superstructure/turret/aiming.h"
2
3#include "frc971/control_loops/pose.h"
4#include "y2020/control_loops/drivetrain/drivetrain_base.h"
5
6namespace y2020 {
7namespace control_loops {
8namespace superstructure {
9namespace turret {
10
11using frc971::control_loops::Pose;
12
13namespace {
14flatbuffers::DetachedBuffer MakePrefilledGoal() {
15 flatbuffers::FlatBufferBuilder fbb;
16 fbb.ForceDefaults(true);
17 Aimer::Goal::Builder builder(fbb);
18 builder.add_unsafe_goal(0);
19 builder.add_goal_velocity(0);
20 builder.add_ignore_profile(true);
21 fbb.Finish(builder.Finish());
22 return fbb.Release();
23}
24} // namespace
25
26Aimer::Aimer() : goal_(MakePrefilledGoal()) {}
27
28void Aimer::Update(const Status *status) {
29 // For now, just do enough to keep the turret pointed straight towards (0, 0).
30 // Don't worry about properly handling shooting on the fly--just try to keep
31 // the turret pointed straight towards one target.
32 // This also doesn't do anything intelligent with wrapping--it just produces a
33 // result in the range (-pi, pi] rather than taking advantage of the turret's
34 // full range.
35 Pose goal({0, 0, 0}, 0);
36 const Pose robot_pose({status->x(), status->y(), 0}, status->theta());
37 goal = goal.Rebase(&robot_pose);
38 const double heading_to_goal = goal.heading();
39 CHECK(status->has_localizer());
40 // TODO(james): This code should probably just be in the localizer and have
41 // xdot/ydot get populated in the status message directly... that way we don't
42 // keep duplicating this math.
43 // Also, this doesn't currently take into account the lateral velocity of the
44 // robot. All of this would be helped by just doing this work in the Localizer
45 // itself.
46 const Eigen::Vector2d linear_angular =
47 drivetrain::GetDrivetrainConfig().Tlr_to_la() *
48 Eigen::Vector2d(status->localizer()->left_velocity(),
49 status->localizer()->right_velocity());
50 // X and Y dot are negated because we are interested in the derivative of
51 // (target_pos - robot_pos).
52 const double xdot = -linear_angular(0) * std::cos(status->theta());
53 const double ydot = -linear_angular(0) * std::sin(status->theta());
54 const double rel_x = goal.rel_pos().x();
55 const double rel_y = goal.rel_pos().y();
56 const double squared_norm = rel_x * rel_x + rel_y * rel_y;
57 // If squared_norm gets to be too close to zero, just zero out the relevant
58 // term to prevent NaNs. Note that this doesn't address the chattering that
59 // would likely occur if we were to get excessively close to the target.
60 const double atan_diff = (squared_norm < 1e-3)
61 ? 0.0
62 : (rel_x * ydot - rel_y * xdot) / squared_norm;
63 // heading = atan2(relative_y, relative_x) - robot_theta
64 // dheading / dt = (rel_x * rel_y' - rel_y * rel_x') / (rel_x^2 + rel_y^2) - dtheta / dt
65 const double dheading_dt = atan_diff - linear_angular(1);
66
67 goal_.mutable_message()->mutate_unsafe_goal(heading_to_goal);
68 goal_.mutable_message()->mutate_goal_velocity(dheading_dt);
69}
70
71flatbuffers::Offset<AimerStatus> Aimer::PopulateStatus(
72 flatbuffers::FlatBufferBuilder *fbb) const {
73 AimerStatus::Builder builder(*fbb);
74 builder.add_turret_position(goal_.message().unsafe_goal());
75 builder.add_turret_velocity(goal_.message().goal_velocity());
76 return builder.Finish();
77}
78
79} // namespace turret
80} // namespace superstructure
81} // namespace control_loops
82} // namespace y2020