BSMotors.cs 18 KB

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