blob: 4705f8673f8caf8cd6accadabf8e782fe36aef9d [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_shooter_shooter_H_
#define FRC971_CONTROL_LOOPS_shooter_shooter_H_
#include <memory>
#include "aos/common/control_loop/ControlLoop.h"
#include "frc971/control_loops/state_feedback_loop.h"
#include "aos/common/time.h"
#include "frc971/constants.h"
#include "frc971/control_loops/shooter/shooter_motor_plant.h"
#include "frc971/control_loops/shooter/shooter.q.h"
namespace frc971 {
namespace control_loops {
namespace testing {
class ShooterTest_NoWindupPositive_Test;
class ShooterTest_NoWindupNegative_Test;
};
using ::aos::time::Time;
// Note: Everything in this file assumes that there is a 1 cycle delay between
// power being requested and it showing up at the motor. It assumes that
// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
// that isn't true.
// This class implements the CapU function correctly given all the extra
// information that we know about from the wrist motor.
// It does not have any zeroing logic in it, only logic to deal with a delta U
// controller.
class ZeroedStateFeedbackLoop : public StateFeedbackLoop<3, 1, 1> {
public:
ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> loop)
: StateFeedbackLoop<3, 1, 1>(loop),
voltage_(0.0),
last_voltage_(0.0),
uncapped_voltage_(0.0),
offset_(0.0),
encoder_(0.0),
last_encoder_(0.0){}
const static int kZeroingMaxVoltage = 5;
// Caps U, but this time respects the state of the wrist as well.
virtual void CapU();
// Returns the accumulated voltage.
double voltage() const { return voltage_; }
// Returns the uncapped voltage.
double uncapped_voltage() const { return uncapped_voltage_; }
// Zeros the accumulator.
void ZeroPower() { voltage_ = 0.0; }
enum JointZeroingState {
// We don't know where the joint is at all.
UNKNOWN_POSITION,
// Ready for use during teleop.
CALIBRATED
};
void set_zeroing_state(JointZeroingState zeroing_state) {
zeroing_state_ = zeroing_state;
}
JointZeroingState zeroing_state() const { return zeroing_state_; }
// Sets the calibration offset given the absolute angle and the corrisponding
// encoder value.
void SetCalibration(double encoder_val, double known_position) {
offset_ = known_position - encoder_val;
}
bool SetCalibrationOnEdge(const constants::Values::ShooterLimits &shooter_values,
JointZeroingState zeroing_state) {
double edge_encoder;
double known_position;
if (GetPositionOfEdge(shooter_values, &edge_encoder, &known_position)) {
LOG(INFO, "Calibration edge.\n");
SetCalibration(edge_encoder, known_position);
set_zeroing_state(zeroing_state);
return true;
}
return false;
}
void SetPositionValues(double position) {
Eigen::Matrix<double, 1, 1> Y;
Y << position;
Correct(Y);
}
void SetGoalPosition(double desired_position,
double desired_velocity) {
// austin said something about which matrix to set, but I didn't under
// very much of it
//some_matrix = {desired_position, desired_velocity};
R << desired_position, desired_velocity, 0;
}
double position() const { return X_hat(0, 0); }
double encoder() const { return encoder_; }
double last_encoder() const { return last_encoder_; }
// Returns true if an edge was detected in the last cycle and then sets the
// edge_position to the absolute position of the edge.
bool GetPositionOfEdge(const constants::Values::ShooterLimits &shooter,
double *edge_encoder, double *known_position);
#undef COUNT_SETTER_GETTER
private:
// The accumulated voltage to apply to the motor.
double voltage_;
double last_voltage_;
double uncapped_voltage_;
double offset_;
double previous_position_;
JointZeroingState zeroing_state_;
double encoder_;
double last_encoder_;
};
class ShooterMotor
: public aos::control_loops::ControlLoop<control_loops::ShooterGroup> {
public:
explicit ShooterMotor(
control_loops::ShooterGroup *my_shooter = &control_loops::shooter_queue_group);
// True if the goal was moved to avoid goal windup.
//bool capped_goal() const { return shooter_.capped_goal(); }
double PowerToPosition(double power) {
LOG(WARNING, "power to position not correctly implemented");
const frc971::constants::Values &values = constants::GetValues();
double new_pos =
(power > values.shooter.upper_limit) ? values.shooter.upper_limit
: (power < 0.0) ? 0.0 : power;
return new_pos;
}
typedef enum {
STATE_INITIALIZE,
STATE_REQUEST_LOAD,
STATE_LOAD_BACKTRACK,
STATE_LOAD,
STATE_LOADING_PROBLEM,
STATE_PREPARE_SHOT,
STATE_READY,
STATE_REQUEST_FIRE,
STATE_PREPARE_FIRE,
STATE_FIRE,
STATE_UNLOAD,
STATE_UNLOAD_MOVE,
STATE_READY_UNLOAD
} State;
protected:
virtual void RunIteration(
const ShooterGroup::Goal *goal,
const control_loops::ShooterGroup::Position *position,
ShooterGroup::Output *output,
ShooterGroup::Status *status);
private:
// Friend the test classes for acces to the internal state.
friend class testing::ShooterTest_NoWindupPositive_Test;
friend class testing::ShooterTest_NoWindupNegative_Test;
control_loops::ShooterGroup::Position last_position_;
ZeroedStateFeedbackLoop shooter_;
// position need to zero
double calibration_position_;
// state machine state
State state_;
// time to giving up on loading problem
Time loading_problem_end_time_;
// wait for brake to set
Time shooter_brake_set_time_;
// we are attempting to take up some of the backlash
// in the gears before the plunger hits
Time prepare_fire_end_time_;
// time that shot must have completed
Time shot_end_time_;
// track cycles that we are stuck to detect errors
int cycles_not_moved_;
DISALLOW_COPY_AND_ASSIGN(ShooterMotor);
};
} // namespace control_loops
} // namespace frc971
#endif // FRC971_CONTROL_LOOPS_shooter_shooter_H_