blob: a827dc9df77165ae0a713c72ee0b7b8d695a49d1 [file] [log] [blame]
#include "frc971/control_loops/catapult/catapult.h"
namespace frc971::control_loops::catapult {
const flatbuffers::Offset<
frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>
Catapult::Iterate(const CatapultGoal *catapult_goal,
const PotAndAbsolutePosition *position,
double battery_voltage, double *catapult_voltage, bool fire,
flatbuffers::FlatBufferBuilder *fbb) {
const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
*return_goal =
catapult_goal != nullptr && catapult_goal->has_return_position()
? catapult_goal->return_position()
: nullptr;
const bool catapult_disabled =
catapult_.Correct(return_goal, position, catapult_voltage == nullptr);
if (catapult_disabled) {
catapult_state_ = CatapultState::PROFILE;
} else if (catapult_.running() && catapult_goal != nullptr && fire &&
!last_firing_) {
catapult_state_ = CatapultState::FIRING;
latched_shot_position = catapult_goal->shot_position();
latched_shot_velocity = catapult_goal->shot_velocity();
}
// Don't update last_firing_ if the catapult is disabled, so that we actually
// end up firing once it's enabled
if (catapult_.running() && !catapult_disabled) {
last_firing_ = fire;
}
use_profile_ = true;
switch (catapult_state_) {
case CatapultState::FIRING: {
// Select the ball controller. We should only be firing if we have a
// ball, or at least should only care about the shot accuracy.
catapult_.set_controller_index(0);
// Ok, so we've now corrected. Next step is to run the MPC.
//
// Since there is a unit delay between when we ask for a U and the
// hardware applies it, we need to run the optimizer for the position at
// the *next* control loop cycle.
Eigen::Vector3d next_X = catapult_.estimated_state();
for (int i = catapult_.controller().plant().coefficients().delayed_u;
i > 1; --i) {
next_X = catapult_.controller().plant().A() * next_X +
catapult_.controller().plant().B() *
catapult_.controller().observer().last_U(i - 1);
}
catapult_mpc_.SetState(
next_X.block<2, 1>(0, 0),
Eigen::Vector2d(latched_shot_position, latched_shot_velocity));
const bool solved = catapult_mpc_.Solve();
current_horizon_ = catapult_mpc_.current_horizon();
const bool started = catapult_mpc_.started();
if (solved || started) {
std::optional<double> solution = catapult_mpc_.Next();
if (!solution.has_value()) {
CHECK_NOTNULL(catapult_voltage);
*catapult_voltage = 0.0;
if (catapult_mpc_.started()) {
++shot_count_;
// Finished the catapult, time to fire.
catapult_state_ = CatapultState::RESETTING;
}
} else {
// TODO(austin): Voltage error?
CHECK_NOTNULL(catapult_voltage);
if (current_horizon_ == 1) {
battery_voltage = 12.0;
}
*catapult_voltage = std::max(
0.0, std::min(12.0, (*solution - 0.0 * next_X(2, 0)) * 12.0 /
std::max(battery_voltage, 8.0)));
use_profile_ = false;
}
} else {
if (!fire) {
// Eh, didn't manage to solve before it was time to fire. Give up.
catapult_state_ = CatapultState::PROFILE;
}
}
if (!use_profile_) {
catapult_.ForceGoal(catapult_.estimated_position(),
catapult_.estimated_velocity());
}
}
if (catapult_state_ != CatapultState::RESETTING) {
break;
} else {
[[fallthrough]];
}
case CatapultState::RESETTING:
if (catapult_.controller().R(1, 0) > 7.0) {
catapult_.mutable_profile()->set_maximum_velocity(7.0);
catapult_.mutable_profile()->set_maximum_acceleration(2000.0);
} else if (catapult_.controller().R(1, 0) > 0.0) {
catapult_.mutable_profile()->set_maximum_velocity(7.0);
catapult_.mutable_profile()->set_maximum_acceleration(1000.0);
} else {
catapult_state_ = CatapultState::PROFILE;
}
[[fallthrough]];
case CatapultState::PROFILE:
break;
}
if (use_profile_) {
if (catapult_state_ != CatapultState::FIRING) {
catapult_mpc_.Reset();
}
// Select the controller designed for when we have no ball.
catapult_.set_controller_index(1);
current_horizon_ = 0u;
const double output_voltage = catapult_.UpdateController(catapult_disabled);
if (catapult_voltage != nullptr) {
*catapult_voltage = output_voltage;
}
}
catapult_.UpdateObserver(catapult_voltage != nullptr
? (*catapult_voltage * battery_voltage / 12.0)
: 0.0);
return catapult_.MakeStatus(fbb);
}
} // namespace frc971::control_loops::catapult