blob: 943e0ad784a28b98323c4547598276afbb8c157a [file] [log] [blame]
#ifndef Y2014_CONTROL_LOOPS_CLAW_CLAW_H_
#define Y2014_CONTROL_LOOPS_CLAW_CLAW_H_
#include <memory>
#include "frc971/control_loops/coerce_goal.h"
#include "frc971/control_loops/control_loop.h"
#include "frc971/control_loops/hall_effect_tracker.h"
#include "frc971/control_loops/polytope.h"
#include "frc971/control_loops/state_feedback_loop.h"
#include "y2014/constants.h"
#include "y2014/control_loops/claw/claw_goal_generated.h"
#include "y2014/control_loops/claw/claw_motor_plant.h"
#include "y2014/control_loops/claw/claw_output_generated.h"
#include "y2014/control_loops/claw/claw_position_generated.h"
#include "y2014/control_loops/claw/claw_status_generated.h"
namespace y2014 {
namespace control_loops {
namespace claw {
namespace testing {
class WindupClawTest;
};
// 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.
class ClawLimitedLoop : public StateFeedbackLoop<4, 2, 2> {
public:
ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop);
virtual void CapU();
void set_is_zeroing(bool is_zeroing) { is_zeroing_ = is_zeroing; }
void set_positions_known(bool top_known, bool bottom_known) {
top_known_ = top_known;
bottom_known_ = bottom_known;
}
void ChangeTopOffset(double doffset);
void ChangeBottomOffset(double doffset);
double uncapped_average_voltage() const { return uncapped_average_voltage_; }
private:
double uncapped_average_voltage_;
bool is_zeroing_;
bool top_known_ = false, bottom_known_ = false;
const ::frc971::controls::HPolytope<2> U_Poly_, U_Poly_zeroing_;
};
class ClawMotor;
// 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:
ZeroedStateFeedbackLoop(const char *name, ClawMotor *motor);
const static int kZeroingMaxVoltage = 5;
enum JointZeroingState {
// We don't know where the joint is at all.
UNKNOWN_POSITION,
// We have an approximate position for where the claw is using.
APPROXIMATE_CALIBRATION,
// We observed the calibration edge while disabled. This is good enough for
// autonomous mode.
DISABLED_CALIBRATION,
// Ready for use during teleop.
CALIBRATED
};
void set_zeroing_state(JointZeroingState zeroing_state) {
zeroing_state_ = zeroing_state;
}
JointZeroingState zeroing_state() const { return zeroing_state_; }
void SetPositionValues(const HalfClawPosition *claw);
void Reset(const HalfClawPosition *claw);
double absolute_position() const { return encoder() + offset(); }
const ::frc971::HallEffectTracker &front() const { return front_; }
const ::frc971::HallEffectTracker &calibration() const {
return calibration_;
}
const ::frc971::HallEffectTracker &back() const { return back_; }
bool any_hall_effect_changed() const {
return front().either_count_changed() ||
calibration().either_count_changed() ||
back().either_count_changed();
}
bool front_or_back_triggered() const {
return front().value() || back().value();
}
bool any_triggered() const {
return calibration().value() || front().value() || back().value();
}
double encoder() const { return encoder_; }
double last_encoder() const { return last_encoder_; }
double offset() const { return offset_; }
// 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::Claws::Claw &claw,
double *edge_encoder, double *edge_angle);
bool SawFilteredPosedge(const ::frc971::HallEffectTracker &this_sensor,
const ::frc971::HallEffectTracker &sensorA,
const ::frc971::HallEffectTracker &sensorB);
bool SawFilteredNegedge(const ::frc971::HallEffectTracker &this_sensor,
const ::frc971::HallEffectTracker &sensorA,
const ::frc971::HallEffectTracker &sensorB);
#undef COUNT_SETTER_GETTER
protected:
// The accumulated voltage to apply to the motor.
double offset_;
const char *name_;
ClawMotor *motor_;
::frc971::HallEffectTracker front_, calibration_, back_;
JointZeroingState zeroing_state_;
double min_hall_effect_on_angle_;
double max_hall_effect_on_angle_;
double min_hall_effect_off_angle_;
double max_hall_effect_off_angle_;
double encoder_;
double last_encoder_;
double last_on_encoder_;
double last_off_encoder_;
bool any_triggered_last_;
const ::frc971::HallEffectTracker *posedge_filter_ = nullptr;
const ::frc971::HallEffectTracker *negedge_filter_ = nullptr;
private:
// Does the edges of 1 sensor for GetPositionOfEdge.
bool DoGetPositionOfEdge(const constants::Values::Claws::AnglePair &angles,
double *edge_encoder, double *edge_angle,
const ::frc971::HallEffectTracker &sensor,
const ::frc971::HallEffectTracker &sensorA,
const ::frc971::HallEffectTracker &sensorB,
const char *hall_effect_name);
};
class TopZeroedStateFeedbackLoop : public ZeroedStateFeedbackLoop {
public:
TopZeroedStateFeedbackLoop(ClawMotor *motor)
: ZeroedStateFeedbackLoop("top", motor) {}
// Sets the calibration offset given the absolute angle and the corresponding
// encoder value.
void SetCalibration(double edge_encoder, double edge_angle);
bool SetCalibrationOnEdge(const constants::Values::Claws::Claw &claw_values,
JointZeroingState zeroing_state);
double ComputeCalibrationChange(double edge_encoder, double edge_angle);
void HandleCalibrationError(
const constants::Values::Claws::Claw &claw_values);
};
class BottomZeroedStateFeedbackLoop : public ZeroedStateFeedbackLoop {
public:
BottomZeroedStateFeedbackLoop(ClawMotor *motor)
: ZeroedStateFeedbackLoop("bottom", motor) {}
// Sets the calibration offset given the absolute angle and the corrisponding
// encoder value.
void SetCalibration(double edge_encoder, double edge_angle);
bool SetCalibrationOnEdge(const constants::Values::Claws::Claw &claw_values,
JointZeroingState zeroing_state);
double ComputeCalibrationChange(double edge_encoder, double edge_angle);
void HandleCalibrationError(
const constants::Values::Claws::Claw &claw_values);
};
class ClawMotor
: public frc971::controls::ControlLoop<Goal, Position, Status, Output> {
public:
explicit ClawMotor(::aos::EventLoop *event_loop,
const ::std::string &name = "/claw");
// True if the state machine is ready.
bool capped_goal() const { return capped_goal_; }
double uncapped_average_voltage() const {
return claw_.uncapped_average_voltage();
}
// True if the claw is zeroing.
bool is_zeroing() const;
// True if the state machine is ready.
bool is_ready() const;
void ChangeTopOffset(double doffset);
void ChangeBottomOffset(double doffset);
enum CalibrationMode {
READY,
PREP_FINE_TUNE_TOP,
FINE_TUNE_TOP,
PREP_FINE_TUNE_BOTTOM,
FINE_TUNE_BOTTOM,
UNKNOWN_LOCATION
};
CalibrationMode mode() const { return mode_; }
protected:
virtual void RunIteration(const Goal *goal, const Position *position,
aos::Sender<Output>::Builder *output,
aos::Sender<Status>::Builder *status);
double top_absolute_position() const {
return claw_.X_hat(1, 0) + claw_.X_hat(0, 0);
}
double bottom_absolute_position() const { return claw_.X_hat(0, 0); }
private:
// Friend the test classes for acces to the internal state.
friend class testing::WindupClawTest;
// The zeroed joint to use.
bool has_top_claw_goal_;
double top_claw_goal_;
TopZeroedStateFeedbackLoop top_claw_;
bool has_bottom_claw_goal_;
double bottom_claw_goal_;
BottomZeroedStateFeedbackLoop bottom_claw_;
// The claw loop.
ClawLimitedLoop claw_;
bool was_enabled_;
bool doing_calibration_fine_tune_;
// The initial separation when disabled. Used as the safe separation
// distance.
double initial_separation_;
bool capped_goal_;
CalibrationMode mode_;
DISALLOW_COPY_AND_ASSIGN(ClawMotor);
};
// Modifies the bottom and top goal such that they are within the limits and
// their separation isn't too much or little.
void LimitClawGoal(double *bottom_goal, double *top_goal,
const constants::Values &values);
} // namespace claw
} // namespace control_loops
} // namespace y2014
#endif // Y2014_CONTROL_LOOPS_CLAW_CLAW_H_