aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs')
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMotors.cs571
1 files changed, 467 insertions, 104 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
index bc6e4c4..6d0db2e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
@@ -1,104 +1,467 @@
1using System; 1/*
2using System.Collections.Generic; 2 * Copyright (c) Contributors, http://opensimulator.org/
3using System.Text; 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4using OpenMetaverse; 4 *
5 5 * Redistribution and use in source and binary forms, with or without
6namespace OpenSim.Region.Physics.BulletSPlugin 6 * modification, are permitted provided that the following conditions are met:
7{ 7 * * Redistributions of source code must retain the above copyright
8public abstract class BSMotor 8 * notice, this list of conditions and the following disclaimer.
9{ 9 * * Redistributions in binary form must reproduce the above copyright
10 public virtual void Reset() { } 10 * notice, this list of conditions and the following disclaimer in the
11 public virtual void Zero() { } 11 * documentation and/or other materials provided with the distribution.
12} 12 * * Neither the name of the OpenSimulator Project nor the
13// Can all the incremental stepping be replaced with motor classes? 13 * names of its contributors may be used to endorse or promote products
14public class BSVMotor : BSMotor 14 * derived from this software without specific prior written permission.
15{ 15 *
16 public Vector3 FrameOfReference { get; set; } 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 public Vector3 Offset { get; set; } 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 public float TimeScale { get; set; } 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 public float TargetValueDecayTimeScale { get; set; } 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 public Vector3 CurrentValueReductionTimescale { get; set; } 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 public float Efficiency { get; set; } 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 public Vector3 TargetValue { get; private set; } 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 public Vector3 CurrentValue { get; private set; } 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 26 *
27 27 */
28 28using System;
29 BSVMotor(float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) 29using System.Collections.Generic;
30 { 30using System.Text;
31 TimeScale = timeScale; 31using OpenMetaverse;
32 TargetValueDecayTimeScale = decayTimeScale; 32using OpenSim.Framework;
33 CurrentValueReductionTimescale = frictionTimeScale; 33
34 Efficiency = efficiency; 34namespace OpenSim.Region.Physics.BulletSPlugin
35 } 35{
36 public void SetCurrent(Vector3 current) 36public abstract class BSMotor
37 { 37{
38 CurrentValue = current; 38 // Timescales and other things can be turned off by setting them to 'infinite'.
39 } 39 public const float Infinite = 12345.6f;
40 public void SetTarget(Vector3 target) 40 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
41 { 41
42 TargetValue = target; 42 public BSMotor(string useName)
43 } 43 {
44 public Vector3 Step(float timeStep) 44 UseName = useName;
45 { 45 PhysicsScene = null;
46 if (CurrentValue.LengthSquared() > 0.001f) 46 Enabled = true;
47 { 47 }
48 // Vector3 origDir = Target; // DEBUG 48 public virtual bool Enabled { get; set; }
49 // Vector3 origVel = CurrentValue; // DEBUG 49 public virtual void Reset() { }
50 50 public virtual void Zero() { }
51 // Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete 51 public virtual void GenerateTestOutput(float timeStep) { }
52 Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; 52
53 CurrentValue += addAmount; 53 // A name passed at motor creation for easily identifyable debugging messages.
54 54 public string UseName { get; private set; }
55 float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; 55
56 TargetValue *= (1f - decayFactor); 56 // Used only for outputting debug information. Might not be set so check for null.
57 57 public BSScene PhysicsScene { get; set; }
58 Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; 58 protected void MDetailLog(string msg, params Object[] parms)
59 CurrentValue *= (Vector3.One - frictionFactor); 59 {
60 } 60 if (PhysicsScene != null)
61 else 61 {
62 { 62 PhysicsScene.DetailLog(msg, parms);
63 // if what remains of direction is very small, zero it. 63 }
64 TargetValue = Vector3.Zero; 64 }
65 CurrentValue = Vector3.Zero; 65}
66 66
67 // VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 67// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
68 } 68// The TargetValue decays in TargetValueDecayTimeScale and
69 return CurrentValue; 69// the CurrentValue will be held back by FrictionTimeScale.
70 } 70// This motor will "zero itself" over time in that the targetValue will
71} 71// decay to zero and the currentValue will follow it to that zero.
72 72// The overall effect is for the returned correction value to go from large
73public class BSFMotor : BSMotor 73// values (the total difference between current and target minus friction)
74{ 74// to small and eventually zero values.
75 public float TimeScale { get; set; } 75// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
76 public float DecayTimeScale { get; set; } 76
77 public float Friction { get; set; } 77// For instance, if something is moving at speed X and the desired speed is Y,
78 public float Efficiency { get; set; } 78// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
79 79// values of CurrentValue are returned that approach the TargetValue.
80 public float Target { get; private set; } 80// The feature of decaying TargetValue is so vehicles will eventually
81 public float CurrentValue { get; private set; } 81// come to a stop rather than run forever. This can be disabled by
82 82// setting TargetValueDecayTimescale to 'infinite'.
83 BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) 83// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
84 { 84public class BSVMotor : BSMotor
85 } 85{
86 public void SetCurrent(float target) 86 // public Vector3 FrameOfReference { get; set; }
87 { 87 // public Vector3 Offset { get; set; }
88 } 88
89 public void SetTarget(float target) 89 public virtual float TimeScale { get; set; }
90 { 90 public virtual float TargetValueDecayTimeScale { get; set; }
91 } 91 public virtual Vector3 FrictionTimescale { get; set; }
92 public float Step(float timeStep) 92 public virtual float Efficiency { get; set; }
93 { 93
94 return 0f; 94 public virtual float ErrorZeroThreshold { get; set; }
95 } 95
96} 96 public virtual Vector3 TargetValue { get; protected set; }
97public class BSPIDMotor : BSMotor 97 public virtual Vector3 CurrentValue { get; protected set; }
98{ 98 public virtual Vector3 LastError { get; protected set; }
99 // TODO: write and use this one 99
100 BSPIDMotor() 100 public virtual bool ErrorIsZero()
101 { 101 {
102 } 102 return ErrorIsZero(LastError);
103} 103 }
104} 104 public virtual bool ErrorIsZero(Vector3 err)
105 {
106 return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold));
107 }
108
109 public BSVMotor(string useName)
110 : base(useName)
111 {
112 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
113 Efficiency = 1f;
114 FrictionTimescale = BSMotor.InfiniteVector;
115 CurrentValue = TargetValue = Vector3.Zero;
116 ErrorZeroThreshold = 0.001f;
117 }
118 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
119 : this(useName)
120 {
121 TimeScale = timeScale;
122 TargetValueDecayTimeScale = decayTimeScale;
123 FrictionTimescale = frictionTimeScale;
124 Efficiency = efficiency;
125 CurrentValue = TargetValue = Vector3.Zero;
126 }
127 public void SetCurrent(Vector3 current)
128 {
129 CurrentValue = current;
130 }
131 public void SetTarget(Vector3 target)
132 {
133 TargetValue = target;
134 }
135 public override void Zero()
136 {
137 base.Zero();
138 CurrentValue = TargetValue = Vector3.Zero;
139 }
140
141 // Compute the next step and return the new current value
142 public virtual Vector3 Step(float timeStep)
143 {
144 if (!Enabled) return TargetValue;
145
146 Vector3 origTarget = TargetValue; // DEBUG
147 Vector3 origCurrVal = CurrentValue; // DEBUG
148
149 Vector3 correction = Vector3.Zero;
150 Vector3 error = TargetValue - CurrentValue;
151 if (!ErrorIsZero(error))
152 {
153 correction = Step(timeStep, error);
154
155 CurrentValue += correction;
156
157 // The desired value reduces to zero which also reduces the difference with current.
158 // If the decay time is infinite, don't decay at all.
159 float decayFactor = 0f;
160 if (TargetValueDecayTimeScale != BSMotor.Infinite)
161 {
162 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
163 TargetValue *= (1f - decayFactor);
164 }
165
166 // The amount we can correct the error is reduced by the friction
167 Vector3 frictionFactor = Vector3.Zero;
168 if (FrictionTimescale != BSMotor.InfiniteVector)
169 {
170 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
171 // Individual friction components can be 'infinite' so compute each separately.
172 frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
173 frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
174 frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
175 frictionFactor *= timeStep;
176 CurrentValue *= (Vector3.One - frictionFactor);
177 }
178
179 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
180 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
181 timeStep, error, correction);
182 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
183 BSScene.DetailLogZero, UseName,
184 TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
185 TargetValue, CurrentValue);
186 }
187 else
188 {
189 // Difference between what we have and target is small. Motor is done.
190 CurrentValue = TargetValue;
191 MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
192 BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
193 }
194
195 return CurrentValue;
196 }
197 public virtual Vector3 Step(float timeStep, Vector3 error)
198 {
199 if (!Enabled) return Vector3.Zero;
200
201 LastError = error;
202 Vector3 returnCorrection = Vector3.Zero;
203 if (!ErrorIsZero())
204 {
205 // correction = error / secondsItShouldTakeToCorrect
206 Vector3 correctionAmount;
207 if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
208 correctionAmount = error * timeStep;
209 else
210 correctionAmount = error / TimeScale * timeStep;
211
212 returnCorrection = correctionAmount;
213 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
214 BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
215 }
216 return returnCorrection;
217 }
218
219 // The user sets all the parameters and calls this which outputs values until error is zero.
220 public override void GenerateTestOutput(float timeStep)
221 {
222 // maximum number of outputs to generate.
223 int maxOutput = 50;
224 MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
225 MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
226 BSScene.DetailLogZero, UseName,
227 TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
228 CurrentValue, TargetValue);
229
230 LastError = BSMotor.InfiniteVector;
231 while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
232 {
233 Vector3 lastStep = Step(timeStep);
234 MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
235 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
236 }
237 MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
238
239
240 }
241
242 public override string ToString()
243 {
244 return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4},frictTS={5}>",
245 UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale, FrictionTimescale);
246 }
247}
248
249// ============================================================================
250// ============================================================================
251public class BSFMotor : BSMotor
252{
253 public virtual float TimeScale { get; set; }
254 public virtual float TargetValueDecayTimeScale { get; set; }
255 public virtual float FrictionTimescale { get; set; }
256 public virtual float Efficiency { get; set; }
257
258 public virtual float ErrorZeroThreshold { get; set; }
259
260 public virtual float TargetValue { get; protected set; }
261 public virtual float CurrentValue { get; protected set; }
262 public virtual float LastError { get; protected set; }
263
264 public virtual bool ErrorIsZero()
265 {
266 return ErrorIsZero(LastError);
267 }
268 public virtual bool ErrorIsZero(float err)
269 {
270 return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold);
271 }
272
273 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
274 : base(useName)
275 {
276 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
277 Efficiency = 1f;
278 FrictionTimescale = BSMotor.Infinite;
279 CurrentValue = TargetValue = 0f;
280 ErrorZeroThreshold = 0.01f;
281 }
282 public void SetCurrent(float current)
283 {
284 CurrentValue = current;
285 }
286 public void SetTarget(float target)
287 {
288 TargetValue = target;
289 }
290 public override void Zero()
291 {
292 base.Zero();
293 CurrentValue = TargetValue = 0f;
294 }
295
296 public virtual float Step(float timeStep)
297 {
298 if (!Enabled) return TargetValue;
299
300 float origTarget = TargetValue; // DEBUG
301 float origCurrVal = CurrentValue; // DEBUG
302
303 float correction = 0f;
304 float error = TargetValue - CurrentValue;
305 if (!ErrorIsZero(error))
306 {
307 correction = Step(timeStep, error);
308
309 CurrentValue += correction;
310
311 // The desired value reduces to zero which also reduces the difference with current.
312 // If the decay time is infinite, don't decay at all.
313 float decayFactor = 0f;
314 if (TargetValueDecayTimeScale != BSMotor.Infinite)
315 {
316 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
317 TargetValue *= (1f - decayFactor);
318 }
319
320 // The amount we can correct the error is reduced by the friction
321 float frictionFactor = 0f;
322 if (FrictionTimescale != BSMotor.Infinite)
323 {
324 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
325 // Individual friction components can be 'infinite' so compute each separately.
326 frictionFactor = 1f / FrictionTimescale;
327 frictionFactor *= timeStep;
328 CurrentValue *= (1f - frictionFactor);
329 }
330
331 MDetailLog("{0}, BSFMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
332 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
333 timeStep, error, correction);
334 MDetailLog("{0}, BSFMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
335 BSScene.DetailLogZero, UseName,
336 TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
337 TargetValue, CurrentValue);
338 }
339 else
340 {
341 // Difference between what we have and target is small. Motor is done.
342 CurrentValue = TargetValue;
343 MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
344 BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
345 }
346
347 return CurrentValue;
348 }
349
350 public virtual float Step(float timeStep, float error)
351 {
352 if (!Enabled) return 0f;
353
354 LastError = error;
355 float returnCorrection = 0f;
356 if (!ErrorIsZero())
357 {
358 // correction = error / secondsItShouldTakeToCorrect
359 float correctionAmount;
360 if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
361 correctionAmount = error * timeStep;
362 else
363 correctionAmount = error / TimeScale * timeStep;
364
365 returnCorrection = correctionAmount;
366 MDetailLog("{0}, BSFMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
367 BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
368 }
369 return returnCorrection;
370 }
371
372 public override string ToString()
373 {
374 return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4},frictTS={5}>",
375 UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale, FrictionTimescale);
376 }
377
378}
379
380// ============================================================================
381// ============================================================================
382// Proportional, Integral, Derivitive Motor
383// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
384public class BSPIDVMotor : BSVMotor
385{
386 // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
387 public Vector3 proportionFactor { get; set; }
388 public Vector3 integralFactor { get; set; }
389 public Vector3 derivFactor { get; set; }
390
391 // The factors are vectors for the three dimensions. This is the proportional of each
392 // that is applied. This could be multiplied through the actual factors but it
393 // is sometimes easier to manipulate the factors and their mix separately.
394 // to
395 public Vector3 FactorMix;
396
397 // Arbritrary factor range.
398 // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
399 public float EfficiencyHigh = 0.4f;
400 public float EfficiencyLow = 4.0f;
401
402 // Running integration of the error
403 Vector3 RunningIntegration { get; set; }
404
405 public BSPIDVMotor(string useName)
406 : base(useName)
407 {
408 proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
409 integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
410 derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
411 FactorMix = new Vector3(0.5f, 0.25f, 0.25f);
412 RunningIntegration = Vector3.Zero;
413 LastError = Vector3.Zero;
414 }
415
416 public override void Zero()
417 {
418 base.Zero();
419 }
420
421 public override float Efficiency
422 {
423 get { return base.Efficiency; }
424 set
425 {
426 base.Efficiency = Util.Clamp(value, 0f, 1f);
427
428 // Compute factors based on efficiency.
429 // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
430 // If efficiency is low (0f), use a factor value that overcorrects.
431 // TODO: might want to vary contribution of different factor depending on efficiency.
432 float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
433 // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
434
435 proportionFactor = new Vector3(factor, factor, factor);
436 integralFactor = new Vector3(factor, factor, factor);
437 derivFactor = new Vector3(factor, factor, factor);
438
439 MDetailLog("{0},BSPIDVMotor.setEfficiency,eff={1},factor={2}", BSScene.DetailLogZero, Efficiency, factor);
440 }
441 }
442
443 // Ignore Current and Target Values and just advance the PID computation on this error.
444 public override Vector3 Step(float timeStep, Vector3 error)
445 {
446 if (!Enabled) return Vector3.Zero;
447
448 // Add up the error so we can integrate over the accumulated errors
449 RunningIntegration += error * timeStep;
450
451 // A simple derivitive is the rate of change from the last error.
452 Vector3 derivitive = (error - LastError) * timeStep;
453 LastError = error;
454
455 // Correction = (proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
456 Vector3 ret = error * timeStep * proportionFactor * FactorMix.X
457 + RunningIntegration * integralFactor * FactorMix.Y
458 + derivitive * derivFactor * FactorMix.Z
459 ;
460
461 MDetailLog("{0},BSPIDVMotor.step,ts={1},err={2},runnInt={3},deriv={4},ret={5}",
462 BSScene.DetailLogZero, timeStep, error, RunningIntegration, derivitive, ret);
463
464 return ret;
465 }
466}
467}