Claw gets further when zeroing.
- Claw now roughly finds out where it is.
- The state feedback loop now has a Correct method which takes the
measurement to use.
diff --git a/frc971/control_loops/claw/claw.cc b/frc971/control_loops/claw/claw.cc
index fb16c51..8c35733 100644
--- a/frc971/control_loops/claw/claw.cc
+++ b/frc971/control_loops/claw/claw.cc
@@ -52,7 +52,7 @@
uncapped_voltage_ = voltage_;
- double limit = zeroing_state_ != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
+ double limit = zeroing_state() != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
// Make sure that reality and the observer can't get too far off. There is a
// delay by one cycle between the applied voltage and X_hat(2, 0), so compare
@@ -68,8 +68,8 @@
voltage_ = std::min(limit, voltage_);
voltage_ = std::max(-limit, voltage_);
U(0, 0) = voltage_ - old_voltage;
- //LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
- //LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
+ LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
+ LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
last_voltage_ = voltage_;
}
@@ -86,11 +86,77 @@
const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
+bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
+ const constants::Values::Claw &claw_values, double *edge_encoder,
+ double *edge_angle) {
+
+ // TODO(austin): Validate that the hall effect edge makes sense.
+ // We must now be on the side of the edge that we expect to be, and the
+ // encoder must have been on either side of the edge before and after.
+
+ if (front_hall_effect_posedge_count_changed()) {
+ if (encoder() - last_encoder() < 0) {
+ *edge_angle = claw_values.front.upper_angle;
+ } else {
+ *edge_angle = claw_values.front.lower_angle;
+ }
+ *edge_encoder = posedge_value_;
+ return true;
+ }
+ if (front_hall_effect_negedge_count_changed()) {
+ if (encoder() - last_encoder() > 0) {
+ *edge_angle = claw_values.front.upper_angle;
+ } else {
+ *edge_angle = claw_values.front.lower_angle;
+ }
+ *edge_encoder = negedge_value_;
+ return true;
+ }
+ if (calibration_hall_effect_posedge_count_changed()) {
+ if (encoder() - last_encoder() < 0) {
+ *edge_angle = claw_values.calibration.upper_angle;
+ } else {
+ *edge_angle = claw_values.calibration.lower_angle;
+ }
+ *edge_encoder = posedge_value_;
+ return true;
+ }
+ if (calibration_hall_effect_negedge_count_changed()) {
+ if (encoder() - last_encoder() > 0) {
+ *edge_angle = claw_values.calibration.upper_angle;
+ } else {
+ *edge_angle = claw_values.calibration.lower_angle;
+ }
+ *edge_encoder = negedge_value_;
+ return true;
+ }
+ if (back_hall_effect_posedge_count_changed()) {
+ if (encoder() - last_encoder() < 0) {
+ *edge_angle = claw_values.back.upper_angle;
+ } else {
+ *edge_angle = claw_values.back.lower_angle;
+ }
+ *edge_encoder = posedge_value_;
+ return true;
+ }
+ if (back_hall_effect_negedge_count_changed()) {
+ if (encoder() - last_encoder() > 0) {
+ *edge_angle = claw_values.back.upper_angle;
+ } else {
+ *edge_angle = claw_values.back.lower_angle;
+ }
+ *edge_encoder = negedge_value_;
+ return true;
+ }
+ return false;
+}
+
// Positive angle is up, and positive power is up.
void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
const control_loops::ClawGroup::Position *position,
control_loops::ClawGroup::Output *output,
::aos::control_loops::Status *status) {
+ constexpr double dt = 0.01;
// Disable the motors now so that all early returns will return with the
// motors disabled.
@@ -114,7 +180,12 @@
return;
}
+ const frc971::constants::Values &values = constants::GetValues();
+
if (position) {
+ top_claw_.SetPositionValues(position->top);
+ bottom_claw_.SetPositionValues(position->bottom);
+
if (!has_top_claw_goal_) {
has_top_claw_goal_ = true;
top_claw_goal_ = position->top.position;
@@ -123,35 +194,10 @@
has_bottom_claw_goal_ = true;
bottom_claw_goal_ = position->bottom.position;
}
-
- top_claw_.set_front_hall_effect_posedge_count(
- position->top.front_hall_effect_posedge_count);
- top_claw_.set_front_hall_effect_negedge_count(
- position->top.front_hall_effect_negedge_count);
- top_claw_.set_calibration_hall_effect_posedge_count(
- position->top.calibration_hall_effect_posedge_count);
- top_claw_.set_calibration_hall_effect_negedge_count(
- position->top.calibration_hall_effect_negedge_count);
- top_claw_.set_back_hall_effect_posedge_count(
- position->top.back_hall_effect_posedge_count);
- top_claw_.set_back_hall_effect_negedge_count(
- position->top.back_hall_effect_negedge_count);
-
- bottom_claw_.set_front_hall_effect_posedge_count(
- position->bottom.front_hall_effect_posedge_count);
- bottom_claw_.set_front_hall_effect_negedge_count(
- position->bottom.front_hall_effect_negedge_count);
- bottom_claw_.set_calibration_hall_effect_posedge_count(
- position->bottom.calibration_hall_effect_posedge_count);
- bottom_claw_.set_calibration_hall_effect_negedge_count(
- position->bottom.calibration_hall_effect_negedge_count);
- bottom_claw_.set_back_hall_effect_posedge_count(
- position->bottom.back_hall_effect_posedge_count);
- bottom_claw_.set_back_hall_effect_negedge_count(
- position->bottom.back_hall_effect_negedge_count);
}
bool autonomous = ::aos::robot_state->autonomous;
+ bool enabled = ::aos::robot_state->enabled;
if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
@@ -164,6 +210,8 @@
ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
// Ready to use the claw.
// Limit the goals here.
+ bottom_claw_goal_ = goal->bottom_angle;
+ top_claw_goal_ = goal->bottom_angle + goal->seperation_angle;
} else if (top_claw_.zeroing_state() !=
ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
bottom_claw_.zeroing_state() !=
@@ -175,9 +223,18 @@
}
} else {
if (!was_enabled_ && enabled) {
-
+ if (position) {
+ top_claw_goal_ = position->top.position;
+ bottom_claw_goal_ = position->bottom.position;
+ } else {
+ has_top_claw_goal_ = false;
+ has_bottom_claw_goal_ = false;
+ }
}
- // Limit the goals here.
+
+ // TODO(austin): Limit the goals here.
+ // Need to prevent windup, limit voltage, deal with windup on only 1 claw,
+ // ...
if (top_claw_.zeroing_state() ==
ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
}
@@ -187,28 +244,49 @@
if (bottom_claw_.zeroing_state() !=
ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
- // Time to slowly move back up to find any position to narrow down the
- // zero.
+ if (enabled) {
+ // Time to slowly move back up to find any position to narrow down the
+ // zero.
+ top_claw_goal_ += values.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ += values.claw_zeroing_off_speed * dt;
+ // TODO(austin): Goal velocity too!
+ }
} else {
// We don't know where either claw is. Slowly start moving down to find
// any hall effect.
- LOG(INFO, "Unknown position\n");
+ if (enabled) {
+ top_claw_goal_-= values.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ -= values.claw_zeroing_off_speed * dt;
+ // TODO(austin): Goal velocity too!
+ }
+ }
+
+ if (enabled) {
+ top_claw_.SetCalibrationOnEdge(
+ values.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ bottom_claw_.SetCalibrationOnEdge(
+ values.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ } else {
+ top_claw_.SetCalibrationOnEdge(
+ values.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+ bottom_claw_.SetCalibrationOnEdge(
+ values.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
}
}
// TODO(austin): Handle disabled.
- if (position) {
- top_claw_.Y << position->top.position;
- bottom_claw_.Y << position->bottom.position;
- }
-
// TODO(austin): ...
- top_claw_.R << goal->bottom_angle + goal->seperation_angle, 0.0, 0.0;
- bottom_claw_.R << goal->bottom_angle, 0.0, 0.0;
+ if (has_top_claw_goal_ && has_bottom_claw_goal_) {
+ top_claw_.R << top_claw_goal_, 0.0, 0.0;
+ bottom_claw_.R << bottom_claw_goal_, 0.0, 0.0;
- top_claw_.Update(position != nullptr, output == nullptr);
- bottom_claw_.Update(position != nullptr, output == nullptr);
+ top_claw_.Update(output == nullptr);
+ bottom_claw_.Update(output == nullptr);
+ } else {
+ top_claw_.ZeroPower();
+ bottom_claw_.ZeroPower();
+ }
if (position) {
//LOG(DEBUG, "pos: %f hall: %s absolute: %f\n", position->top_position,
diff --git a/frc971/control_loops/claw/claw.h b/frc971/control_loops/claw/claw.h
index fc5e917..b585fbb 100644
--- a/frc971/control_loops/claw/claw.h
+++ b/frc971/control_loops/claw/claw.h
@@ -4,6 +4,7 @@
#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/top_claw_motor_plant.h"
@@ -32,6 +33,7 @@
voltage_(0.0),
last_voltage_(0.0),
uncapped_voltage_(0.0),
+ offset_(0.0),
front_hall_effect_posedge_count_(0.0),
previous_front_hall_effect_posedge_count_(0.0),
front_hall_effect_negedge_count_(0.0),
@@ -44,7 +46,11 @@
previous_back_hall_effect_posedge_count_(0.0),
back_hall_effect_negedge_count_(0.0),
previous_back_hall_effect_negedge_count_(0.0),
- zeroing_state_(UNKNOWN_POSITION) {}
+ zeroing_state_(UNKNOWN_POSITION),
+ posedge_value_(0.0),
+ negedge_value_(0.0),
+ encoder_(0.0),
+ last_encoder_(0.0) {}
const static int kZeroingMaxVoltage = 5;
@@ -77,6 +83,42 @@
}
JointZeroingState zeroing_state() const { return zeroing_state_; }
+ // Sets the calibration offset given the absolute angle and the corrisponding
+ // encoder value.
+ void SetCalibration(double edge_encoder, double edge_angle) {
+ offset_ = edge_angle - edge_encoder;
+ }
+
+ bool SetCalibrationOnEdge(const constants::Values::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;
+ }
+
+ void SetPositionValues(const Claw &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;
+ Eigen::Matrix<double, 1, 1> Y;
+ Y << claw.position;
+ Correct(Y);
+ }
+
#define COUNT_SETTER_GETTER(variable) \
void set_##variable(int32_t value) { \
previous_##variable##_ = variable##_; \
@@ -87,6 +129,7 @@
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);
@@ -103,6 +146,15 @@
back_hall_effect_negedge_count_changed();
}
+ 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::Claw &claw,
+ double *edge_encoder, double *edge_angle);
+
#undef COUNT_SETTER_GETTER
private:
@@ -110,6 +162,9 @@
double voltage_;
double last_voltage_;
double uncapped_voltage_;
+ double offset_;
+
+ double previous_position_;
int32_t front_hall_effect_posedge_count_;
int32_t previous_front_hall_effect_posedge_count_;
@@ -125,6 +180,10 @@
int32_t previous_back_hall_effect_negedge_count_;
JointZeroingState zeroing_state_;
+ double posedge_value_;
+ double negedge_value_;
+ double encoder_;
+ double last_encoder_;
};
class ClawMotor
@@ -142,8 +201,8 @@
control_loops::ClawGroup::Output *output,
::aos::control_loops::Status *status);
- double top_absolute_position() const { return top_claw_.X_hat(0, 0); }
- double bottom_absolute_position() const { return bottom_claw_.X_hat(0, 0); }
+ double top_absolute_position() const { return top_claw_.position(); }
+ double bottom_absolute_position() const { return bottom_claw_.position(); }
private:
// Friend the test classes for acces to the internal state.
diff --git a/frc971/control_loops/claw/claw_lib_test.cc b/frc971/control_loops/claw/claw_lib_test.cc
index 9e8e097..30155f5 100644
--- a/frc971/control_loops/claw/claw_lib_test.cc
+++ b/frc971/control_loops/claw/claw_lib_test.cc
@@ -46,27 +46,14 @@
void Reinitialize(double initial_top_position,
double initial_bottom_position) {
+ LOG(INFO, "Reinitializing to {top: %f, bottom: %f}\n", initial_top_position,
+ initial_bottom_position);
ReinitializePartial(TOP_CLAW, initial_top_position);
ReinitializePartial(BOTTOM_CLAW, initial_bottom_position);
last_position_.Zero();
SetPhysicalSensors(&last_position_);
}
- // Resets the plant so that it starts at initial_position.
- void ReinitializePartial(ClawType type, double initial_position) {
- StateFeedbackPlant<2, 1, 1>* plant;
- if (type == TOP_CLAW) {
- plant = top_claw_plant_.get();
- } else {
- plant = bottom_claw_plant_.get();
- }
- initial_position_[type] = initial_position;
- plant->X(0, 0) = initial_position_[type];
- plant->X(1, 0) = 0.0;
- plant->Y = plant->C() * plant->X;
- last_voltage_[type] = 0.0;
- }
-
// Returns the absolute angle of the wrist.
double GetAbsolutePosition(ClawType type) const {
if (type == TOP_CLAW) {
@@ -94,6 +81,7 @@
double pos[2] = {GetAbsolutePosition(TOP_CLAW),
GetAbsolutePosition(BOTTOM_CLAW)};
+ LOG(DEBUG, "Physical claws are at {top: %f, bottom: %f}\n", pos[TOP_CLAW], pos[BOTTOM_CLAW]);
const frc971::constants::Values& values = constants::GetValues();
@@ -119,6 +107,9 @@
::aos::ScopedMessagePtr<control_loops::ClawGroup::Position> position =
claw_queue_group.position.MakeMessage();
+ // Initialize all the counters to their previous values.
+ *position = last_position_;
+
SetPhysicalSensors(position.get());
const frc971::constants::Values& values = constants::GetValues();
@@ -301,6 +292,21 @@
::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> bottom_claw_plant_;
private:
+ // Resets the plant so that it starts at initial_position.
+ void ReinitializePartial(ClawType type, double initial_position) {
+ StateFeedbackPlant<2, 1, 1>* plant;
+ if (type == TOP_CLAW) {
+ plant = top_claw_plant_.get();
+ } else {
+ plant = bottom_claw_plant_.get();
+ }
+ initial_position_[type] = initial_position;
+ plant->X(0, 0) = initial_position_[type];
+ plant->X(1, 0) = 0.0;
+ plant->Y = plant->C() * plant->X;
+ last_voltage_[type] = 0.0;
+ }
+
void Simulate(ClawType type, StateFeedbackPlant<2, 1, 1>* plant,
const constants::Values::Claw &claw, double nl_voltage) {
plant->U << last_voltage_[type];
@@ -346,7 +352,7 @@
".frc971.control_loops.claw_queue_group.output",
".frc971.control_loops.claw_queue_group.status"),
claw_motor_(&claw_queue_group),
- claw_motor_plant_(0.2, 0.4),
+ claw_motor_plant_(0.4, 0.2),
min_seperation_(constants::GetValues().claw_min_seperation) {
// Flush the robot state queue so we can use clean shared memory for this
// test.
@@ -394,7 +400,7 @@
// Tests that the wrist zeros correctly starting on the hall effect sensor.
TEST_F(ClawTest, ZerosStartingOn) {
- claw_motor_plant_.Reinitialize(90 * M_PI / 180.0, 100 * M_PI / 180.0);
+ claw_motor_plant_.Reinitialize(100 * M_PI / 180.0, 90 * M_PI / 180.0);
claw_queue_group.goal.MakeWithBuilder()
.bottom_angle(0.1)
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 4a05f7a..982c6e9 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -93,7 +93,9 @@
void SetRawPosition(double left, double right) {
_raw_right = right;
_raw_left = left;
- loop_->Y << left, right;
+ Eigen::Matrix<double, 2, 1> Y;
+ Y << left, right;
+ loop_->Correct(Y);
}
void SetPosition(
double left, double right, double gyro, bool control_loop_driving) {
@@ -108,8 +110,8 @@
SetRawPosition(left, right);
}
- void Update(bool update_observer, bool stop_motors) {
- loop_->Update(update_observer, stop_motors);
+ void Update(bool stop_motors) {
+ loop_->Update(stop_motors);
}
void SendMotors(Drivetrain::Output *output) {
@@ -606,7 +608,7 @@
}
}
dt_openloop.SetPosition(position);
- dt_closedloop.Update(position, output == NULL);
+ dt_closedloop.Update(output == NULL);
dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
dt_openloop.Update();
if (control_loop_driving) {
diff --git a/frc971/control_loops/state_feedback_loop.h b/frc971/control_loops/state_feedback_loop.h
index 38ca89c..420a0e7 100644
--- a/frc971/control_loops/state_feedback_loop.h
+++ b/frc971/control_loops/state_feedback_loop.h
@@ -218,7 +218,6 @@
Eigen::Matrix<double, number_of_inputs, 1> U;
Eigen::Matrix<double, number_of_inputs, 1> U_uncapped;
Eigen::Matrix<double, number_of_outputs, 1> U_ff;
- Eigen::Matrix<double, number_of_outputs, 1> Y;
const StateFeedbackController<number_of_states, number_of_inputs,
number_of_outputs> &controller() const {
@@ -237,7 +236,6 @@
U.setZero();
U_uncapped.setZero();
U_ff.setZero();
- Y.setZero();
}
StateFeedbackLoop(const StateFeedbackController<
@@ -286,9 +284,14 @@
}
}
- // update_observer is whether or not to use the values in Y.
+ // Corrects X_hat given the observation in Y.
+ void Correct(const Eigen::Matrix<double, number_of_outputs, 1> &Y) {
+ Y_ = Y;
+ new_y_ = true;
+ }
+
// stop_motors is whether or not to output all 0s.
- void Update(bool update_observer, bool stop_motors) {
+ void Update(bool stop_motors) {
if (stop_motors) {
U.setZero();
} else {
@@ -296,8 +299,9 @@
CapU();
}
- if (update_observer) {
- X_hat = (A() - L() * C()) * X_hat + L() * Y + B() * U;
+ if (new_y_) {
+ X_hat = (A() - L() * C()) * X_hat + L() * Y_ + B() * U;
+ new_y_ = false;
} else {
X_hat = A() * X_hat + B() * U;
}
@@ -326,6 +330,11 @@
static const int kNumOutputs = number_of_outputs;
static const int kNumInputs = number_of_inputs;
+ // Temporary storage for a measurement until I can figure out why I can't
+ // correct when the measurement is made.
+ Eigen::Matrix<double, number_of_outputs, 1> Y_;
+ bool new_y_ = false;
+
int controller_index_;
};