Making a cruise control system for a vehicle

Hi, I’m currently struggling on how to make a cruise control system for a car.

I’m using A-Chassis 6.52S by Inspare.

I want to be able to achieve and maintain a set speed, I already have the values set up for changing the values. There are values for the throttle and the brake named _GThrot and _GBrake, these can be changed anywhere throughout the script and I’m not sure if I’ll require a ton of math for being able to seamlessly change the speeds and have the car accelerate / brake to achieve said speed.

The maximum and minimum values of the _GThrot and _GBrake are 0 and 1.

If anybody has any hints or tips could you please share them?

Thanks!

5 Likes

Just run a loop when the CC is active to accelerate slowly when the vehicle’s speed is below the target speed.

Set target speed to the vehicle’s current speed when the user activated CC.

If the user manually accelerate or brakes, deactivates CC

Would there be a way to add some form of gradual increase in the speed until it’s reached the target speed? I’ve tried the way you described however when it exceeds the target speed I apply the brakes and it goes below and then it’s just a constant cycle of accelerating and decelerating quickly.

You don’t want to initiate the brake for the user, that will give you kangaroo petrol, so to speak.

No just make it so the car accelerate Only when you’re below the target speed.

As for gradual acceleration, I don’t know how that system works, perhaps set the throttle to 0.5 or lower?

I had a look at this and there’s a lot of math that I don’t understand: https://www.youtube.com/watch?v=zWuwjCuDAEc

A basic rundown of the video is that it uses math to find the best and most efficient way to accelerate the vehicle in a gradual manner, rather than accelerating too fast or too slow to the target speed.

However, I don’t know how to implement such a mathematical system.

If you want to be able to control your acceleration in a parameter-defined manner, you would probably be looking for Motion Control Profiles.
momcp_1

(just look at the one on the right). This would allow you to control your desired acceleration and maximum velocity to reach some desired position. The motion profile will then give you velocities/accelerations with respect to time, which you can just feed into your car’s speed controller (̶p̶r̶o̶b̶a̶b̶l̶y̶ ̶j̶u̶s̶t̶ ̶a̶ ̶s̶i̶m̶p̶l̶e̶ ̶H̶i̶n̶g̶e̶ ̶m̶o̶t̶o̶r̶ ̶t̶h̶i̶n̶g̶y̶ ̶t̶h̶a̶t̶ ̶R̶O̶B̶L̶O̶X̶ ̶g̶i̶v̶e̶s̶,̶ ̶w̶h̶i̶c̶h̶ ̶i̶n̶t̶e̶r̶n̶a̶l̶l̶y̶ ̶r̶e̶g̶u̶l̶a̶t̶e̶s̶ ̶V̶e̶l̶o̶c̶i̶t̶y̶ ̶t̶h̶r̶o̶u̶g̶h̶ ̶s̶o̶m̶e̶ ̶r̶a̶n̶d̶o̶m̶ ̶P̶D̶ ̶l̶o̶o̶p̶s̶)̶.̶ ̶B̶y̶ ̶f̶o̶l̶l̶o̶w̶i̶n̶g̶ ̶t̶h̶e̶ ̶v̶e̶l̶o̶c̶i̶t̶y̶ ̶p̶r̶o̶f̶i̶l̶e̶,̶ ̶y̶o̶u̶ ̶w̶i̶l̶l̶ ̶e̶n̶d̶ ̶u̶p̶ ̶a̶t̶ ̶y̶o̶u̶r̶ ̶d̶e̶s̶i̶r̶e̶d̶ ̶p̶o̶s̶i̶t̶i̶o̶n̶.̶ ̶W̶e̶ ̶c̶a̶n̶ ̶v̶a̶l̶i̶d̶a̶t̶e̶ ̶t̶h̶i̶s̶ ̶b̶y̶ ̶i̶n̶t̶e̶g̶r̶a̶t̶i̶n̶g̶ ̶t̶h̶e̶ ̶v̶e̶l̶o̶c̶i̶t̶y̶ ̶g̶r̶a̶p̶h̶ ̶o̶r̶ ̶d̶o̶u̶b̶l̶e̶ ̶i̶n̶t̶e̶g̶r̶a̶t̶i̶n̶g̶ ̶t̶h̶e̶ ̶a̶c̶c̶e̶l̶e̶r̶a̶t̶i̶o̶n̶.̶)

Seeing as you control the throttle, you would need some extra-steps to have your desired velocity actually executed by the car. In the ideal world, assuming throttle = force, you could just use F=ma, and plug in your acceleration and car mass. If there is no friction and your forces are perfectly replicated, this would ideally have your car follow your exact desired velocity/positions. However, your car probably has friction, drag, and random physics issues related with ROBLOX forces. To counteract these, you would use closed-loop control in conjunction with your estimated force given by f=ma. Given that this is a 1st order system, a simple P or PD loop should do the trick.

If you’re looking to make a cruise control similar to how real vehicles function, you’ll want to research something called a PID loop. They’re used a lot in the real world when automatic control of physical things is required (cruise control, air conditioning, robotics, etc.).

This graphic is a good representation of PID control, but it’s a little confusing if you don’t know anything about PID, so I’ll try to explain. The basic premise of PID control is that you have some wheel, or valve, or mechanical thing which needs to do something precisely (spin at a certain speed, maneuver to a certain position, etc.). Normally it’s very hard to control that thing reliably. PID theory is the solution to reliable control.

PID Visualized
svg

