package org.cowboycoders.turbotrainers.bushido.brake; import org.cowboycoders.pid.GainController; import org.cowboycoders.pid.GainParameters; import org.cowboycoders.pid.OutputControlParameters; import org.cowboycoders.pid.OutputController; import org.cowboycoders.pid.PidController; import org.cowboycoders.pid.ProcessVariableProvider; import org.cowboycoders.turbotrainers.Mode; import org.fluxoid.utils.SimpleCsvLogger; import java.io.File; public class PowerPidBrakeController extends AbstractController { private static String TAG = PowerPidBrakeController.class.getSimpleName(); private static final Mode SUPPORTED_MODE = Mode.TARGET_POWER; private static final double PID_PROPORTIONAL_GAIN = 10.0; private static final double PID_INTEGRAL_GAIN = 4.0; private static final double PID_DERIVATIVE_GAIN = 1.0; private static final double STARTING_RESISTANCE_PERCENT = 0.0; // TODO(doug): Make this configurable private static final double POWER_THRESHOLD_W = 50; private static final double TINY_POWER_W = 1; // Choose a low default so the resistance stays low until this is updated externally. private double targetPowerW = POWER_THRESHOLD_W; // This is accessed concurrently private volatile double actualPower = -1.0; private OutputController resistanceOutputController = new OutputController() { @Override public void setOutput(double resistance) { log("Setting trainer resistance in % to: " + resistance); BrakeModel bushidoDataModel = getDataModel(); logToCsv(ABSOLUTE_RESISTANCE_HEADING, bushidoDataModel.getAbsoluteResistance()); bushidoDataModel.setResistance(resistance); } @Override public double getMaxOutput() { return BrakeModel.RESISTANCE_MAX; } @Override public double getMinOutput() { return BrakeModel.RESISTANCE_MIN; } }; private ProcessVariableProvider powerProvider = new ProcessVariableProvider() { @Override public double getProcessVariable() { // Using normalised power return (targetPowerW < TINY_POWER_W) ? 0.0 : actualPower / targetPowerW; } }; private GainController resistanceGainController = new GainController() { @Override public GainParameters getGain(OutputControlParameters parameters) { GainParameters defaultParameters = new GainParameters( PID_PROPORTIONAL_GAIN, PID_INTEGRAL_GAIN, PID_DERIVATIVE_GAIN); return defaultParameters; } }; private PidController pidController = new PidController( powerProvider, resistanceOutputController, resistanceGainController); protected void setActualPower(double power) { logToCsv(POWER_HEADING, power); log("setActualPower: " + power); actualPower = power; } public void setTargetPower(double power) { log("setTargetPower: " + power); targetPowerW = power; } @Override public double onPowerChange(double power) { setActualPower(power); if (power <= POWER_THRESHOLD_W) { log("Power is less than the threshold, resetting PID controller."); System.out.println(); pidController.reset(); } log("Triggering update of PID controller."); // Using normalised power, so this is always 1. pidController.adjustSetpoint(1.0); return power; } @Override public Mode getMode() { return SUPPORTED_MODE; } @Override public void onStart() { BrakeModel bushidoDataModel = getDataModel(); bushidoDataModel.setResistance(STARTING_RESISTANCE_PERCENT); } protected void log(String msg) { System.out.println(TAG + ": " + msg); } @Override protected SimpleCsvLogger getCsvLogger(File file) { SimpleCsvLogger logger = new SimpleCsvLogger(file, POWER_HEADING, ABSOLUTE_RESISTANCE_HEADING); logger.addTime(true); logger.append(true); return logger; } @Override public void onStop() { } }