Advertisement
Whiplash141

PID Tester

Mar 30th, 2019
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.35 KB | None | 0 0
  1. // -Z is forward
  2. // +X is right
  3. // +Y is up
  4. const Vector3D inputVec = new Vector3D(1,0,-1);
  5. const double kP = 10;
  6. const double kI = 0;
  7. const double kD = 5;
  8. const int runsPerSecond = 10;
  9.  
  10.  
  11. // End of variables
  12.  
  13.  
  14. int ticksPerRun = 60 / runsPerSecond;
  15. int currentTicks = 141;
  16.  
  17. Vector3D targetVec;
  18. bool align = false;
  19.  
  20. PID yawPID;
  21. PID pitchPID;
  22.  
  23. Program()
  24. {
  25.     Runtime.UpdateFrequency = UpdateFrequency.Update1;
  26.     yawPID = new PID(kP, kI, kD, 0.25, 1.0 / (double)runsPerSecond);
  27.     pitchPID = new PID(kP, kI, kD, 0.25, 1.0 / (double)runsPerSecond);
  28. }
  29.  
  30. void Main(string arg, UpdateType updateSource)
  31. {
  32.     var shipController = GetFirstBlockOfType<IMyShipController>();
  33.     var gyros = new List<IMyGyro>();
  34.     GridTerminalSystem.GetBlocksOfType(gyros, x => !x.CustomName.Contains("Ignore"));
  35.  
  36.     if (shipController == null || gyros.Count == 0)
  37.     {
  38.         Echo("ERROR: No ship controller and/or gyro");
  39.         return;
  40.     }
  41.  
  42.     switch (arg.ToLower().Trim())
  43.     {
  44.         case "start":
  45.             targetVec = Vector3D.Rotate(inputVec, shipController.WorldMatrix);
  46.             align = true;
  47.             yawPID.Reset();
  48.             pitchPID.Reset();
  49.             break;
  50.         case "stop":
  51.             align = false;
  52.             break;
  53.     }
  54.  
  55.     currentTicks++;
  56.     if (currentTicks < ticksPerRun)
  57.         return;
  58.  
  59.     currentTicks = 0;
  60.  
  61.  
  62.     if (!align)
  63.     {
  64.         ApplyGyroOverride(0, 0, 0, gyros, shipController);
  65.         Echo("Not aligning");
  66.     }
  67.     else
  68.     {
  69.         double pitch = 0, yaw = 0;
  70.         GetRotationAngles(targetVec, shipController.WorldMatrix, out yaw, out pitch);
  71.        
  72.         var yawSpeed = yawPID.Control(yaw);
  73.         var pitchSpeed = pitchPID.Control(pitch);
  74.        
  75.         Echo($"Yaw: {yaw:n4}");
  76.         Echo($"yawSpeed: {yawSpeed:n2}");
  77.         Echo($"pitch: {pitch:n4}");
  78.         Echo($"pitchSpeed: {pitchSpeed:n2}");
  79.        
  80.         ApplyGyroOverride(pitchSpeed, yawSpeed, 0, gyros, shipController);
  81.         Echo("Aligning...");
  82.     }
  83. }
  84.  
  85. T GetFirstBlockOfType<T>() where T : class, IMyTerminalBlock
  86. {
  87.     var blocks = new List<T>();
  88.     GridTerminalSystem.GetBlocksOfType(blocks);
  89.  
  90.     return blocks.Count > 0 ? blocks[0] : null;
  91. }
  92.  
  93. //Whip's PID controller class v8 - 5/2/18
  94. public class PID
  95. {
  96.     double _kP = 0;
  97.     double _kI = 0;
  98.     double _kD = 0;
  99.     double _integralDecayRatio = 0;
  100.     double _lowerBound = 0;
  101.     double _upperBound = 0;
  102.     double _timeStep = 0;
  103.     double _inverseTimeStep = 0;
  104.     double _errorSum = 0;
  105.     double _lastError = 0;
  106.     double _scalingThreshold = -1;
  107.     bool _firstRun = true;
  108.     bool _integralDecay = false;
  109.     bool _scaleDerivative = false;
  110.     public double Value { get; private set; }
  111.  
  112.     public PID(double kP, double kI, double kD, double lowerBound, double upperBound, double timeStep)
  113.     {
  114.         _kP = kP;
  115.         _kI = kI;
  116.         _kD = kD;
  117.         _lowerBound = lowerBound;
  118.         _upperBound = upperBound;
  119.         _timeStep = timeStep;
  120.         _inverseTimeStep = 1 / _timeStep;
  121.         _integralDecay = false;
  122.         _scaleDerivative = false;
  123.     }
  124.  
  125.     public PID(double kP, double kI, double kD, double integralDecayRatio, double timeStep)
  126.     {
  127.         _kP = kP;
  128.         _kI = kI;
  129.         _kD = kD;
  130.         _timeStep = timeStep;
  131.         _inverseTimeStep = 1 / _timeStep;
  132.         _integralDecayRatio = integralDecayRatio;
  133.         _integralDecay = true;
  134.         _scaleDerivative = false;
  135.     }
  136.    
  137.     public PID(double kP, double kI, double kD, double integralDecayRatio, double timeStep, double scalingThreshold, bool placeholder)
  138.     {
  139.         _kP = kP;
  140.         _kI = kI;
  141.         _kD = kD;
  142.         _timeStep = timeStep;
  143.         _inverseTimeStep = 1 / _timeStep;
  144.         _integralDecayRatio = integralDecayRatio;
  145.         _scalingThreshold = scalingThreshold;
  146.         _integralDecay = true;
  147.         _scaleDerivative = true;
  148.     }
  149.  
  150.     public double Control(double error)
  151.     {
  152.         //Compute derivative term
  153.         var errorDerivative = (error - _lastError) * _inverseTimeStep;
  154.  
  155.         if (_firstRun)
  156.         {
  157.             errorDerivative = 0;
  158.             _firstRun = false;
  159.         }
  160.  
  161.         //Compute integral term
  162.         if (!_integralDecay)
  163.         {
  164.             _errorSum += error * _timeStep;
  165.  
  166.             //Clamp integral term
  167.             if (_errorSum > _upperBound)
  168.                 _errorSum = _upperBound;
  169.             else if (_errorSum < _lowerBound)
  170.                 _errorSum = _lowerBound;
  171.         }
  172.         else
  173.         {
  174.             _errorSum = _errorSum * (1.0 - _integralDecayRatio) + error * _timeStep;
  175.         }
  176.  
  177.         //Store this error as last error
  178.         _lastError = error;
  179.  
  180.         //Construct output
  181.        
  182.         this.Value = _kP * error + _kI * _errorSum + (_scaleDerivative && Math.Abs(error) < _scalingThreshold ? Math.Abs(error / _scalingThreshold) * _kD * errorDerivative : _kD * errorDerivative);
  183.         return this.Value;
  184.     }
  185.    
  186.     public double Control(double error, double timeStep)
  187.     {
  188.         _timeStep = timeStep;
  189.         _inverseTimeStep = 1 / _timeStep;
  190.         return Control(error);
  191.     }
  192.  
  193.     public void Reset()
  194.     {
  195.         _errorSum = 0;
  196.         _lastError = 0;
  197.         _firstRun = true;
  198.     }
  199. }
  200.  
  201. void GetRotationAngles(Vector3D targetVector, MatrixD worldMatrix, out double yaw, out double pitch)
  202. {
  203.     var localTargetVector = Vector3D.TransformNormal(targetVector, MatrixD.Transpose(worldMatrix));
  204.     var flattenedTargetVector = new Vector3D(localTargetVector.X, 0, localTargetVector.Z);
  205.    
  206.     yaw = VectorAngleBetween(Vector3D.Forward, flattenedTargetVector) * Math.Sign(localTargetVector.X); //right is positive
  207.     if (Math.Abs(yaw) < 1E-6 && localTargetVector.Z > 0) //check for straight back case
  208.         yaw = Math.PI;
  209.    
  210.     if (Vector3D.IsZero(flattenedTargetVector)) //check for straight up case
  211.         pitch = MathHelper.PiOver2 * Math.Sign(localTargetVector.Y);
  212.     else
  213.         pitch = VectorAngleBetween(localTargetVector, flattenedTargetVector) * Math.Sign(localTargetVector.Y); //up is positive
  214. }
  215.  
  216. Vector3D VectorProjection(Vector3D a, Vector3D b)
  217. {
  218.     if (Vector3D.IsZero(b))
  219.         return Vector3D.Zero;
  220.  
  221.     return a.Dot(b) / b.LengthSquared() * b;
  222. }
  223.  
  224. double VectorAngleBetween(Vector3D a, Vector3D b) //returns radians
  225. {
  226.     if (Vector3D.IsZero(a) || Vector3D.IsZero(b))
  227.         return 0;
  228.     else
  229.         return Math.Acos(MathHelper.Clamp(a.Dot(b) / Math.Sqrt(a.LengthSquared() * b.LengthSquared()), -1, 1));
  230. }
  231.  
  232. //Whip's ApplyGyroOverride Method v9 - 8/19/17
  233. void ApplyGyroOverride(double pitch_speed, double yaw_speed, double roll_speed, List<IMyGyro> gyro_list, IMyTerminalBlock reference)
  234. {
  235.     var rotationVec = new Vector3D(-pitch_speed, yaw_speed, roll_speed);
  236.  
  237.     var shipMatrix = reference.WorldMatrix;
  238.     var relativeRotationVec = Vector3D.TransformNormal(rotationVec, shipMatrix);
  239.  
  240.     foreach (var thisGyro in gyro_list)
  241.     {
  242.         var gyroMatrix = thisGyro.WorldMatrix;
  243.         var transformedRotationVec = Vector3D.TransformNormal(relativeRotationVec, Matrix.Transpose(gyroMatrix));
  244.  
  245.         thisGyro.Pitch = (float)transformedRotationVec.X; //because keen does some weird stuff with signs
  246.         thisGyro.Yaw = (float)transformedRotationVec.Y;
  247.         thisGyro.Roll = (float)transformedRotationVec.Z;
  248.         thisGyro.GyroOverride = true;
  249.     }
  250. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement