Whiplash141

Whip's Lift Off Script v8

Nov 10th, 2017
1,249
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.31 KB | None | 0 0
  1. /*
  2. //Whip's Lift Off Script v8 - 1/10/18
  3.  
  4. /// TO DO: Make ion and atmos go full burn and make hydro pick up the slack
  5. ___________________________________________________________________________________
  6. /// DESCRIPTION ///
  7.  
  8. This script automatically throttles your FORWARD thrusters to optimize
  9. fuel useage while exiting planetary influence. This script also takes
  10. control of the gyroscopes on the grid in order to ensure that you are on
  11. the optimal escape trajectory. Once your ship leaves the gravity well, the
  12. code will execute a "Turn 'N Burn" to make you come to a stop.
  13.  
  14. Make sure
  15. to point your reference ship controller in the direction you wish to take off!
  16. ___________________________________________________________________________________
  17. /// SETUP ///
  18.  
  19. 1. Place a programmable block with this program loaded in it
  20.  
  21. 2. Place a ship controller (cockpit, flight seat, remote, etc...)
  22.     - add the phrase "Reference" into it's name somewhere
  23.     - The thrusters that propel the craft FORWARD will automatically be grabbed
  24.  
  25. 3. Enter the argument "start" to begin lift-off
  26. ___________________________________________________________________________________
  27. /// ARGUMENTS ///
  28.  
  29. start : starts lift-off procedure
  30.  
  31. stop : stops lift-off procedure
  32. */
  33.  
  34. string shipControllerName = "Reference";
  35. double ascentSpeed = 95;
  36. bool minimizeHydrogenUseage = true;
  37.  
  38. //=====================================================================
  39. //                NO TOUCHEY BELOW THIS LINE!!!111!1!
  40. //=====================================================================
  41.  
  42. IMyShipController reference;
  43. List<IMyThrust> mainThrusters = new List<IMyThrust>();
  44. List<IMyThrust> hydroThrusters = new List<IMyThrust>();
  45. List<IMyThrust> ionAndAtmoThrusters = new List<IMyThrust>();
  46. List<IMyGyro> gyros = new List<IMyGyro>();
  47.  
  48. bool isSetup = false;
  49. bool shouldLiftOff = false;
  50.  
  51. const double updatesPerSecond = 10;
  52. const double updateTime = 1.0 / updatesPerSecond;
  53. const double refreshInterval = 10;
  54. const double minAlignmentTicks = 60;
  55.  
  56. double currentRefreshTime = 141;
  57. double timeSinceLastUpdate = 141;
  58. double alignmemtTicks = 0;
  59.  
  60. PID velocityPID = new PID(1, .2, .4, -10, 10, updateTime);
  61.  
  62. Program()
  63. {
  64.     isSetup = GrabBlocks();
  65.     Runtime.UpdateFrequency = UpdateFrequency.Once;
  66. }
  67.  
  68. void Main(string arg, UpdateType updateType)
  69. {
  70.     //Argument handling
  71.     if ((updateType & (UpdateType.Trigger | UpdateType.Terminal | UpdateType.Script)) != 0)
  72.     {
  73.         switch (arg.ToLower())
  74.         {
  75.             case "start":
  76.                 shouldLiftOff = true;
  77.                 alignmemtTicks = 0;
  78.                 Runtime.UpdateFrequency = UpdateFrequency.Update1;
  79.                 break;
  80.  
  81.             case "stop":
  82.                 shouldLiftOff = false;
  83.                 DisableGyroOverride(gyros);
  84.                 ApplyThrust(mainThrusters, 0);
  85.                 alignmemtTicks = 0;
  86.                 Runtime.UpdateFrequency = UpdateFrequency.None;
  87.                 break;
  88.         }
  89.     }
  90.  
  91.     Echo("WMI Liftoff Manager\n");
  92.     Echo($"Lift Off?: {shouldLiftOff}");
  93.  
  94.     if ((updateType & UpdateType.Update1) == 0)
  95.         return;
  96.  
  97.     currentRefreshTime += 1.0 / 60.0;
  98.     timeSinceLastUpdate += 1.0 / 60.0;
  99.  
  100.     if (!isSetup || currentRefreshTime >= refreshInterval)
  101.     {
  102.         isSetup = GrabBlocks();
  103.         currentRefreshTime = 0;
  104.     }
  105.  
  106.     if (!isSetup)
  107.         return;
  108.  
  109.     if (timeSinceLastUpdate >= updateTime)
  110.     {
  111.         if (shouldLiftOff)
  112.             LiftOff();
  113.  
  114.         timeSinceLastUpdate = 0;
  115.     }
  116. }
  117.  
  118. void LiftOff()
  119. {
  120.     var mass = reference.CalculateShipMass().PhysicalMass;
  121.     var gravityVec = reference.GetNaturalGravity();
  122.  
  123.     double thrustForce = 0;
  124.     var shipWeight = mass * gravityVec.Length();
  125.    
  126.     bool useHydrogen = false;
  127.     double ionThrustSum = 0, hydroThrustSum = 0, atmoThrustSum = 0;
  128.  
  129.     if (minimizeHydrogenUseage)
  130.     {
  131.         //Calculate thrust sums
  132.        
  133.         CalculateMaxThrustByType(mainThrusters, out ionThrustSum, out hydroThrustSum, out atmoThrustSum, out hydroThrusters, out ionAndAtmoThrusters);
  134.  
  135.         useHydrogen = ionThrustSum + atmoThrustSum <= shipWeight;
  136.        
  137.         foreach (var block in hydroThrusters)
  138.         {
  139.             block.Enabled = useHydrogen;
  140.         }
  141.  
  142.         foreach (var block in ionAndAtmoThrusters)
  143.         {
  144.             block.Enabled = true;
  145.             block.ThrustOverridePercentage = 1f; //full override
  146.            
  147.             shipWeight -= block.IsFunctional && useHydrogen ? block.MaxEffectiveThrust : 0.0;
  148.         }
  149.     }
  150.     else
  151.     {
  152.         foreach (var block in mainThrusters)
  153.         {
  154.             block.Enabled = true;
  155.         }
  156.     }
  157.  
  158.     if (useHydrogen)
  159.         thrustForce = hydroThrustSum;
  160.     else
  161.         thrustForce = CalculateMaxThrust(mainThrusters);
  162.  
  163.     //var maxAcceleration = thrustForce / mass;
  164.     var velocityVec = reference.GetShipVelocities().LinearVelocity;
  165.     var speed = velocityVec.Length();
  166.  
  167.     Vector3D alignmentVector = new Vector3D(0, 0, 0);
  168.  
  169.     if (gravityVec.LengthSquared() == 0) //outside gravity well
  170.     {
  171.         //execute retro-burn
  172.         alignmentVector = -velocityVec;
  173.  
  174.         double deviationAngle = VectorAngleBetween(reference.WorldMatrix.Forward, alignmentVector);
  175.         Echo($"{deviationAngle}");
  176.  
  177.         ApplyThrust(mainThrusters, 0);
  178.  
  179.         if (deviationAngle < 5.0 / 180.0 * Math.PI)
  180.         {
  181.             if (alignmemtTicks > minAlignmentTicks)
  182.             {
  183.                 reference.DampenersOverride = true;
  184.  
  185.             }
  186.             else
  187.             {
  188.                 reference.DampenersOverride = false;
  189.                 alignmemtTicks++;
  190.             }
  191.         }
  192.         else
  193.             reference.DampenersOverride = false;
  194.  
  195.         if (speed < 1)
  196.         {
  197.             DisableGyroOverride(gyros);
  198.             reference.DampenersOverride = true;
  199.             shouldLiftOff = false;
  200.             alignmemtTicks = 0;
  201.             return;
  202.         }
  203.     }
  204.     else
  205.     {
  206.         //var lateralVelocity = velocityVec - VectorProjection(velocityVec, gravityVec);
  207.         alignmentVector = CalculateHeadingVector(-gravityVec, velocityVec, true, 0.5);
  208.  
  209.         //var equilibriumThrustPercentage = gravityVec.Length() / maxAcceleration;
  210.         var equilibriumThrustPercentage = shipWeight / thrustForce;
  211.         var thrustAdjustment = velocityPID.Control(ascentSpeed - speed * Math.Sign(velocityVec.Dot(-gravityVec)));
  212.         var finalThrustOverride = equilibriumThrustPercentage + thrustAdjustment * 0.01;
  213.        
  214.         if (useHydrogen)
  215.             ApplyThrust(hydroThrusters, finalThrustOverride);
  216.         else
  217.             ApplyThrust(mainThrusters, finalThrustOverride);
  218.     }
  219.  
  220.     double pitch = 0, yaw = 0;
  221.     GetRotationAngles(alignmentVector, reference.WorldMatrix.Forward, reference.WorldMatrix.Left, reference.WorldMatrix.Up, out yaw, out pitch);
  222.  
  223.     double pitchSpeed = Math.Round(pitch, 2);
  224.     double yawSpeed = Math.Round(yaw, 2);
  225.     ApplyGyroOverride(pitchSpeed, yawSpeed, 0, gyros, reference);
  226. }
  227.  
  228. void ApplyThrust(List<IMyThrust> thrusters, double thrustOverride)
  229. {
  230.     foreach (var block in thrusters)
  231.         block.ThrustOverridePercentage = (float)thrustOverride;
  232. }
  233.  
  234. bool GrabBlocks()
  235. {
  236.     List<IMyShipController> shipControllers = new List<IMyShipController>();
  237.     GridTerminalSystem.GetBlocksOfType(shipControllers, x => x.CustomName.Contains(shipControllerName));
  238.  
  239.     if (shipControllers.Count == 0)
  240.     {
  241.         Echo($"Error: No ship controller named '{shipControllerName}' were found!");
  242.         return false;
  243.     }
  244.  
  245.     reference = shipControllers[0];
  246.  
  247.     GridTerminalSystem.GetBlocksOfType(mainThrusters, x => x.WorldMatrix.Forward == reference.WorldMatrix.Backward);
  248.     if (mainThrusters.Count == 0)
  249.     {
  250.         Echo($"Error: No lift-off thrusters were found!");
  251.         return false;
  252.     }
  253.  
  254.     GridTerminalSystem.GetBlocksOfType(gyros);
  255.     if (gyros.Count == 0)
  256.     {
  257.         Echo($"Error: No gyros were found!");
  258.         return false;
  259.     }
  260.  
  261.     return true;
  262. }
  263.  
  264. void CalculateMaxThrustByType(List<IMyThrust> thrust, out double ionSum, out double hydroSum, out double atmoSum, out List<IMyThrust> hydroThrust, out List<IMyThrust> ionAndAtmosThrust)
  265. {
  266.     ionSum = 0; hydroSum = 0; atmoSum = 0;
  267.     hydroThrust = new List<IMyThrust>();
  268.     ionAndAtmosThrust = new List<IMyThrust>();
  269.  
  270.     foreach (var block in thrust)
  271.     {
  272.         var definitionName = block.BlockDefinition.SubtypeId;
  273.  
  274.         if (definitionName.ToUpperInvariant().Contains("HYDROGEN"))
  275.         {
  276.             hydroSum += block.IsFunctional ? block.MaxEffectiveThrust : 0.0;
  277.             hydroThrust.Add(block);
  278.         }
  279.         else if (definitionName.ToUpperInvariant().Contains("ATMOSPHERIC"))
  280.         {
  281.             atmoSum += block.IsFunctional ? block.MaxEffectiveThrust : 0.0;
  282.             ionAndAtmosThrust.Add(block);
  283.         }
  284.         else
  285.         {
  286.             ionSum += block.IsFunctional ? block.MaxEffectiveThrust : 0.0;
  287.             ionAndAtmosThrust.Add(block);
  288.         }
  289.     }
  290. }
  291.  
  292. double CalculateMaxThrust(List<IMyThrust> thrusters)
  293. {
  294.     double thrustSum = 0;
  295.     foreach (var block in thrusters)
  296.     {
  297.         thrustSum += block.IsWorking ? block.MaxEffectiveThrust : 0.0;
  298.     }
  299.     return thrustSum;
  300. }
  301.  
  302. void DisableGyroOverride(List<IMyGyro> gyros)
  303. {
  304.     foreach (var block in gyros)
  305.         block.GyroOverride = false;
  306. }
  307.  
  308. //Whip's ApplyGyroOverride Method v9 - 8/19/17
  309. void ApplyGyroOverride(double pitch_speed, double yaw_speed, double roll_speed, List<IMyGyro> gyro_list, IMyTerminalBlock reference)
  310. {
  311.     var rotationVec = new Vector3D(-pitch_speed, yaw_speed, roll_speed); //because keen does some weird stuff with signs
  312.     var shipMatrix = reference.WorldMatrix;
  313.     var relativeRotationVec = Vector3D.TransformNormal(rotationVec, shipMatrix);
  314.  
  315.     foreach (var thisGyro in gyro_list)
  316.     {
  317.         var gyroMatrix = thisGyro.WorldMatrix;
  318.         var transformedRotationVec = Vector3D.TransformNormal(relativeRotationVec, Matrix.Transpose(gyroMatrix));
  319.  
  320.         thisGyro.Pitch = (float)transformedRotationVec.X;
  321.         thisGyro.Yaw = (float)transformedRotationVec.Y;
  322.         thisGyro.Roll = (float)transformedRotationVec.Z;
  323.         thisGyro.GyroOverride = true;
  324.     }
  325. }
  326.  
  327. //Whip's Get Rotation Angles Method v5 - 5/30/17
  328. void GetRotationAngles(Vector3D v_target, Vector3D v_front, Vector3D v_left, Vector3D v_up, out double yaw, out double pitch)
  329. {
  330.     //Dependencies: VectorProjection() | VectorAngleBetween()
  331.     var projectTargetUp = VectorProjection(v_target, v_up);
  332.     var projTargetFrontLeft = v_target - projectTargetUp;
  333.  
  334.     yaw = VectorAngleBetween(v_front, projTargetFrontLeft);
  335.     pitch = VectorAngleBetween(v_target, projTargetFrontLeft);
  336.  
  337.     //---Check if yaw angle is left or right  
  338.     //multiplied by -1 to convert from right hand rule to left hand rule
  339.     yaw = -1 * Math.Sign(v_left.Dot(v_target)) * yaw;
  340.  
  341.     //---Check if pitch angle is up or down    
  342.     pitch = Math.Sign(v_up.Dot(v_target)) * pitch;
  343.  
  344.     //---Check if target vector is pointing opposite the front vector
  345.     if (pitch == 0 && yaw == 0 && v_target.Dot(v_front) < 0)
  346.     {
  347.         yaw = Math.PI;
  348.     }
  349. }
  350.  
  351. Vector3D CalculateHeadingVector(Vector3D targetVec, Vector3D velocityVec, bool driftComp, double rejectionFactor)
  352. {
  353.     if (!driftComp)
  354.     {
  355.         return targetVec;
  356.     }
  357.  
  358.     if (velocityVec.LengthSquared() < 100)
  359.     {
  360.         return targetVec;
  361.     }
  362.  
  363.     if (targetVec.Dot(velocityVec) > 0)
  364.     {
  365.         return VectorReflection(velocityVec, targetVec, rejectionFactor);
  366.     }
  367.     else
  368.     {
  369.         return -velocityVec;
  370.     }
  371. }
  372.  
  373. Vector3D VectorProjection(Vector3D a, Vector3D b) //proj a on b    
  374. {
  375.     Vector3D projection = a.Dot(b) / b.LengthSquared() * b;
  376.     return projection;
  377. }
  378.  
  379. double VectorAngleBetween(Vector3D a, Vector3D b) //returns radians  
  380. {
  381.     if (a.LengthSquared() == 0 || b.LengthSquared() == 0)
  382.         return 0;
  383.     else
  384.         return Math.Acos(MathHelper.Clamp(a.Dot(b) / a.Length() / b.Length(), -1, 1));
  385. }
  386.  
  387. //Whip's Vector Reflection Method
  388. Vector3D VectorReflection(Vector3D a, Vector3D b, double rejectionFactor = 1) //reflect a over b    
  389. {
  390.     Vector3D project_a = VectorProjection(a, b);
  391.     Vector3D reject_a = a - project_a;
  392.     return project_a - reject_a * rejectionFactor;
  393. }
  394.  
  395. //Whip's PID controller class v4 - 8/27/17
  396. public class PID
  397. {
  398.     double _kP = 0;
  399.     double _kI = 0;
  400.     double _kD = 0;
  401.     double _integralDecayRatio = 0;
  402.     double _lowerBound = 0;
  403.     double _upperBound = 0;
  404.     double _timeStep = 0;
  405.     double _errorSum = 0;
  406.     double _lastError = 0;
  407.     bool _firstRun = true;
  408.     bool _integralDecay = false;
  409.     public double Value { get; private set; }
  410.  
  411.     public PID(double kP, double kI, double kD, double lowerBound, double upperBound, double timeStep)
  412.     {
  413.         _kP = kP;
  414.         _kI = kI;
  415.         _kD = kD;
  416.         _lowerBound = lowerBound;
  417.         _upperBound = upperBound;
  418.         _timeStep = timeStep;
  419.         _integralDecay = false;
  420.     }
  421.  
  422.     public PID(double kP, double kI, double kD, double integralDecayRatio, double timeStep)
  423.     {
  424.         _kP = kP;
  425.         _kI = kI;
  426.         _kD = kD;
  427.         _timeStep = timeStep;
  428.         _integralDecayRatio = integralDecayRatio;
  429.         _integralDecay = true;
  430.     }
  431.  
  432.     public double Control(double error)
  433.     {
  434.         //Compute derivative term
  435.         var errorDerivative = (error - _lastError) / _timeStep;
  436.  
  437.         if (_firstRun)
  438.         {
  439.             errorDerivative = 0;
  440.             _firstRun = false;
  441.         }
  442.  
  443.         //Compute integral term
  444.         if (!_integralDecay)
  445.         {
  446.             _errorSum += error * _timeStep;
  447.  
  448.             //Clamp integral term
  449.             if (_errorSum > _upperBound)
  450.                 _errorSum = _upperBound;
  451.             else if (_errorSum < _lowerBound)
  452.                 _errorSum = _lowerBound;
  453.         }
  454.         else
  455.         {
  456.             _errorSum = _errorSum * (1.0 - _integralDecayRatio) + error * _timeStep;
  457.         }
  458.  
  459.         //Store this error as last error
  460.         _lastError = error;
  461.  
  462.         //Construct output
  463.         this.Value = _kP * error + _kI * _errorSum + _kD * errorDerivative;
  464.         return this.Value;
  465.     }
  466.  
  467.     public void Reset()
  468.     {
  469.         _errorSum = 0;
  470.         _lastError = 0;
  471.         _firstRun = true;
  472.     }
  473. }
Advertisement
Add Comment
Please, Sign In to add comment