Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Improved Missile Guidance Script V2 (incorporating critiques)
- - Relative Vector Consistency Addressed
- - Smoothed Target Acceleration Estimation (EWMA)
- - Optional Two-Stage Iterative Prediction
- - Tuned Overshoot Correction
- - Terminal Guidance phase (direct intercept when close)
- ]]
- -- --- Configuration ---
- local MainframeIndex = 0 -- Index of the AI Mainframe providing target data (0 = first mainframe)
- local DetonationRadius = 8.0 -- Proximity fuse radius (meters)
- local MinClosingSpeed = 1.0 -- Minimum closing speed (m/s) to attempt interception. Avoids division by zero/instability.
- local MaxInterceptTime = 20.0 -- Maximum time to predict ahead (seconds).
- -- Prediction Config
- local EnableAPNPrediction = true -- Use target acceleration in prediction
- local AccelFactor = 0.5 -- How much weight to give the acceleration term (0.5 is standard kinematic)
- local MaxEstimatedAccel = 50.0 -- Clamp max estimated acceleration magnitude (m/s^2)
- local EnableAccelSmoothing = true -- Use EWMA to smooth acceleration estimate
- local AccelSmoothingAlpha = 0.1 -- Smoothing factor for EWMA (lower = smoother, more lag; 0.1 suggested)
- local EnableIterativePrediction = true -- Use two iterations for prediction (addresses critique #4)
- -- Overshoot Correction Config
- local EnableOvershootCorrection = true -- Try to correct if missile flies too far past target
- local OvershootMinRange = 75.0 -- Only check for overshoot beyond this range
- local OvershootMinTargetSpeed = 5.0 -- Only check if target is moving reasonably fast
- local OvershootDistanceAhead = 65.0 -- How far ahead of target (along its velocity vector) missile must be to trigger correction (Increased from 50 based on critique #2)
- -- Note on Overshoot: Sensor noise can affect accuracy. This aims back at target if missile seems far ahead along target's path.
- -- Terminal Guidance Config
- local EnableTerminalGuidance = true -- Use direct intercept when very close
- local TerminalGuidanceRange = 35.0 -- Distance (m) to switch to direct intercept. MUST be > DetonationRadius.
- -- --- End Configuration ---
- -- --- Global State ---
- -- Initialize state variables lazily or ensure they exist
- if prevTime == nil then prevTime = 0 end
- if prevTargetVel == nil then prevTargetVel = Vector3(0,0,0) end
- if prevUpdateTime == nil then prevUpdateTime = 0 end
- if smoothedEstimatedTargetAccel == nil then smoothedEstimatedTargetAccel = Vector3(0,0,0) end -- For EWMA
- -- --- End Global State ---
- --- Main Update Function ---
- function Update(I)
- local currentTime = I:GetTime()
- -- Avoid running multiple times in the same physics step or if time hasn't advanced
- if currentTime <= prevTime + 0.001 then return end
- local scriptDeltaTime = currentTime - prevTime
- prevTime = currentTime
- local numTargets = I:GetNumberOfTargets(MainframeIndex)
- if numTargets == 0 then
- -- Reset history if no targets
- prevUpdateTime = 0
- smoothedEstimatedTargetAccel = Vector3(0,0,0)
- return
- end
- local targetInfo = I:GetTargetInfo(MainframeIndex, 0)
- if not targetInfo.Valid then
- -- Reset history if target invalid
- prevUpdateTime = 0
- smoothedEstimatedTargetAccel = Vector3(0,0,0)
- return
- end
- local targetPos = targetInfo.Position
- local targetVel = targetInfo.Velocity
- local targetSpeed = targetVel.magnitude
- -- --- Estimate Target Acceleration (with Smoothing Option) ---
- local rawEstimatedTargetAccel = Vector3(0,0,0)
- local actualDeltaTime = currentTime - prevUpdateTime -- Time since last valid target update
- if EnableAPNPrediction and prevUpdateTime > 0 and actualDeltaTime > 0.01 then
- rawEstimatedTargetAccel = (targetVel - prevTargetVel) / actualDeltaTime
- -- Clamp raw acceleration estimate
- if rawEstimatedTargetAccel.magnitude > MaxEstimatedAccel then
- rawEstimatedTargetAccel = rawEstimatedTargetAccel.normalized * MaxEstimatedAccel
- end
- -- Apply EWMA smoothing if enabled (addresses critique #3)
- if EnableAccelSmoothing then
- smoothedEstimatedTargetAccel = rawEstimatedTargetAccel * AccelSmoothingAlpha + smoothedEstimatedTargetAccel * (1.0 - AccelSmoothingAlpha)
- else
- smoothedEstimatedTargetAccel = rawEstimatedTargetAccel -- Use raw if smoothing disabled
- end
- else
- -- Reset smoothed accel if we don't have a valid delta time or previous velocity
- smoothedEstimatedTargetAccel = Vector3(0,0,0)
- end
- -- Store current values for next frame's calculation
- prevTargetVel = targetVel
- prevUpdateTime = currentTime
- -- --- End Acceleration Estimation ---
- local transceiverCount = I:GetLuaTransceiverCount()
- for trIdx = 0, transceiverCount - 1 do
- local missileCount = I:GetLuaControlledMissileCount(trIdx)
- for mIdx = 0, missileCount - 1 do
- local missileInfo = I:GetLuaControlledMissileInfo(trIdx, mIdx)
- if not missileInfo.Valid then goto NextMissile end -- Skip dead/invalid missiles
- local missilePos = missileInfo.Position
- local missileVel = missileInfo.Velocity
- local missileSpeed = missileVel.magnitude
- -- Calculate vectors FROM target TO missile (addresses critique #1 consistency)
- local relativePos = missilePos - targetPos
- local relativeVel = missileVel - targetVel -- Velocity of missile relative to target
- local range = relativePos.magnitude
- -- *** Order of Checks: Detonation -> Terminal -> Overshoot -> Prediction ***
- -- 1. Check for Detonation Proximity
- if range <= DetonationRadius then
- -- I:Log(string.format("Missile [%d,%d] Detonating. Range: %.1f", trIdx, mIdx, range)) -- Debug
- I:DetonateLuaControlledMissile(trIdx, mIdx)
- goto NextMissile
- end
- -- 2. Check for Terminal Guidance Phase
- if EnableTerminalGuidance and range <= TerminalGuidanceRange then
- -- Aim directly at the target's current position for final approach
- -- I:Log(string.format("Missile [%d,%d] Terminal Phase. Range: %.1f", trIdx, mIdx, range)) -- Debug
- I:SetLuaControlledMissileAimPoint(trIdx, mIdx, targetPos.x, targetPos.y, targetPos.z)
- goto NextMissile -- Skip prediction and overshoot logic
- end
- -- 3. Check for Overshoot Condition (Only if not in terminal phase)
- -- Corrects if missile is significantly ahead of target along target's path
- if EnableOvershootCorrection and range > OvershootMinRange and targetSpeed > OvershootMinTargetSpeed then
- local targetVelNorm = targetVel.normalized
- if targetVelNorm.magnitude > 0.01 then -- Ensure target has a clear direction
- -- Vector from target to missile (already calculated as relativePos)
- -- Project this vector onto the target's velocity direction
- local distanceAhead = Vector3.Dot(relativePos, targetVelNorm)
- -- If missile is further ahead than the threshold, aim back directly at target
- if distanceAhead > OvershootDistanceAhead then
- -- I:Log(string.format("Missile [%d,%d] Overshoot detected (%.1f > %.1f). Correcting.", trIdx, mIdx, distanceAhead, OvershootDistanceAhead)) -- Debug
- I:SetLuaControlledMissileAimPoint(trIdx, mIdx, targetPos.x, targetPos.y, targetPos.z)
- goto NextMissile
- end
- end
- end
- -- 4. Proceed with Predictive Guidance
- if range < 0.01 then -- Safety check (should be caught earlier)
- I:DetonateLuaControlledMissile(trIdx, mIdx)
- goto NextMissile
- end
- -- Calculate closing speed using consistent vectors (addresses critique #1)
- -- Closing speed is component of relative velocity ALONG the line from target to missile
- local closingSpeed = Vector3.Dot(relativeVel, relativePos.normalized)
- -- Check if missile can realistically close the distance
- if closingSpeed < MinClosingSpeed then
- -- Fallback: Aim directly at current target position if cannot close effectively
- -- I:Log(string.format("Missile [%d,%d] Low closing speed: %.1f. Aiming direct.", trIdx, mIdx, closingSpeed)) -- Debug
- I:SetLuaControlledMissileAimPoint(trIdx, mIdx, targetPos.x, targetPos.y, targetPos.z)
- goto NextMissile
- end
- -- --- Prediction Calculation ---
- local predictedInterceptPoint
- -- Iteration 1 (or only iteration if IterativePrediction disabled)
- local timeToIntercept1 = range / closingSpeed
- timeToIntercept1 = Mathf.Min(timeToIntercept1, MaxInterceptTime) -- Clamp prediction time
- local accelTerm1 = Vector3(0,0,0)
- if EnableAPNPrediction and smoothedEstimatedTargetAccel.magnitude > 0.01 then
- -- Standard kinematic prediction: s = ut + 0.5at^2
- accelTerm1 = smoothedEstimatedTargetAccel * (timeToIntercept1 * timeToIntercept1 * AccelFactor)
- end
- local predictedInterceptPoint1 = targetPos + targetVel * timeToIntercept1 + accelTerm1
- if EnableIterativePrediction then
- -- Iteration 2 (Refine TTI based on distance to first PIP) - addresses critique #4
- local rangeToPIP1 = (predictedInterceptPoint1 - missilePos).magnitude
- local timeToIntercept2 = rangeToPIP1 / closingSpeed -- Use same closing speed, but new range
- timeToIntercept2 = Mathf.Min(timeToIntercept2, MaxInterceptTime) -- Clamp again
- local accelTerm2 = Vector3(0,0,0)
- if EnableAPNPrediction and smoothedEstimatedTargetAccel.magnitude > 0.01 then
- accelTerm2 = smoothedEstimatedTargetAccel * (timeToIntercept2 * timeToIntercept2 * AccelFactor)
- end
- predictedInterceptPoint = targetPos + targetVel * timeToIntercept2 + accelTerm2
- else
- -- Use the result from the first iteration if iterative prediction is off
- predictedInterceptPoint = predictedInterceptPoint1
- end
- -- --- End Prediction Calculation ---
- -- Aim the missile at the final predicted point
- I:SetLuaControlledMissileAimPoint(trIdx, mIdx, predictedInterceptPoint.x, predictedInterceptPoint.y, predictedInterceptPoint.z)
- -- Label for goto statement to jump to the next missile
- ::NextMissile::
- end -- End missile loop (mIdx)
- end -- End transceiver loop (trIdx)
- end -- End Update function
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement