BulletSim: reorganize motor code a little to pull together common functions.

Add BSFMotor.
user_profiles
Robert Adams 2013-01-03 19:37:01 -08:00
parent 44492b3a49
commit 5432180027
1 changed files with 129 additions and 18 deletions

View File

@ -58,14 +58,11 @@ public abstract class BSMotor
protected void MDetailLog(string msg, params Object[] parms) protected void MDetailLog(string msg, params Object[] parms)
{ {
if (PhysicsScene != null) if (PhysicsScene != null)
{
if (PhysicsScene.VehicleLoggingEnabled)
{ {
PhysicsScene.DetailLog(msg, parms); PhysicsScene.DetailLog(msg, parms);
} }
} }
} }
}
// Motor which moves CurrentValue to TargetValue over TimeScale seconds. // Motor which moves CurrentValue to TargetValue over TimeScale seconds.
// The TargetValue decays in TargetValueDecayTimeScale and // The TargetValue decays in TargetValueDecayTimeScale and
@ -100,10 +97,13 @@ public class BSVMotor : BSMotor
public virtual Vector3 CurrentValue { get; protected set; } public virtual Vector3 CurrentValue { get; protected set; }
public virtual Vector3 LastError { get; protected set; } public virtual Vector3 LastError { get; protected set; }
public virtual bool ErrorIsZero public virtual bool ErrorIsZero()
{ get { {
return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold); return ErrorIsZero(LastError);
} }
public virtual bool ErrorIsZero(Vector3 err)
{
return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold));
} }
public BSVMotor(string useName) public BSVMotor(string useName)
@ -148,7 +148,7 @@ public class BSVMotor : BSMotor
Vector3 correction = Vector3.Zero; Vector3 correction = Vector3.Zero;
Vector3 error = TargetValue - CurrentValue; Vector3 error = TargetValue - CurrentValue;
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) if (!ErrorIsZero(error))
{ {
correction = Step(timeStep, error); correction = Step(timeStep, error);
@ -200,7 +200,7 @@ public class BSVMotor : BSMotor
LastError = error; LastError = error;
Vector3 returnCorrection = Vector3.Zero; Vector3 returnCorrection = Vector3.Zero;
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) if (!ErrorIsZero())
{ {
// correction = error / secondsItShouldTakeToCorrect // correction = error / secondsItShouldTakeToCorrect
Vector3 correctionAmount; Vector3 correctionAmount;
@ -246,32 +246,139 @@ public class BSVMotor : BSMotor
} }
} }
// ============================================================================
// ============================================================================
public class BSFMotor : BSMotor public class BSFMotor : BSMotor
{ {
public float TimeScale { get; set; } public virtual float TimeScale { get; set; }
public float DecayTimeScale { get; set; } public virtual float TargetValueDecayTimeScale { get; set; }
public float Friction { get; set; } public virtual float FrictionTimescale { get; set; }
public float Efficiency { get; set; } public virtual float Efficiency { get; set; }
public float Target { get; private set; } public virtual float ErrorZeroThreshold { get; set; }
public float CurrentValue { get; private set; }
public virtual float TargetValue { get; protected set; }
public virtual float CurrentValue { get; protected set; }
public virtual float LastError { get; protected set; }
public virtual bool ErrorIsZero()
{
return ErrorIsZero(LastError);
}
public virtual bool ErrorIsZero(float err)
{
return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold);
}
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency) public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
: base(useName) : base(useName)
{ {
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
Efficiency = 1f;
FrictionTimescale = BSMotor.Infinite;
CurrentValue = TargetValue = 0f;
ErrorZeroThreshold = 0.01f;
} }
public void SetCurrent(float target) public void SetCurrent(float current)
{ {
CurrentValue = current;
} }
public void SetTarget(float target) public void SetTarget(float target)
{ {
TargetValue = target;
} }
public virtual float Step(float timeStep) public override void Zero()
{ {
return 0f; base.Zero();
} CurrentValue = TargetValue = 0f;
} }
public virtual float Step(float timeStep)
{
if (!Enabled) return TargetValue;
float origTarget = TargetValue; // DEBUG
float origCurrVal = CurrentValue; // DEBUG
float correction = 0f;
float error = TargetValue - CurrentValue;
if (!ErrorIsZero(error))
{
correction = Step(timeStep, error);
CurrentValue += correction;
// The desired value reduces to zero which also reduces the difference with current.
// If the decay time is infinite, don't decay at all.
float decayFactor = 0f;
if (TargetValueDecayTimeScale != BSMotor.Infinite)
{
decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
TargetValue *= (1f - decayFactor);
}
// The amount we can correct the error is reduced by the friction
float frictionFactor = 0f;
if (FrictionTimescale != BSMotor.Infinite)
{
// frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
// Individual friction components can be 'infinite' so compute each separately.
frictionFactor = 1f / FrictionTimescale;
frictionFactor *= timeStep;
CurrentValue *= (1f - frictionFactor);
}
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
timeStep, error, correction);
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
BSScene.DetailLogZero, UseName,
TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
TargetValue, CurrentValue);
}
else
{
// Difference between what we have and target is small. Motor is done.
CurrentValue = TargetValue;
MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
}
return CurrentValue;
}
public virtual float Step(float timeStep, float error)
{
if (!Enabled) return 0f;
LastError = error;
float returnCorrection = 0f;
if (!ErrorIsZero())
{
// correction = error / secondsItShouldTakeToCorrect
float correctionAmount;
if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
correctionAmount = error * timeStep;
else
correctionAmount = error / TimeScale * timeStep;
returnCorrection = correctionAmount;
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
}
return returnCorrection;
}
public override string ToString()
{
return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4},frictTS={5}>",
UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale, FrictionTimescale);
}
}
// ============================================================================
// ============================================================================
// Proportional, Integral, Derivitive Motor // Proportional, Integral, Derivitive Motor
// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors. // Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
public class BSPIDVMotor : BSVMotor public class BSPIDVMotor : BSVMotor
@ -319,6 +426,7 @@ public class BSPIDVMotor : BSVMotor
proportionFactor = new Vector3(factor, factor, factor); proportionFactor = new Vector3(factor, factor, factor);
integralFactor = new Vector3(factor, factor, factor); integralFactor = new Vector3(factor, factor, factor);
derivFactor = new Vector3(factor, factor, factor); derivFactor = new Vector3(factor, factor, factor);
MDetailLog("{0},BSPIDVMotor.setEfficiency,eff={1},factor={2}", BSScene.DetailLogZero, Efficiency, factor);
} }
} }
@ -341,6 +449,9 @@ public class BSPIDVMotor : BSVMotor
+ derivFactor * derivFactor + derivFactor * derivFactor
); );
MDetailLog("{0},BSPIDVMotor.step,ts={1},err={2},runnInt={3},derivFact={4},ret={5}",
BSScene.DetailLogZero, timeStep, error, RunningIntegration, derivFactor, ret);
return ret; return ret;
} }
} }