package me.drton.flightplot.processors;
import me.drton.flightplot.processors.tools.LowPassFilter;
import me.drton.flightplot.processors.tools.PID;
import me.drton.jmavlib.conversion.RotationConversion;
import me.drton.jmavlib.processing.Batterworth2pLPF;
import me.drton.jmavlib.processing.DelayLine;
import me.drton.jmavlib.processing.Filter;
import java.util.HashMap;
import java.util.Map;
/**
* User: ton Date: 20.06.13 Time: 6:06
*/
public class PosRatePIDControlSimulator extends PlotProcessor {
private double timeStep;
private double thrustK;
private double accScale;
private double drag;
private DelayLine<Double> delayLine = new DelayLine<Double>();
private Filter rateLPF = new Batterworth2pLPF();
private LowPassFilter controlLPF = new LowPassFilter();
private PID pidPos = new PID();
private PID pidRate = new PID();
private double pos;
private double rate;
private double posSP;
private boolean useRateSP;
private double rateSP;
private double rateFF;
private double rateCtl;
private double timePrev;
private String spField;
private String rateSpField;
@Override
public Map<String, Object> getDefaultParameters() {
Map<String, Object> params = new HashMap<String, Object>();
params.put("Time Step", 0.004);
params.put("Thrust T", 0.03);
params.put("Thrust Delay", 0.05);
params.put("Thrust K", 200.0);
params.put("Ctrl P", 5.0);
params.put("Ctrl D", 0.0);
params.put("Ctrl Limit", 0.0);
params.put("Ctrl Rate P", 0.1);
params.put("Ctrl Rate I", 0.0);
params.put("Ctrl Rate D", 0.0);
params.put("Ctrl Rate D SP", false);
params.put("Ctrl Rate Limit", 0.0);
params.put("Ctrl Rate FF", 0.0);
params.put("Acc Scale", 1.0);
params.put("Drag", 0.0);
params.put("Use Rate SP", false);
params.put("Rate LPF", 0.0);
params.put("Field SP", "");
params.put("Field Rate SP", "");
return params;
}
@Override
public void init() {
pos = 0.0;
rate = 0.0;
posSP = 0.0;
rateSP = 0.0;
rateCtl = 0.0;
timePrev = -1.0;
timeStep = (Double) parameters.get("Time Step");
thrustK = (Double) parameters.get("Thrust K");
accScale = (Double) parameters.get("Acc Scale");
drag = (Double) parameters.get("Drag");
useRateSP = (Boolean) parameters.get("Use Rate SP");
delayLine.reset();
delayLine.setDelay((Double) parameters.get("Thrust Delay"));
controlLPF.reset();
controlLPF.setT((Double) parameters.get("Thrust T"));
pidPos.reset();
pidPos.setK((Double) parameters.get("Ctrl P"), 0.0, (Double) parameters.get("Ctrl D"),
(Double) parameters.get("Ctrl Limit"), PID.MODE.DERIVATIVE_SET);
pidRate.reset();
PID.MODE pidRateMode = (Boolean) parameters.get(
"Ctrl Rate D SP") ? PID.MODE.DERIVATIVE_CALC : PID.MODE.DERIVATIVE_CALC_NO_SP;
pidRate.setK((Double) parameters.get("Ctrl Rate P"), (Double) parameters.get("Ctrl Rate I"),
(Double) parameters.get("Ctrl Rate D"), (Double) parameters.get("Ctrl Rate Limit"), pidRateMode);
rateFF = (Double) parameters.get("Ctrl Rate FF");
rateLPF.setCutoffFreqFactor(((Double) parameters.get("Rate LPF")) * timeStep);
spField = (String) parameters.get("Field SP");
rateSpField = (String) parameters.get("Field Rate SP");
addSeries("Rate");
addSeries("Ctrl");
addSeries("Acc");
if (useRateSP) {
addSeries("Rate SP");
} else {
addSeries("Pos SP");
addSeries("Pos");
}
}
@Override
public void process(double time, Map<String, Object> update) {
if (timePrev < 0.0) {
timePrev = time;
return;
}
while (time > timePrev + timeStep) {
timePrev += timeStep;
updateSimulation(timePrev, timeStep);
}
updateSP(update);
}
private void updateSP(Map<String, Object> update) {
Number rateSPNum = (Number) update.get(rateSpField);
if (useRateSP) {
if (rateSPNum != null) {
rateSP = rateSPNum.doubleValue();
}
} else {
if (rateSPNum != null) {
rateCtl = rateSPNum.doubleValue();
}
String[] p = spField.split(" ");
if (p.length > 1) {
int axis = "RPY".indexOf(p[0]);
if (axis >= 0 && axis < 3) {
double[] q = new double[4];
for (int i = 0; i < 4; i++) {
Number v = (Number) update.get(p[i + 1]);
if (v == null) {
return;
}
q[i] = v.doubleValue();
}
double[] euler = RotationConversion.eulerAnglesByQuaternion(q);
posSP = euler[axis];
}
} else {
Number v = (Number) update.get(spField);
if (v != null) {
posSP = v.doubleValue();
}
}
}
}
private void updateSimulation(double time, double dt) {
Double force = delayLine.getOutput(time, controlLPF.getOutput(time, 0.0));
if (force == null) {
force = 0.0;
}
double acc = force * thrustK - drag * Math.abs(rate) * rate;
rate += acc * dt;
pos += rate * dt;
double rateFiltered = rateLPF.apply(rate);
if (!useRateSP) {
rateSP = pidPos.getOutput(posSP - pos, - rateFiltered, dt) + rateFF * rateCtl;
}
double control = pidRate.getOutput(rateSP, rateFiltered, 0.0, dt, 1.0);
controlLPF.setInput(control);
addPoint(0, time, rate);
addPoint(1, time, control);
if (accScale != 0.0) {
addPoint(2, time, acc * accScale);
}
if (useRateSP) {
addPoint(3, time, rateSP);
} else {
addPoint(3, time, posSP);
addPoint(4, time, pos);
}
}
}