Imagine you have a motor connected to a fan which cools you off on a hot summer day. Currently, your motor is not spinning, but you want to get the motor spinning so that it will cool you off. To get the motor spinning, you apply an electrical charge to the motor: the more charge you apply the faster it will spin. Your problem, though, is that it’s not easy to control the fan manually. If the fan spins too fast, your hat (which is keeping the sun off your head) will blow away. If the fan spins too slow, you won’t cool off. This is where PID theory comes in.

PID is something known as a control loop, which means that it makes very small adjustments to a measured value (speed, position, etc.) over short intervals (loops) to get achieve a desired value. In this case, you’re trying to get your motor to spin at just the right speed.

The image is a representation of a PID loop. On the left is the term r(t) which stands for the desired value, in this case the perfect fan speed. On the right hand side is the term y(t) which stands for the measured value AKA the current speed of the fan.

A PID loop begins at the first yellow circle on the left. The measured value is subtracted from the desired value to find e(t): the error. After the error is found, three individual operations are performed based on it. In the green rectangle is the proportional operation. The proportional operation multiplies the error by the proportional constant, which is just a number less than 1. I’m going to call the result of the operation P.

In the blue rectangle is the integral operation and is probably the most convoluted (and the operation the video you linked was referring to). Integral is a fancy calculus term for the area between a curve and the x-axis of a graph. In the blue rectangle, the integral is calculated by (1.) Graphing all of the separate errors in the y-axis (remember that we’re “looping” very quickly, so we keep track of each error from the previous loop), and using the x-axis to represent the amount of time which has passed. (2.) Finding the area under the curve formed by the graph. This image is a good way to visualize what I just said. The integral(area under the curve) is then multiplied by a decimal called the integral constant to get a final number that I’ll call I.

The final rectangle, the orange one, represents an operation called the derivative. The derivative is another fancy calculus term which refers to the slope of a curve. Although there are a lot of complicated ways to explain how to find the derivative, but in a simple PID loop the only things that matter are the current error, and the error from the last loop of the PID controller. To find the derivative, you divide the change in error (current - last) by the amount of time between loops. The derivative is then multiplied by a decimal called the derivative constant and creates a number I’ll call D.

The final mathy operation of a PID loop is the easiest to comprehend: you simply add the 3 rectangles together. P + I + D = the power you supply to the fan! The beauty of a PID loop is that because it constantly adjusts the power to the fan, it’s self-regulating.

Hopefully, this is a good introduction to PID theory.

(now I’m going to bed :stuck_out_tongue_winking_eye: )

3 Likes

Keep in mind that PID is quite limited when your plants start to get more complex. For example, if you have an adaptive cruise control system, which keeps your car a certain distance away from another car + keeps a certain velocity, you would need to use some more advanced techniques. While you could cascade multiple PID loops, you would run into problems which make analyze the system via root locus much more difficult. For system which have multiple state-variables and inputs, it would be ideal to use a state-space control model. Furthermore, your PID control loop would be limited and unable to control higher-order systems.

In this application, using just need a PD loop to control your steer your plant towards a desired velocity. Your desired velocities would be calculated via a motion control profile generator, so that you can limit your acceleration and velocity ramping to numbers you control. You would also add in a feed-forward term so that your PD loop is just adjusting for any uncertainties that aren’t modeled.

1 Like

One issue with A-Chassis is that you can’t just set the max speed, or else the easiest option for a cruise control would be force set the throttle to 1, and change the max speed to the cruise speed.

Definitely consider making your own chassis if you want to be more technical.

I also forgot to mention something very important in my post: the reason you don’t see control loops very often in video games.

Because of the simulated nature of video games, when you’re trying to create something like a cruise control system it’s almost always much easier to achieve a constant speed without the need for control loops. A control loop would certainly work, but I wouldn’t be surprised if there is a much easier way to control speed than that.

I use a derivative of the chassis you’re working with and here’s how I’ve had it implemented:

function CruiseControl()
    local HoldSpeed = Vehicle.VehicleSeat.Velocity.Magnitude

    repeat
	    if Vehicle.VehicleSeat.Velocity.Magnitude < HoldSpeed and Vehicle.VehicleSeat.Velocity.Magnitude < MaxSpeed then
		    _GThrot = math.min(((HoldSpeed - Vehicle.VehicleSeat.Velocity.Magnitude) / 10) + (_Tune.IdleThrottle * 2), 0.75)
	    elseif Vehicle.VehicleSeat.Velocity.Magnitude > HoldSpeed + 1 or Vehicle.VehicleSeat.Velocity.Magnitude > MaxSpeed then
		    _GThrot = 0
	    end
	    game:GetService("RunService").Heartbeat:wait()
    until not CC or VehicleSeat.Values.Gas.Value <= 0 or _CGear < 1
    CC = false

    _GThrot = 0
    script.Values.Cruise.Value = 0
end

-where ‘CC’ is a global variable that gets toggled on and off whenever the player toggles cruise control.

Basically, all you need to do is find whatever your _GThrot variable is being set to under normal acceleration conditions and set it to that when your vehicle is under your cruise control and maximum speeds.

12 Likes

Neat way of going about it. Kinda surprised UD doesn’t have a fully kitted out chassis from scratch and instead uses a remixed A-Chassis.

That’s something I’m hoping to remediate over this winter break, I just haven’t had the time to work on putting together something brand new while maintaining everything else.

1 Like

Awesome insight guys thanks for the help, I managed to figure it out by setting the throttle value to a certain point where it won’t let the car accelerate or decelerate. I’ll have a look into that PID theory and implement an adaptive cruise control system to distance my vehicle from other cars and possibly use raycasting to read stop signs and traffic lights.

3 Likes