I'm trying to make a wheel collider PID controller based on the following tutorial:
https://www.habrador.com/tutorials/pid-controller/1-car-follow-path/
I made a revision to the FixedUpdate method to modify the steer direction based on the angle difference with a spline waypoint (if one is instantiated in the script) and also try to introduce "coasting" or "braking" behavior based on how fast the car is going and how tight the angle of the turn is going to be. Currently, "drifting" sets the wheel friction curve to an alternate forward and sideways friction stiffness value between the wheels. Normally, any car has a sideways stiffness factor of 2 on the wheel collider, like here:
Now, problem is, it's tough to find a balance of ideal PID controller settings and an expected threshold to 1.) brake or coast due to the turning angle possibly spinning out of control 2.) "drift" when the upcoming angle between the two next waypoints ahead of the car makes a tight angle for the car to turn.
Here's my current PID values, honestly I've been tweaking with these quite a bit (which I know to some degree is expected):
Here's the relevant code - FixedUpdate loop:
void FixedUpdate()
{
float motor = carControl.maxForwardTorque;
//Manual controls for debugging
//float motor = carControl.maxForwardTorque * im.throttle;
//float steering = carControl.maxTurn * im.steer;
//
//Calculate the steering angle
//
//The simple but less accurate way -> will produce drunk behavior
//float steeringAngle = maxSteeringAngle * Math.SteerDirection(transform, steerPosition, currentWaypoint);
//Get the cross track error, which is what we want to minimize with the pid controller
float CTE = AIVehicleMath.GetCrossTrackError(steerPosition, previousWaypoint, currentWaypoint);
//But we still need a direction to steer
if(splineWalker)
CTE *= AIVehicleMath.SteerDirection(transform, steerPosition, splineWalker.transform.position);
else
CTE *= AIVehicleMath.SteerDirection(transform, steerPosition, currentWaypoint);
float steeringAngle = PIDControllerScript.GetSteerFactorFromPIDController(CTE);
//Limit the steering angle
steeringAngle = Mathf.Clamp(steeringAngle, -carControl.maxTurn, carControl.maxTurn);
//Average the steering angles to simulate the time it takes to turn the steering wheel
float averageAmount = 30f;
averageSteeringAngle = averageSteeringAngle + ((steeringAngle - averageSteeringAngle) / averageAmount);
Debug.Log("Average steering angle is " + averageSteeringAngle);
//
//Apply everything to the car
//
float signFactor = averageSteeringAngle/carControl.maxTurn;
im.steer = signFactor;
if (Mathf.Abs(averageSteeringAngle) > 15 && rb.velocity.magnitude > 15)
{
im.throttle = 0;
im.brake = true;
}
else if (Mathf.Abs(averageSteeringAngle) > 5 && rb.velocity.magnitude > 15)
{
im.throttle = 0;
im.brake = false;
}
else
{
im.throttle = 1;
im.brake = false;
}
float currDistFromWP = Vector3.Distance(transform.position, allWaypoints[currentWaypointIndex].position);
float speed = rb.velocity.magnitude;
float timeFloatLeft = currDistFromWP / speed;
Vector3 nextVector;
Transform nodeA;
Transform nodeB;
if (currentWaypointIndex == allWaypoints.Count - 1)
{
nodeA = allWaypoints[currentWaypointIndex];
nodeB = allWaypoints[0];
}
else
{
nodeA = allWaypoints[currentWaypointIndex];
nodeB = allWaypoints[currentWaypointIndex + 1];
}
float distanceBetweenNextTwo = Vector3.Distance(nodeA.position, nodeB.position);
float angleBetweenTwo = Vector3.Angle(transform.position, nodeB.position);
if (currDistFromWP < 20 && angleBetweenTwo > 30)
im.drift = true;
else
im.drift = false;
}
Can anyone give suggestions on how I can approach this more efficiently, maybe? No matter what I do, the car keeps turning in a drunken fashion, even when I limit the speed to something super slow like 10 or 20. And then of course, imagine the car going at a speed of 70 or 90! It's proving to be a bit of a headache for now right now.
Appreciate any suggestions or comments. Thanks again.