blob: 845f1c6325376b63ef334f2ebd0509d85638eeb7 [file] [log] [blame]
#include "frc971/control_loops/index.h"
#include <stdio.h>
#include <algorithm>
#include "aos/aos_core.h"
#include "aos/common/messages/RobotState.q.h"
#include "aos/common/control_loop/control_loops.q.h"
#include "aos/common/logging/logging.h"
#include "frc971/constants.h"
#include "frc971/control_loops/index_motor_plant.h"
using ::aos::time::Time;
namespace frc971 {
namespace control_loops {
IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
: aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
wrist_loop_(new StateFeedbackLoop<2, 1, 1>(MakeIndexLoop())),
hopper_disc_count_(0),
total_disc_count_(0),
loader_up_(false),
disc_clamped_(false),
disc_ejected_(false),
last_bottom_disc_detect_(false) {
}
const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
bool IndexMotor::FetchConstants() {
if (!constants::horizontal_lower_limit(&horizontal_lower_limit_)) {
LOG(ERROR, "Failed to fetch the horizontal lower limit constant.\n");
return false;
}
if (!constants::horizontal_upper_limit(&horizontal_upper_limit_)) {
LOG(ERROR, "Failed to fetch the horizontal upper limit constant.\n");
return false;
}
if (!constants::horizontal_hall_effect_start_angle(
&horizontal_hall_effect_start_angle_)) {
LOG(ERROR, "Failed to fetch the horizontal start angle constant.\n");
return false;
}
if (!constants::horizontal_zeroing_speed(
&horizontal_zeroing_speed_)) {
LOG(ERROR, "Failed to fetch the horizontal zeroing speed constant.\n");
return false;
}
return true;
}
// Distance to move the indexer when grabbing a disc.
const double kNextPosition = 10.0;
/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
}
/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(const double angle) {
return angle * (kDiscRadius + kRollerRadius);
}
/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
}
/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
return IndexMotor::ConvertDiscAngleToDiscPosition(
ConvertIndexToDiscAngle(angle));
}
// Positive angle is towards the shooter, and positive power is towards the
// shooter.
void IndexMotor::RunIteration(
const control_loops::IndexLoop::Goal *goal,
const control_loops::IndexLoop::Position *position,
control_loops::IndexLoop::Output *output,
control_loops::IndexLoop::Status *status) {
// Make goal easy to work with.
Goal goal_enum = static_cast<Goal>(goal->goal_state);
// Disable the motors now so that all early returns will return with the
// motors disabled.
if (output) {
output->transfer_voltage = 0.0;
output->index_voltage = 0.0;
}
status->ready_to_intake = false;
// Cache the constants to avoid error handling down below.
if (!FetchConstants()) {
return;
}
if (position) {
wrist_loop_->Y << position->index_position;
}
const double index_position = wrist_loop_->X_hat(0, 0);
bool safe_to_change_state_ = true;
switch (safe_goal_) {
case HOLD:
// The goal should already be good, so sit tight with everything the same
// as it was.
printf("HOLD Not implemented\n");
break;
case READY_LOWER:
printf("READY_LOWER Not implemented\n");
break;
case INTAKE:
{
Time now = Time::Now();
if (hopper_disc_count_ < 4) {
output->transfer_voltage = 12.0;
}
// Posedge of the disc entering the beam break.
if (position) {
if (position->bottom_disc_detect && !last_bottom_disc_detect_) {
transfer_frisbee_.Reset();
transfer_frisbee_.bottom_posedge_time_ = now;
printf("Posedge of bottom disc %f\n",
transfer_frisbee_.bottom_posedge_time_.ToSeconds());
++hopper_disc_count_;
}
// Disc exited the beam break now.
if (!position->bottom_disc_detect && last_bottom_disc_detect_) {
transfer_frisbee_.bottom_negedge_time_ = now;
printf("Negedge of bottom disc %f\n",
transfer_frisbee_.bottom_negedge_time_.ToSeconds());
frisbees_.push_front(transfer_frisbee_);
}
if (position->bottom_disc_detect) {
output->transfer_voltage = 12.0;
// Must wait until the disc gets out before we can change state.
safe_to_change_state_ = false;
// TODO(aschuh): A disc on the way through needs to start moving the
// indexer if it isn't already moving. Maybe?
Time elapsed_posedge_time = now -
transfer_frisbee_.bottom_posedge_time_;
if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
// It has been too long. The disc must be jammed.
LOG(ERROR, "Been way too long. Jammed disc?\n");
printf("Been way too long. Jammed disc?\n");
}
}
for (Frisbee &frisbee : frisbees_) {
if (!frisbee.has_been_indexed_) {
output->transfer_voltage = 12.0;
Time elapsed_posedge_time = now -
frisbee.bottom_posedge_time_;
if (elapsed_posedge_time >= Time::InSeconds(0.07)) {
// Should have just engaged.
// Save the indexer position, and the time.
// It has been long enough since the disc entered the indexer.
// Treat now as the time at which it contacted the indexer.
LOG(INFO, "Grabbed on the index now at %f\n", index_position);
printf("Grabbed on the index now at %f\n", index_position);
frisbee.has_been_indexed_ = true;
frisbee.index_start_position_ = index_position;
frisbee.index_start_time_ = now;
}
}
if (!frisbee.has_been_indexed_) {
// Discs must all be indexed before it is safe to stop indexing.
safe_to_change_state_ = false;
}
}
double new_index_position = wrist_loop_->R(0, 0);
// TODO(aschuh): As we loop through, assess the state of the indexer
// and figure if the bottom disc is in a place such that we can
// intake without filling the hopper early.
// status->ready_to_intake = false;
for (Frisbee &frisbee : frisbees_) {
if (frisbee.has_been_indexed_) {
// We want to store it pi from where the disc was grabbed
// (for now).
new_index_position = ::std::max(
new_index_position,
(frisbee.index_start_position_ +
ConvertDiscAngleToIndex(M_PI)));
// TODO(aschuh): We should be able to pick the M_PI knowing if
// the next disc is coming in hot or not.
}
}
wrist_loop_->R << new_index_position, 0.0;
}
printf("INTAKE Not implemented\n");
}
break;
case READY_SHOOTER:
printf("READY_SHOOTER Not implemented\n");
break;
case SHOOT:
printf("SHOOT Not implemented\n");
break;
}
// Update the observer.
wrist_loop_->Update(position != NULL, output == NULL);
if (position) {
LOG(DEBUG, "pos=%f currently %f\n",
position->index_position, index_position);
last_bottom_disc_detect_ = position->bottom_disc_detect;
}
status->hopper_disc_count = hopper_disc_count_;
status->total_disc_count = total_disc_count_;
if (output) {
output->index_voltage = wrist_loop_->U(0, 0);
}
if (safe_to_change_state_) {
safe_goal_ = goal_enum;
}
}
} // namespace control_loops
} // namespace frc971