--[[ diag_steer2.luau -- WHY does the steering servo not rotate? diag_steering showed TargetAngle -14..-26 deg with CurrentAngle stuck near 0.3 deg. This isolates the cause by removing one load at a time: A. HOISTED -- body anchored, wheels in free air. No tyre load at all. If the servo still will not turn, the fault is the constraint itself, not grip. B. GROUND, STATIC -- resting on the pad, no throttle. C. GROUND, ROLLING -- coasting, no drive torque (drive is what saturates the friction circle). D. TORQUE SWEEP -- is 1e6 actually enough? ]] local ServerScriptService = game:GetService("ServerScriptService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local out = {} local function log(...) local parts = {} for i = 1, select("#", ...) do parts[#parts + 1] = tostring((select(i, ...))) end table.insert(out, table.concat(parts, " ")) end local Shared = ReplicatedStorage:FindFirstChild("Shared") local Cars = require(Shared.Cars) local Chassis = require(Shared.Chassis) local Server = ServerScriptService:FindFirstChild("Server", true) local CarManager = require(Server.CarManager) local pad = Instance.new("Part") pad.Name = "DiagPad2" pad.Size = Vector3.new(4000, 8, 4000) pad.Position = Vector3.new(0, 5000, 0) pad.Anchored = true pad.Material = Enum.Material.Concrete pad.Parent = workspace local padTop = pad.Position.Y + 4 local car = Cars.Get("gtr_r34") local tuning = Chassis.tuningFor(car) local function build() local m = CarManager._buildForDiag(car, tuning) m.Parent = workspace m:PivotTo(CFrame.new(0, padTop + 3, 0)) return m end local function partsOf(model) local servos, motors = {}, {} for _, d in ipairs(model:GetDescendants()) do if d:IsA("HingeConstraint") then if d.Name == Chassis.STEER_SERVO_NAME then table.insert(servos, d) elseif d.Name == Chassis.WHEEL_MOTOR_NAME then table.insert(motors, d) end end end return servos, motors end local LOCK = tuning.MaxSteerAngle log(string.format("MaxSteerAngle %.1f deg | ServoMaxTorque 1e6 | AngularSpeed %.1f rad/s", LOCK, tuning.SteerSpeed)) log("") -- Report the hinge axes once: a mis-set axis would make the servo yaw about -- the wrong vector and look exactly like "no steering". do local m = build() local servos = partsOf(m) for i, s in ipairs(servos) do local a0 = s.Attachment0 local a1 = s.Attachment1 log( string.format( "servo %d axis0 (%.2f,%.2f,%.2f) axis1 (%.2f,%.2f,%.2f) | A0 on %s, A1 on %s", i, a0.WorldAxis.X, a0.WorldAxis.Y, a0.WorldAxis.Z, a1.WorldAxis.X, a1.WorldAxis.Y, a1.WorldAxis.Z, a0.Parent.Name, a1.Parent.Name ) ) end m:Destroy() log("") end -- A. HOISTED --------------------------------------------------------------- do local m = build() local body = m.PrimaryPart m:PivotTo(CFrame.new(0, padTop + 40, 0)) -- well clear of the pad body.Anchored = true local servos = partsOf(m) for _ = 1, 30 do RunService.Heartbeat:Wait() end for _, s in ipairs(servos) do s.TargetAngle = -LOCK end local trace = {} for step = 1, 120 do if step % 20 == 0 then table.insert(trace, string.format("%.1f", servos[1].CurrentAngle)) end RunService.Heartbeat:Wait() end log(string.format("A. HOISTED (no tyre load): target %.1f -> CurrentAngle %s", -LOCK, table.concat(trace, ", "))) log(string.format(" final err %.2f deg", math.abs(servos[1].CurrentAngle - -LOCK))) m:Destroy() end -- B. GROUND, STATIC -------------------------------------------------------- do local m = build() local servos = partsOf(m) for _ = 1, 60 do RunService.Heartbeat:Wait() end for _, s in ipairs(servos) do s.TargetAngle = -LOCK end local trace = {} for step = 1, 120 do if step % 20 == 0 then table.insert(trace, string.format("%.1f", servos[1].CurrentAngle)) end RunService.Heartbeat:Wait() end log(string.format("B. GROUND STATIC: target %.1f -> CurrentAngle %s", -LOCK, table.concat(trace, ", "))) log(string.format(" final err %.2f deg", math.abs(servos[1].CurrentAngle - -LOCK))) m:Destroy() end -- C. GROUND, COASTING (no drive torque) ------------------------------------ do local m = build() local body = m.PrimaryPart local servos, motors = partsOf(m) for _ = 1, 60 do RunService.Heartbeat:Wait() end -- Push to a moderate speed, then cut drive entirely and steer. body.AssemblyLinearVelocity = body.CFrame.LookVector * 60 for _, mo in ipairs(motors) do mo.MotorMaxTorque = 0 -- freewheel: friction circle fully available for cornering mo.AngularVelocity = 0 end for _, s in ipairs(servos) do s.TargetAngle = -LOCK end local trace, yawTrace = {}, {} for step = 1, 150 do if step % 30 == 0 then table.insert(trace, string.format("%.1f", servos[1].CurrentAngle)) table.insert(yawTrace, string.format("%.3f", body.AssemblyAngularVelocity.Y)) end RunService.Heartbeat:Wait() end log(string.format("C. GROUND COASTING @60: CurrentAngle %s", table.concat(trace, ", "))) log(string.format(" yawRate %s", table.concat(yawTrace, ", "))) m:Destroy() end -- D. TORQUE SWEEP (ground, static) ----------------------------------------- log("") log("D. ServoMaxTorque sweep (ground, static, full lock, 1.5 s):") for _, tq in ipairs({ 1e5, 1e6, 1e7, 1e8, 1e9 }) do local m = build() local servos = partsOf(m) for _ = 1, 45 do RunService.Heartbeat:Wait() end for _, s in ipairs(servos) do s.ServoMaxTorque = tq s.TargetAngle = -LOCK end for _ = 1, 90 do RunService.Heartbeat:Wait() end log( string.format( " torque %10.0e -> CurrentAngle %6.1f (err %5.1f)", tq, servos[1].CurrentAngle, math.abs(servos[1].CurrentAngle + LOCK) ) ) m:Destroy() end pad:Destroy() return table.concat(out, "\n")