diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs | 193 |
1 files changed, 150 insertions, 43 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs index e0faf4e..34a87c6 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs | |||
@@ -29,13 +29,14 @@ using System; | |||
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Text; | 30 | using System.Text; |
31 | using OpenMetaverse; | 31 | using OpenMetaverse; |
32 | using OpenSim.Framework; | ||
32 | 33 | ||
33 | namespace OpenSim.Region.Physics.BulletSPlugin | 34 | namespace OpenSim.Region.Physics.BulletSPlugin |
34 | { | 35 | { |
35 | public abstract class BSMotor | 36 | public abstract class BSMotor |
36 | { | 37 | { |
37 | // Timescales and other things can be turned off by setting them to 'infinite'. | 38 | // Timescales and other things can be turned off by setting them to 'infinite'. |
38 | public const float Infinite = 12345f; | 39 | public const float Infinite = 12345.6f; |
39 | public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite); | 40 | public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite); |
40 | 41 | ||
41 | public BSMotor(string useName) | 42 | public BSMotor(string useName) |
@@ -45,6 +46,7 @@ public abstract class BSMotor | |||
45 | } | 46 | } |
46 | public virtual void Reset() { } | 47 | public virtual void Reset() { } |
47 | public virtual void Zero() { } | 48 | public virtual void Zero() { } |
49 | public virtual void GenerateTestOutput(float timeStep) { } | ||
48 | 50 | ||
49 | // A name passed at motor creation for easily identifyable debugging messages. | 51 | // A name passed at motor creation for easily identifyable debugging messages. |
50 | public string UseName { get; private set; } | 52 | public string UseName { get; private set; } |
@@ -62,12 +64,16 @@ public abstract class BSMotor | |||
62 | } | 64 | } |
63 | } | 65 | } |
64 | } | 66 | } |
65 | // Can all the incremental stepping be replaced with motor classes? | ||
66 | 67 | ||
67 | // Motor which moves CurrentValue to TargetValue over TimeScale seconds. | 68 | // Motor which moves CurrentValue to TargetValue over TimeScale seconds. |
68 | // The TargetValue decays in TargetValueDecayTimeScale and | 69 | // The TargetValue decays in TargetValueDecayTimeScale and |
69 | // the CurrentValue will be held back by FrictionTimeScale. | 70 | // the CurrentValue will be held back by FrictionTimeScale. |
70 | // TimeScale and TargetDelayTimeScale may be 'infinite' which means go decay. | 71 | // This motor will "zero itself" over time in that the targetValue will |
72 | // decay to zero and the currentValue will follow it to that zero. | ||
73 | // The overall effect is for the returned correction value to go from large | ||
74 | // values (the total difference between current and target minus friction) | ||
75 | // to small and eventually zero values. | ||
76 | // TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay. | ||
71 | 77 | ||
72 | // For instance, if something is moving at speed X and the desired speed is Y, | 78 | // For instance, if something is moving at speed X and the desired speed is Y, |
73 | // CurrentValue is X and TargetValue is Y. As the motor is stepped, new | 79 | // CurrentValue is X and TargetValue is Y. As the motor is stepped, new |
@@ -81,13 +87,16 @@ public class BSVMotor : BSMotor | |||
81 | // public Vector3 FrameOfReference { get; set; } | 87 | // public Vector3 FrameOfReference { get; set; } |
82 | // public Vector3 Offset { get; set; } | 88 | // public Vector3 Offset { get; set; } |
83 | 89 | ||
84 | public float TimeScale { get; set; } | 90 | public virtual float TimeScale { get; set; } |
85 | public float TargetValueDecayTimeScale { get; set; } | 91 | public virtual float TargetValueDecayTimeScale { get; set; } |
86 | public Vector3 FrictionTimescale { get; set; } | 92 | public virtual Vector3 FrictionTimescale { get; set; } |
87 | public float Efficiency { get; set; } | 93 | public virtual float Efficiency { get; set; } |
94 | |||
95 | public virtual float ErrorZeroThreshold { get; set; } | ||
88 | 96 | ||
89 | public Vector3 TargetValue { get; private set; } | 97 | public virtual Vector3 TargetValue { get; protected set; } |
90 | public Vector3 CurrentValue { get; private set; } | 98 | public virtual Vector3 CurrentValue { get; protected set; } |
99 | public virtual Vector3 LastError { get; protected set; } | ||
91 | 100 | ||
92 | public BSVMotor(string useName) | 101 | public BSVMotor(string useName) |
93 | : base(useName) | 102 | : base(useName) |
@@ -96,6 +105,7 @@ public class BSVMotor : BSMotor | |||
96 | Efficiency = 1f; | 105 | Efficiency = 1f; |
97 | FrictionTimescale = BSMotor.InfiniteVector; | 106 | FrictionTimescale = BSMotor.InfiniteVector; |
98 | CurrentValue = TargetValue = Vector3.Zero; | 107 | CurrentValue = TargetValue = Vector3.Zero; |
108 | ErrorZeroThreshold = 0.01f; | ||
99 | } | 109 | } |
100 | public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) | 110 | public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) |
101 | : this(useName) | 111 | : this(useName) |
@@ -114,25 +124,25 @@ public class BSVMotor : BSMotor | |||
114 | { | 124 | { |
115 | TargetValue = target; | 125 | TargetValue = target; |
116 | } | 126 | } |
117 | 127 | public override void Zero() | |
118 | // A form of stepping that does not take the time quantum into account. | ||
119 | // The caller must do the right thing later. | ||
120 | public Vector3 Step() | ||
121 | { | 128 | { |
122 | return Step(1f); | 129 | base.Zero(); |
130 | CurrentValue = TargetValue = Vector3.Zero; | ||
123 | } | 131 | } |
124 | 132 | ||
125 | public Vector3 Step(float timeStep) | 133 | // Compute the next step and return the new current value |
134 | public virtual Vector3 Step(float timeStep) | ||
126 | { | 135 | { |
127 | Vector3 returnCurrent = Vector3.Zero; | 136 | Vector3 origTarget = TargetValue; // DEBUG |
128 | if (!CurrentValue.ApproxEquals(TargetValue, 0.01f)) | 137 | Vector3 origCurrVal = CurrentValue; // DEBUG |
138 | |||
139 | Vector3 correction = Vector3.Zero; | ||
140 | Vector3 error = TargetValue - CurrentValue; | ||
141 | if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) | ||
129 | { | 142 | { |
130 | Vector3 origTarget = TargetValue; // DEBUG | 143 | correction = Step(timeStep, error); |
131 | Vector3 origCurrVal = CurrentValue; // DEBUG | ||
132 | 144 | ||
133 | // Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete | 145 | CurrentValue += correction; |
134 | Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep; | ||
135 | CurrentValue += addAmount; | ||
136 | 146 | ||
137 | // The desired value reduces to zero which also reduces the difference with current. | 147 | // The desired value reduces to zero which also reduces the difference with current. |
138 | // If the decay time is infinite, don't decay at all. | 148 | // If the decay time is infinite, don't decay at all. |
@@ -143,40 +153,80 @@ public class BSVMotor : BSMotor | |||
143 | TargetValue *= (1f - decayFactor); | 153 | TargetValue *= (1f - decayFactor); |
144 | } | 154 | } |
145 | 155 | ||
156 | // The amount we can correct the error is reduced by the friction | ||
146 | Vector3 frictionFactor = Vector3.Zero; | 157 | Vector3 frictionFactor = Vector3.Zero; |
147 | if (FrictionTimescale != BSMotor.InfiniteVector) | 158 | if (FrictionTimescale != BSMotor.InfiniteVector) |
148 | { | 159 | { |
149 | // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep; | 160 | // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep; |
150 | // Individual friction components can be 'infinite' so compute each separately. | 161 | // Individual friction components can be 'infinite' so compute each separately. |
151 | frictionFactor.X = FrictionTimescale.X == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.X) * timeStep; | 162 | frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X); |
152 | frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep; | 163 | frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y); |
153 | frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep; | 164 | frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z); |
165 | frictionFactor *= timeStep; | ||
154 | CurrentValue *= (Vector3.One - frictionFactor); | 166 | CurrentValue *= (Vector3.One - frictionFactor); |
155 | } | 167 | } |
156 | 168 | ||
157 | returnCurrent = CurrentValue; | 169 | MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}", |
158 | |||
159 | MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}", | ||
160 | BSScene.DetailLogZero, UseName, origCurrVal, origTarget, | 170 | BSScene.DetailLogZero, UseName, origCurrVal, origTarget, |
161 | timeStep, TimeScale, addAmount, | 171 | timeStep, error, correction); |
162 | TargetValueDecayTimeScale, decayFactor, | 172 | MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}", |
163 | FrictionTimescale, frictionFactor); | 173 | BSScene.DetailLogZero, UseName, |
164 | MDetailLog("{0}, BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}", | 174 | TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor, |
165 | BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, | 175 | TargetValue, CurrentValue); |
166 | addAmount, decayFactor, frictionFactor, returnCurrent); | ||
167 | } | 176 | } |
168 | else | 177 | else |
169 | { | 178 | { |
170 | // Difference between what we have and target is small. Motor is done. | 179 | // Difference between what we have and target is small. Motor is done. |
171 | CurrentValue = Vector3.Zero; | 180 | CurrentValue = TargetValue; |
172 | TargetValue = Vector3.Zero; | 181 | MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={2}", |
182 | BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue); | ||
183 | } | ||
184 | |||
185 | return CurrentValue; | ||
186 | } | ||
187 | public virtual Vector3 Step(float timeStep, Vector3 error) | ||
188 | { | ||
189 | LastError = error; | ||
190 | Vector3 returnCorrection = Vector3.Zero; | ||
191 | if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) | ||
192 | { | ||
193 | // correction = error / secondsItShouldTakeToCorrect | ||
194 | Vector3 correctionAmount; | ||
195 | if (TimeScale == 0f || TimeScale == BSMotor.Infinite) | ||
196 | correctionAmount = error * timeStep; | ||
197 | else | ||
198 | correctionAmount = error / TimeScale * timeStep; | ||
173 | 199 | ||
174 | MDetailLog("{0}, BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}", | 200 | returnCorrection = correctionAmount; |
175 | BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent); | 201 | MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}", |
202 | BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount); | ||
203 | } | ||
204 | return returnCorrection; | ||
205 | } | ||
176 | 206 | ||
207 | // The user sets all the parameters and calls this which outputs values until error is zero. | ||
208 | public override void GenerateTestOutput(float timeStep) | ||
209 | { | ||
210 | // maximum number of outputs to generate. | ||
211 | int maxOutput = 50; | ||
212 | MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName); | ||
213 | MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}", | ||
214 | BSScene.DetailLogZero, UseName, | ||
215 | TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, | ||
216 | CurrentValue, TargetValue); | ||
217 | |||
218 | LastError = BSMotor.InfiniteVector; | ||
219 | while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) | ||
220 | { | ||
221 | Vector3 lastStep = Step(timeStep); | ||
222 | MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}", | ||
223 | BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep); | ||
177 | } | 224 | } |
178 | return returnCurrent; | 225 | MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName); |
226 | |||
227 | |||
179 | } | 228 | } |
229 | |||
180 | public override string ToString() | 230 | public override string ToString() |
181 | { | 231 | { |
182 | return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>", | 232 | return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>", |
@@ -204,17 +254,74 @@ public class BSFMotor : BSMotor | |||
204 | public void SetTarget(float target) | 254 | public void SetTarget(float target) |
205 | { | 255 | { |
206 | } | 256 | } |
207 | public float Step(float timeStep) | 257 | public virtual float Step(float timeStep) |
208 | { | 258 | { |
209 | return 0f; | 259 | return 0f; |
210 | } | 260 | } |
211 | } | 261 | } |
212 | public class BSPIDMotor : BSMotor | 262 | |
263 | // Proportional, Integral, Derivitive Motor | ||
264 | // Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors. | ||
265 | public class BSPIDVMotor : BSVMotor | ||
213 | { | 266 | { |
214 | // TODO: write and use this one | 267 | // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10. |
215 | public BSPIDMotor(string useName) | 268 | public Vector3 proportionFactor { get; set; } |
269 | public Vector3 integralFactor { get; set; } | ||
270 | public Vector3 derivFactor { get; set; } | ||
271 | // Arbritrary factor range. | ||
272 | // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct. | ||
273 | public float EfficiencyHigh = 0.4f; | ||
274 | public float EfficiencyLow = 4.0f; | ||
275 | |||
276 | Vector3 IntegralFactor { get; set; } | ||
277 | |||
278 | public BSPIDVMotor(string useName) | ||
216 | : base(useName) | 279 | : base(useName) |
217 | { | 280 | { |
281 | proportionFactor = new Vector3(1.00f, 1.00f, 1.00f); | ||
282 | integralFactor = new Vector3(1.00f, 1.00f, 1.00f); | ||
283 | derivFactor = new Vector3(1.00f, 1.00f, 1.00f); | ||
284 | IntegralFactor = Vector3.Zero; | ||
285 | LastError = Vector3.Zero; | ||
286 | } | ||
287 | |||
288 | public override void Zero() | ||
289 | { | ||
290 | base.Zero(); | ||
291 | } | ||
292 | |||
293 | public override float Efficiency | ||
294 | { | ||
295 | get { return base.Efficiency; } | ||
296 | set | ||
297 | { | ||
298 | base.Efficiency = Util.Clamp(value, 0f, 1f); | ||
299 | // Compute factors based on efficiency. | ||
300 | // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot. | ||
301 | // If efficiency is low (0f), use a factor value that overcorrects. | ||
302 | // TODO: might want to vary contribution of different factor depending on efficiency. | ||
303 | float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f; | ||
304 | // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow; | ||
305 | proportionFactor = new Vector3(factor, factor, factor); | ||
306 | integralFactor = new Vector3(factor, factor, factor); | ||
307 | derivFactor = new Vector3(factor, factor, factor); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | // Ignore Current and Target Values and just advance the PID computation on this error. | ||
312 | public override Vector3 Step(float timeStep, Vector3 error) | ||
313 | { | ||
314 | // Add up the error so we can integrate over the accumulated errors | ||
315 | IntegralFactor += error * timeStep; | ||
316 | |||
317 | // A simple derivitive is the rate of change from the last error. | ||
318 | Vector3 derivFactor = (error - LastError) * timeStep; | ||
319 | LastError = error; | ||
320 | |||
321 | // Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError) | ||
322 | Vector3 ret = -(error * proportionFactor + IntegralFactor * integralFactor + derivFactor * derivFactor); | ||
323 | |||
324 | return ret; | ||
218 | } | 325 | } |
219 | } | 326 | } |
220 | } | 327 | } |