blob: 0448e09d8b06826929202e240b00b20543be38ae [file] [log] [blame]
#ifndef Y2015_CONTROL_LOOPS_FRIDGE_H_
#define Y2015_CONTROL_LOOPS_FRIDGE_H_
#include <memory>
#include "aos/common/controls/control_loop.h"
#include "aos/common/util/trapezoid_profile.h"
#include "frc971/control_loops/state_feedback_loop.h"
#include "y2015/control_loops/fridge/fridge.q.h"
#include "frc971/zeroing/zeroing.h"
#include "y2015/util/kinematics.h"
namespace frc971 {
namespace control_loops {
namespace testing {
class FridgeTest_DisabledGoalTest_Test;
class FridgeTest_ArmGoalPositiveWindupTest_Test;
class FridgeTest_ElevatorGoalPositiveWindupTest_Test;
class FridgeTest_ArmGoalNegativeWindupTest_Test;
class FridgeTest_ElevatorGoalNegativeWindupTest_Test;
class FridgeTest_SafeArmZeroing_Test;
} // namespace testing
template<int S>
class CappedStateFeedbackLoop : public StateFeedbackLoop<S, 2, 2> {
public:
CappedStateFeedbackLoop(StateFeedbackLoop<S, 2, 2> &&loop)
: StateFeedbackLoop<S, 2, 2>(::std::move(loop)), max_voltage_(12.0) {}
void set_max_voltage(double max_voltage) {
max_voltage_ = ::std::max(0.0, ::std::min(12.0, max_voltage));
}
void CapU() override;
// Returns the amount to change the position goals (average and difference) in
// order to no longer saturate the controller.
Eigen::Matrix<double, 2, 1> UnsaturateOutputGoalChange();
private:
double max_voltage_;
};
class Fridge
: public aos::controls::ControlLoop<control_loops::FridgeQueue> {
public:
explicit Fridge(
control_loops::FridgeQueue *fridge_queue = &control_loops::fridge_queue);
enum State {
// Waiting to receive data before doing anything.
UNINITIALIZED = 0,
// Estimating the starting location.
INITIALIZING = 1,
// Moving the elevator to find an index pulse.
ZEROING_ELEVATOR = 2,
// Moving the arm to find an index pulse.
ZEROING_ARM = 3,
// All good!
RUNNING = 4,
// Internal error caused the fridge to abort.
ESTOP = 5,
};
enum class ProfilingType : int32_t {
// Use angle/height to specify the fridge goal.
ANGLE_HEIGHT_PROFILING = 0,
// Use x/y co-ordinates to specify the fridge goal.
X_Y_PROFILING = 1,
};
State state() const { return state_; }
protected:
void RunIteration(const control_loops::FridgeQueue::Goal *goal,
const control_loops::FridgeQueue::Position *position,
control_loops::FridgeQueue::Output *output,
control_loops::FridgeQueue::Status *status) override;
private:
friend class testing::FridgeTest_DisabledGoalTest_Test;
friend class testing::FridgeTest_ElevatorGoalPositiveWindupTest_Test;
friend class testing::FridgeTest_ArmGoalPositiveWindupTest_Test;
friend class testing::FridgeTest_ElevatorGoalNegativeWindupTest_Test;
friend class testing::FridgeTest_ArmGoalNegativeWindupTest_Test;
friend class testing::FridgeTest_SafeArmZeroing_Test;
// Sets state_ to the correct state given the current state of the zeroing
// estimators.
void UpdateZeroingState();
void SetElevatorOffset(double left_offset, double right_offset);
void SetArmOffset(double left_offset, double right_offset);
// Getters for the current elevator positions.
double left_elevator();
double right_elevator();
double elevator();
// Getters for the current arm positions.
double left_arm();
double right_arm();
double arm();
// Our best guess at the current position of the elevator.
double estimated_left_elevator();
double estimated_right_elevator();
double estimated_elevator();
// Our best guess at the current position of the arm.
double estimated_left_arm();
double estimated_right_arm();
double estimated_arm();
// Returns the current zeroing velocity for either subsystem.
// If the subsystem is too far away from the center, these will switch
// directions.
double elevator_zeroing_velocity();
double arm_zeroing_velocity();
// Corrects the Observer with the current position.
void Correct();
double UseUnlessZero(double target_value, double default_value);
// The state feedback control loop or loops to talk to.
::std::unique_ptr<CappedStateFeedbackLoop<5>> arm_loop_;
::std::unique_ptr<CappedStateFeedbackLoop<4>> elevator_loop_;
zeroing::ZeroingEstimator left_arm_estimator_;
zeroing::ZeroingEstimator right_arm_estimator_;
zeroing::ZeroingEstimator left_elevator_estimator_;
zeroing::ZeroingEstimator right_elevator_estimator_;
// Offsets from the encoder position to the absolute position. Add these to
// the encoder position to get the absolute position.
double left_elevator_offset_ = 0.0;
double right_elevator_offset_ = 0.0;
double left_arm_offset_ = 0.0;
double right_arm_offset_ = 0.0;
// Current velocity to move at while zeroing.
double elevator_zeroing_velocity_ = 0.0;
double arm_zeroing_velocity_ = 0.0;
// The goals for the elevator and arm.
double elevator_goal_ = 0.0;
double arm_goal_ = 0.0;
double arm_goal_velocity_ = 0.0;
double elevator_goal_velocity_ = 0.0;
State state_ = UNINITIALIZED;
State last_state_ = UNINITIALIZED;
control_loops::FridgeQueue::Position current_position_;
ProfilingType last_profiling_type_;
aos::util::ElevatorArmKinematics kinematics_;
aos::util::TrapezoidProfile arm_profile_;
aos::util::TrapezoidProfile elevator_profile_;
aos::util::TrapezoidProfile x_profile_;
aos::util::TrapezoidProfile y_profile_;
};
} // namespace control_loops
} // namespace frc971
#endif // Y2015_CONTROL_LOOPS_FRIDGE_H_