blob: 20b30b6091de8a8af374b6c6709c4c14f202a76c [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_CLAW_CLAW_H_
#define FRC971_CONTROL_LOOPS_CLAW_CLAW_H_
#include <memory>
#include "aos/common/control_loop/ControlLoop.h"
#include "frc971/constants.h"
#include "frc971/control_loops/state_feedback_loop.h"
#include "frc971/control_loops/claw/claw.q.h"
#include "frc971/control_loops/claw/claw_motor_plant.h"
namespace frc971 {
namespace control_loops {
namespace testing {
class ClawTest_NoWindupPositive_Test;
class ClawTest_NoWindupNegative_Test;
};
// 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)
: StateFeedbackLoop<4, 2, 2>(loop),
uncapped_average_voltage_(0.0),
is_zeroing_(true) {}
virtual void CapU();
void set_is_zeroing(bool is_zeroing) { is_zeroing_ = is_zeroing; }
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_;
};
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)
: offset_(0.0),
name_(name),
motor_(motor),
front_hall_effect_posedge_count_(0.0),
previous_front_hall_effect_posedge_count_(0.0),
front_hall_effect_negedge_count_(0.0),
previous_front_hall_effect_negedge_count_(0.0),
calibration_hall_effect_posedge_count_(0.0),
previous_calibration_hall_effect_posedge_count_(0.0),
calibration_hall_effect_negedge_count_(0.0),
previous_calibration_hall_effect_negedge_count_(0.0),
back_hall_effect_posedge_count_(0.0),
previous_back_hall_effect_posedge_count_(0.0),
back_hall_effect_negedge_count_(0.0),
previous_back_hall_effect_negedge_count_(0.0),
front_hall_effect_(false),
calibration_hall_effect_(false),
back_hall_effect_(false),
zeroing_state_(UNKNOWN_POSITION),
posedge_value_(0.0),
negedge_value_(0.0),
encoder_(0.0),
last_encoder_(0.0) {}
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) {
set_front_hall_effect_posedge_count(claw.front_hall_effect_posedge_count);
set_front_hall_effect_negedge_count(claw.front_hall_effect_negedge_count);
set_calibration_hall_effect_posedge_count(
claw.calibration_hall_effect_posedge_count);
set_calibration_hall_effect_negedge_count(
claw.calibration_hall_effect_negedge_count);
set_back_hall_effect_posedge_count(claw.back_hall_effect_posedge_count);
set_back_hall_effect_negedge_count(claw.back_hall_effect_negedge_count);
posedge_value_ = claw.posedge_value;
negedge_value_ = claw.negedge_value;
last_encoder_ = encoder_;
encoder_ = claw.position;
front_hall_effect_ = claw.front_hall_effect;
calibration_hall_effect_ = claw.calibration_hall_effect;
back_hall_effect_ = claw.back_hall_effect;
}
double absolute_position() const { return encoder() + offset(); }
bool front_hall_effect() const { return front_hall_effect_; }
bool calibration_hall_effect() const { return calibration_hall_effect_; }
bool back_hall_effect() const { return back_hall_effect_; }
#define COUNT_SETTER_GETTER(variable) \
void set_##variable(int32_t value) { \
previous_##variable##_ = variable##_; \
variable##_ = value; \
} \
int32_t variable() const { return variable##_; } \
bool variable##_changed() const { \
return previous_##variable##_ != variable##_; \
}
// TODO(austin): Pull all this out of the new sub structure.
COUNT_SETTER_GETTER(front_hall_effect_posedge_count);
COUNT_SETTER_GETTER(front_hall_effect_negedge_count);
COUNT_SETTER_GETTER(calibration_hall_effect_posedge_count);
COUNT_SETTER_GETTER(calibration_hall_effect_negedge_count);
COUNT_SETTER_GETTER(back_hall_effect_posedge_count);
COUNT_SETTER_GETTER(back_hall_effect_negedge_count);
bool any_hall_effect_changed() const {
return front_hall_effect_posedge_count_changed() ||
front_hall_effect_negedge_count_changed() ||
calibration_hall_effect_posedge_count_changed() ||
calibration_hall_effect_negedge_count_changed() ||
back_hall_effect_posedge_count_changed() ||
back_hall_effect_negedge_count_changed();
}
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);
#undef COUNT_SETTER_GETTER
protected:
// The accumulated voltage to apply to the motor.
double offset_;
const char *name_;
ClawMotor *motor_;
int32_t front_hall_effect_posedge_count_;
int32_t previous_front_hall_effect_posedge_count_;
int32_t front_hall_effect_negedge_count_;
int32_t previous_front_hall_effect_negedge_count_;
int32_t calibration_hall_effect_posedge_count_;
int32_t previous_calibration_hall_effect_posedge_count_;
int32_t calibration_hall_effect_negedge_count_;
int32_t previous_calibration_hall_effect_negedge_count_;
int32_t back_hall_effect_posedge_count_;
int32_t previous_back_hall_effect_posedge_count_;
int32_t back_hall_effect_negedge_count_;
int32_t previous_back_hall_effect_negedge_count_;
bool front_hall_effect_;
bool calibration_hall_effect_;
bool back_hall_effect_;
JointZeroingState zeroing_state_;
double posedge_value_;
double negedge_value_;
double encoder_;
double last_encoder_;
};
class TopZeroedStateFeedbackLoop : public ZeroedStateFeedbackLoop {
public:
TopZeroedStateFeedbackLoop(ClawMotor *motor)
: ZeroedStateFeedbackLoop("top", 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 edge_encoder;
double edge_angle;
if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
LOG(INFO, "Calibration edge.\n");
SetCalibration(edge_encoder, edge_angle);
set_zeroing_state(zeroing_state);
return true;
}
return false;
}
};
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 edge_encoder;
double edge_angle;
if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
LOG(INFO, "Calibration edge.\n");
SetCalibration(edge_encoder, edge_angle);
set_zeroing_state(zeroing_state);
return true;
}
return false;
}
};
class ClawMotor
: public aos::control_loops::ControlLoop<control_loops::ClawGroup> {
public:
explicit ClawMotor(control_loops::ClawGroup *my_claw =
&control_loops::claw_queue_group);
// True if the state machine is ready.
bool capped_goal() const { return capped_goal_; }
// True if the state machine is ready.
bool is_ready() const { return false; }
void ChangeTopOffset(double doffset);
void ChangeBottomOffset(double doffset);
protected:
virtual void RunIteration(const control_loops::ClawGroup::Goal *goal,
const control_loops::ClawGroup::Position *position,
control_loops::ClawGroup::Output *output,
::aos::control_loops::Status *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::ClawTest_NoWindupPositive_Test;
friend class testing::ClawTest_NoWindupNegative_Test;
// 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 seperation when disabled. Used as the safe seperation
// distance.
double initial_seperation_;
bool capped_goal_;
DISALLOW_COPY_AND_ASSIGN(ClawMotor);
};
} // namespace control_loops
} // namespace frc971
#endif // FRC971_CONTROL_LOOPS_CLAW_CLAW_H_