Active aero that reacts to grip loss before it happens
I've had this idea for a while now, and it stems from back when I first read about a technology called flexfoil (https://www.flxsys.com/flexfoil). I liked this concept, and began thinking about ways to best utilise it on a car, ultimately coming to the idea of a wing that can completely morph its shape. One possible use-case was a "perfect" DRS system that would allow a wing to both align to local flow and reduce its camber to 0. I didn't pursue this option though, as this was both extremely complex, and having "perfect" DRS is probably not ideal for stability. The next series of ideas revolved around flexible diffusers that could respond in 3D shapes to different roll/pitch angles, ride heights, and slip angles. The problem then was the sheer amount of empirical and/or simulation data needed to predict the required shape for the endless combinations of scenarios. Eventually I abandoned the overly-complex wing and diffuser designs that would simply take up too much space and weight, but new ideas to avoid reliance on existing data kept coming. In the end I decided I wanted to try something with live pressure readings.
Most active aero systems on existing cars rely on some combination of sensor outputs for forces, moments, yaw rates, and suspension travel, and combine them with driver inputs to spit out the ideal total downforce and aero balance for that instant. This method has a few limitations though.
It relies on the car entering a state that requires a shift in aero characteristics before making the appropriate changes
It can't consider external, or otherwise unexpected influences, such as:
wake from other vehicles ahead or alongside
wind
aero or bodywork damage
limited capacity to respond appropriately to adverse VD scenarios (driver error, or an unsettled car from bumps, curbs, or crests)
By incorporating live pressure readings, the aero performance itself can be monitored in real-time and respond to its own changes rather than trusting that the car is doing what the onboard computer assumes. It also measures changes due to external factors at the instant they occur, which in many cases will mean the aero can react before the car actually needs it to, rather than reacting to a grip-limiting scenario that is already occuring.
A setup that incorporates this functionality would not need to differ fundamentally from normal active aero. It would work with only one moving wing (or wing flap element), although having adaptability at both the front and rear would allow for better regulation of both balance and total downforce simultaneously.
Installing pressure sensors and pneumatic tubing is also a reasonably easy task, so the mechanical aspect of the system is very achievable. The complexity comes with the control systems, how they respond to different scenarios, and how driveability/predictability is maintained. I'll come back to that.
This concept is also naturally fail-safe, as the active aero will respond not only to the effects of aero damage on downforce and balance, but if the activation of one wing fails, the other adaptive elements on the car will respond accordingly. And if a pressure sensor fails or a tube disconnects, all it takes is a check for frozen or ambient pressure readings, and that sensor can be removed from consideration automatically.
The required number and location of pressure taps on the car can be carefully optimised through CFD, and pilot locations can be tested in a wind tunnel or through track testing. Locations should target areas of high pressure magnitudes (positive or negative), biased towards locations at the front and rear of the car that contribute most to the total pitch/balance moment. Large longitudinal-vertical surfaces could also be considered for calculating yaw moments to enhance prediction of the ideal balance. Here are some example locations:
The wings in particular should have a higher density of sensors due to their significant influence on total pitch moment, and their sensitivity to small changes in angle-of-attack and available total pressure.
Each tap location would then have an associated representative area and moment arm length for processing the pressure readings into a single aero balance estimate.
Reaction to live pressure readings is pretty simple: all the wings need to do is adjust their downforce level to maintain the instantaneous ideal aero balance. This will all be managed through a control loop such as a PID system that reacts to a difference between the current moment estimate, and the ideal moment. The only slight complexity is introduced with the decision of which aero element/s to adjust, and in which direction. The hardest part of the whole implementation would be figuring out what the desired aero balance at any given instant should be, but this is nothing new. There's just more scenarios to consider now, such as the following:
Braking and accelerating
Splitting downforce between front and rear axles depending on brake bias, and the number/location of driven wheels. Braking in particular should favour an increase in total downforce when deciding which element/s to adapt.
Steady state cornering
What is the required total downforce level? If the car is not grip-limited at the current downforce level, then reduce downforce to reduce unnecessary drag (I'm calling this "DRS", but with a sliding scale rather than just being on or off). If imbalance occurs, a balance adjustment can be made by either backing off one element, or increase downforce on another (assuming 2 or more adaptable elements), providing options to independently adjust balance and total downforce as required.
Yaw moment
More grip is required on the front axle for a snappy turn-in, then the grip requirement transitions to the rear throughout the corner until it is maximised when the rear is used to slow the rotation of the car (see the below image). Consideration is also required for blending with braking and acceleration requirements at entry and exit.
Wind
As well as being able to adapt to balance offsets caused by wind, having pressure taps on vertical surfaces means an estimate for yaw moment due to the cross-flow from wind and/or vehicle slip angle can be obtained. This can be subtracted from the required yaw moment calculation during cornering, and the aero balance adjusted accordingly.
Lost or gained downforce
In most cases, it is assumed that the effects of wake, wind, or damage will reduce total downforce, and thus a bias towards aero balance adjustments that increase total downforce would be preferred where possible. In rare cases however, there may be an unexpected increase in downforce, such as a car pulling alongside and creating downwash from its wings. In this case a balance adjustment that reduces the gained downforce (and thus additional drag) is preferred if the car is not in a grip-limited state.
My plan was to run a model-scale test of the idea. This was partly to be a proof-of-concept, but mostly as an excuse to use the wind tunnel again, and to learn the basics of python, serial communications, and basic control loops. I had avoided coding throughout school, and control systems was only added to the engineering degree after I graduated, so I didn't have any experience in either (besides Matlab, R, and Inventor iLogic, if you can call them coding).
As I mentioned in Project 04, I wanted a cool-looking model to set up the active aero on, even though the size of the wind tunnel meant the model would be too small to achieve flow characteristics even remotely similar to a full-size car. Given I was going to be dealing with ultra-low Reynolds numbers anyway, I decided I'd limit the scale even further based on my 3D printer bed size, so I only had to print two parts. The main considerations then were having sturdy mounts for the wings, and a place to install some servo motors to actuate them.
The rear wing was a simple, flat, single elements, and the front adaptive element was a flap sitting immediately behind the front wing/splitter. Given flow attachment on suction surfaces was potentially non-existent given the low Reynolds number, I attached all the static taps to/near positive pressure surfaces.
Here's the bare model:
And here are various shots of the car with wiring and tubing attached (4 of the 5 images are taken in the wind tunnel). The front extraction openings and body undercut made for very convenient cable-routing options. Not so ideal was the position of the rear servo motor, which needed to sit slightly lower for a better connection to the rear wing, and allow the wires to exit underneath the body. The black/silver object seen under the car is the load-cell on the underside of the wind tunnel, which I didn't use for these tests.
Static pressure tap locations are shown below. The three at the rear all move with the rear wing, while the three at the front surround the flap element without hindering its actuation.
There was no reason why the real-time pressure aspect of thie idea wouldn't work, so it didn't really need a proof of concept. It's more of a demonstration. For that reason, I wanted to include a couple of settings in the control logic loops that would emulate different VD requirements such as adjusting the ideal yaw moment or the level of grip-limitation via an adjustable "DRS" system.
The first step though was to connect everything up and get it all working and talking to each other.
First contact was me interfacing directly with the microcontroller software, dragging the positional sliders. For $5 servos, they have pretty good response and no noticeable delay or backlash. The low price was really only reflected in the limited torque which was of no issue here, and perhaps a shorter lifespan.
The next step was attaching the wings, connecting wires, and correlating servo positions to the required wing angles.
Conveniently, thanks to the adjustable arm length on the servos, the ideal motino range for both front and rear wings was achieved with equal servo actuation angles. The ideal maximum actuation angle of the front wing was less than the rear, as the front wing was surrounded by bodywork, meaning it had the potential to create an almost perfect seal if actuated too far. This meant it was much more sensitive to a.o.a..
Luckily, this was much easier than I was expecting. Opening the connection was a single line of code, and sending commands was equally simple thanks to the existence of a python function library for the micro controller. The first script stepped each servo through its wing-motion range in 2 degree steps every half second, testing communication, angle conversion, and giving me time to react and stop the code if something went wrong.
Thankfully this was equally as simple. The only additional complexity was having to parse the incoming serial values into individual values for each sensor. The microcontroller on the PCB outputs at a baud of 1,000,000, hence the decision to use a laptop to control everything. A cheap Arduino might not have cut it, and I wasn't going to buy a more capable one just to have a bit more portability. Being able to adjust code on the fly was a big plus anyway.
To combat noise, the incoming data for each sensor was averaged over a short period (initially 0.025s) before converting to a "moment" value. The averaging window had to be kept as small as possible to maintain the advantage of this pressure-based system (that is, "instant" response to required balance shifts).
while True:
values_ch11 = []
values_ch12 = []
values_ch13 = []
values_ch14 = []
values_ch15 = []
values_ch16 = []
start_time = time.time()
#Get rid of any data that's built up (should happen naturally but just to be safe)
ser.reset_input_buffer()
#Gather Pressure Data
while (time.time() - start_time) < avg_window_slider_val:
if ser.in_waiting:
raw = ser.readline().decode('ascii', errors='ignore').strip()
values = raw.split(',') #get individual serial channel values
if len(values) = 20: #ignore lines with any missing data
values_ch11.append(int(values[11]))
values_ch12.append(int(values[12]))
values_ch13.append(int(values[13]))
values_ch14.append(int(values[14]))
values_ch15.append(int(values[15]))
values_ch16.append(int(values[16]))
#Process Pressure Data
avg_F = (sum(values_ch11)+sum(values_ch12)+sum(values_ch13)) / (len(values_ch11)*3)
avg_R = (sum(values_ch14)+sum(values_ch15)+sum(values_ch16)) / (len(values_ch14)*3)
momF = avg_F*lvrF
momR = avg_R*lvrR
#Single moment levers for all senors, as distance from COG is equal
For reference, this is the raw output of the pressure pcb with me gently pressing on the ports of each of the 6 sensors being used. Noise levels are effectively non-existent from the sensors themselves, with the only significant noise coming from actual fluctuations in air pressure. Whether this is "true" pressure transience or just due to disturbance at the static port itself is currently unknown.
At this point I sat down to learn about PID controllers. It turns out there wasn't anywhere near as much to learn as I thought, as it's just a matter of taking P, I, and D, which are all simple calculations, and adding them together.
I set up the main control loop with proportional and integral components, with a small dead zone inside which P was automatically set to 0. Throughout the testing, the gain for "P" (kp) was gradually increased to improve system response until it was almost an on-of-off reaction, limited only by the maximum speed of the servo. Disabling kp altogether led to some scenarios where the wing would get stuck in a constant see-saw of overshooting, so keeping a finite kp was still necessary. The purpose of including the "I" component was to allow smooth fine-adjustment of the wing angle when in the dead zone, avoiding the sudden jumps and jitters that would result from keeping kp active. Outside of the dead zone, the effect of I was negligible compared to P.
A finite value of kp and ki meant the response rate of the wings were effectively a function of airspeed, as lower moment offsets are expected at slow speeds (hence a lower value of P + I), and a larger angle delta is required to counteract them, both resulting in slower response. The simplest solution is to include a multiplier for P and I that is inversely dependent on the square of airspeed.
A derivative component wasn't necessary, at least in this small scale implementation where servo torque was sufficient to start and stop wing motion within only a few data updates/cycles.
#Set up Proportional control
ERR = (momF-momR) - 500*(YawMom_slider_val) - 185 #Nm (Last value required tuning in wind tunnel prior to testing)
Kp = Kp_slider_val
if abs(ERR) <= E_window_slider_val:
P = 0
else:
P = Kp * ERR
#Set up Integral control
now = time.time()
I_window.append((now, ERR))
while I_window and now - I_window[0][0] > I_window_slider_val: #check age of value/position 0 (time of oldest sample) against I window
I_window.popleft() #delete old data outside current window
I_sum = sum(e for n, e in I_window)
Ki = Ki_slider_val
I = Ki * I_sum
#Implement PI control
control_function = P+I
#Adjust Target Angles
delta = max(-step_max_slider_val, min(control_function, step_max_slider_val))
The resulting value of P+I was then fed into a list of logical statements to determine which wing should move the calculated amount, and in which direction. Note that in the code below, "moments" are given units of Nm, but they are not really moments nor are they in Nm, as there was no conversion from serial data to pressures and forces, as this was superfluous for the model-scale experiment.
if delta > 0:
if angle1 > a_min_DRS and angle0 == a_min:
angle1 = max(angle1 - delta, a_min_DRS) #Decrease angle1 by delta, from a_max towards a_min_DRS
elif angle1 == a_min_DRS and angle0 != a_min_DRS:
angle0 = min(angle0 + delta, a_min_DRS) #Increase angle0 by delta, from a_min towards a_min_DRS
elif angle1 <= a_min_DRS and angle1 != a_min and angle0 == a_min_DRS:
angle1 = max(angle1 - delta, a_min) #Decrease angle 1 by delta, from a_min_DRS towards a_min
elif angle1 == a_min and angle0 >= a_min_DRS:
angle0 = min(angle0 + delta, a_max) #Increase angle0 by delta, from a_min_DRS towards a_max
else: #should only occur when lateral acceleration/DRS target is changed
if angle0 > angle1:
angle1 = a_min_DRS
angle0 = max(a_min_DRS-(angle0-angle1), a_min)
elif angle0 < angle1:
angle0 = a_min_DRS
angle1 = max(a_min_DRS-(angle1-angle0), a_min)
else:
angle0 = a_min_DRS
angle1 = a_min_DRS
elif delta < 0:
if angle0 > a_min_DRS and angle1 == a_min:
angle0 = max(angle0 + delta, a_min_DRS) #Decrease angle0 by delta, from a_max towards a_min_DRS
elif angle0 == a_min_DRS and angle1 != a_min_DRS:
angle1 = min(angle1 - delta, a_min_DRS) #Increase angle1 by delta, from a_min towards a_min_DRS
elif angle0 <= a_min_DRS and angle0 != a_min and angle1 == a_min_DRS:
angle0 = max(angle0 + delta, a_min) #Decrease angle0 by delta, from a_min_DRS towards a_min
elif angle0 == a_min and angle1 >= a_min_DRS:
angle1 = min(angle1 - delta, a_max) #Increase angle1 by delta, from a_min_DRS towards a_max
else: #should only occur when lateral acceleration/DRS target is changed
if angle0 > angle1:
angle1 = a_min_DRS
angle0 = max(a_min_DRS-(angle0-angle1), a_min)
elif angle0 < angle1:
angle0 = a_min_DRS
angle1 = max(a_min_DRS-(angle1-angle0), a_min)
else:
angle0 = a_min_DRS
angle1 = a_min_DRS
else:
if angle0 > a_min_DRS and angle1 == a_min:
pass
elif angle0 == a_min_DRS and angle1 != a_min_DRS:
pass
elif angle0 <= a_min_DRS and angle0 != a_min and angle1 == a_min_DRS:
pass
elif angle0 == a_min and angle1 >= a_min_DRS:
pass
else: #should only occur when lateral acceleration/DRS target is changed
if angle0 > angle1:
angle1 = a_min_DRS
angle0 = max(a_min_DRS-(angle0-angle1), a_min)
elif angle0 < angle1:
angle0 = a_min_DRS
angle1 = max(a_min_DRS-(angle1-angle0), a_min)
else:
angle0 = a_min_DRS
angle1 = a_min_DRS
Visual summaries of the above code are included below after introduction of the UI and its controls.
The UI consists of controls for both emulation of different vehicle scenarios, and for tuning the control system response.
Control system sliders:
kp
ki
"Damp Zone" (error window within which kp is set to 0)
"Averaging Window" (the time period over which serial pressure data is collected and averaged for each update cycle
"Integral Window" (the time period over which values of ERR are collected and summed to determine the value of I)
Vehicle scenario sliders:
"Yaw moment target" (unitless multiplier to adjust the instantaneous aero balance)
ERR = (momF - momR) - 500 * YawMom_slider_val
"DRS active level" (unitless multiplier for the "neutral" wing angle to adjust the overall total downforce target)
a_min_DRS = a_min + (a_max - a_min) * DRS_slider_val
Note that in the code, "min" and "max" refer to servo angles, and are inverse to the maximum and minimum wing angles of attack.
I also added a pause button, which reset the wing angles to their maximum angle of attack and set kp and ki to 0. This also doubled as an emergency stop, in case my untested code tried to send a servo too far, risking overloading the motor or breaking a mount.
Also included were live system and variable status plots, operating on a five second moving window.
Current servo angles
It wasn't always easy to visually tell where the wings were in their travel (especially the front), so being able to see this live was very handy
ERR value
Shows a trace of raw moment offset values calculated from the averaged pressure sensor data
P and I plots
Shows the live values of P and I to help with fine-tuning and understanding their different contributions
delta plot
Shows P+I clipped to the servo's maximum speed
The image below summarises the first two top-level logic checks (i.e., when delta !=0). The diagram on the left shows the two wings and their three key angles, and the diagram on the right shows how they are adjusted based on delta, and their current position. Current positions are shown as a red line if the angle exactly equals one of the three key positions, and/or a shaded region which is valid for a range of angles between key positions. The function on the right of each image is the response fed to the servo for either Wing1 (rear) or Wing0 (front).
Currently, the system follows a stepped approach (one wing moves at a time, centred around the neutral angle) as shown above. This works, but is not perfect and relies on quick actuation to avoid noticeable changes to total downforce. A better solution would be to adjust both wings simultaneously if delta exceeds a specific value. This would benefit from the addition of an aero map to estimate the rotation required by each wing to target a reasonable initial guess quickly. Without the step-wise logic though, total downforce needs to be considered more directly, as there would be infinite configurations that would achieve just the target aero balance.
The other thing I'll point out is that with DRS = 0, any adjustment to balance will reduce total downforce. This should still help by creating a more predictable car, but would also allow cars to be designed with very aggressive wings regardless of the track layout (particularly if they are multi-element). This means that there will be a significant amount of time where DRS is non-zero not just on straights but in high speed corners.
Even without such an aggressive wing implementation, for cars with front/rear wings significantly forward/aft of the axles, a reduction in downforce of one of these wings in response to a balance shift will increase load at the opposite end of the car slightly, meaning not only increased stability but also increased usable downforce despite a reduction in total downforce.
After bench-testing to tune everything as well as I could without real pressure values from airflow, all that was left was to put it in the wind tunnel.
I made some tools that I could use to interact with the flow near the wings to trigger pressure differences, which consisted of a piece of wire with two interchangeable attachments: A small circular shape that could be used to interact with individual pressure taps, and a larger rectangle to allow interaction with all three front or rear sensors at once.
Here's the external setup:
The first task was to pick an airspeed. I went with 8m/s, reasoning that since I wasn't going to have realistic flow anyway, I might as well go slow to avoid the risk of detaching cables or wings. The pressure sensors gave very usable data even at this low velocity. Much faster than this and I would have had to secure the tubing and cables more effectively.
Next, I needed to get a value for ERR to calibrate the true neutral value. This was done by setting both wings to their maximum angle of attack and manually subtracting the resulting ERR value from the ERR calculation step in the code. To check that the sensitivity of the pressures to wing changes was suitable, ERR was recorded for each of the 4 extreme wing position combinations, including the range of observed noise ("full front bias" refers to the front wing at max a.o.a., and the rear at min a.o.a.. The opposite applies for "full rear bias"). Luckily, my estimate on how much each wing should be able to move was pretty much perfect, as the same ERR/balance was achieved regardless of whether both wings had zero actuation ("Both Min") or full actuation ("Both Max").
Some noise was present even in the averaged data. Implementation of more pressure sensors may help reduce the noise without needing to slow the update rate. I expect somewhere in the region of 50 pressure readings would be required for reasonable aero balance estimates across a whole full-size high-downforce car. Too few and the system could be detrimental to performance or stability due to inaccurate estimates.
For reference, in all UI videos included below, the range for both Angle0 (front) and Angle1 (rear) is 90-144.
This was the simplest test to perform, as all I had to do was drag the UI sliders up and down.
For DRS adjustments the wings should theoretically follow the shifting neutral-angle target, with occasional small adjustments to suit changes in balance due to their new angles, and general unsteadiness in the airflow. The discrete nature of the UI slider meant the wing angles were constantly catching up and then lagging behind the target value as the slider was dragged, so the response of the wings was quite jagged.
The yaw moment adjustment was even simpler, as it just changed the target ERR value. The effects of each of these within the UI are shown in the video below (DRS first, then yaw moment). The plots also appear more jagged in the UI than the actual data due to their update rate being only about once every three control cycles.
This test consisted of alternately blocking different pressure ports from oncoming air flow and observing the results. By only blocking one at a time, the magnitude of the resulting moment offset was kept relatively low.
This was functionally the same as the small air blocker test, but by blocking airflow to multiple taps at once, the reaction was more substantial which gave more obvious visual changes. The UI recording shows the effect of first blocking the rear wing (three times), then the front wing (twice). Between the adjustments, the position of the blocker still affected the flow unless completely withdrawn from near the car. The effect of this can be seen as general background fluctuations in wing angles, and occasional inverse spikes where the blocker passed above or behind the wing, creating instances of positive pressure.
This is the previous test again, but now with a non-zero DRS value. This meant that the wing affected by the lost downforce had capacity to increase a.o.a..
In the wind-tunnel videos below, the rear wing is blocked and can be seen increasing its angle until the maximum limit, after which the front wing angle decreases. This order is inversed when the blocker is removed, hence the rear wing appears delayed in returning to its original position.
In the front-quarter-view video, the second time the blocker is introduced, it first sits above and behind the wing slightly, increasing pressure on the rear wing and triggering an initial decrease in angle. It then slips off the wing, briefly covering then revealing a small section again, before being steadied into the correct position. The response of the wing to these quick and subtle changes appears instant. My first thought upon seeing this response was it was just bad/delayed, until I noticed exactly how the blocker was moving. The only real delay occurs in the changeover between front and rear actuation.
The UI shows the stepped and inverse response between the front and rear wing angles, and how they both operate about an intermediate "DRS"/neutral value.
The effect of this slider is to offset the target ERR value, altering the preferred balance. The result is that the wings are offset from each other even when no additional influence/disturbance is acting.
Both wings now operate about different non-zero neutral values (offset from each other, and from their maximum a.o.a.). When there's clean airflow, this seems to be a value of around 120 for Angle0, and 100 for Angle1.
Nothing to say here other than I waved the blocker around wildly. This did reveal some slight delay in wing response, but it was still quite impressive, and a torquier motor would improve this further. This level of rapid adjustment is not realistic either, as I'm asking the wing to move through 26 degrees in a tenth of a second or less. In a track scenario, turbulence oscillations would be much less dominant than a large bockage placed immediately upstream of the car, and the wing may actually only need to fluctuate through a few degrees, taking much less time and allowing it to more easily keep up.
While playing around with the DRS and yaw moment sliders towards the end of testing, the wire connecting the front wing to its servo came off. This meant the front wing was stuck at about 75% actuation. This gave an accidental example of the damage-adaption capability of the system: when the DRS and yaw moment sliders are returned to 0 at the start of the recording, the command to the front wing is for max a.o.a. (or at least within a couple of degrees depending on any small corrections required), but it's not getting the pressure it expected and so the rear wing remains backed-off to maintain balance even with the "damage". The pause button functionality is also demonstrated.
To be honest, I have my doubts that this system would actually provide enough benefit in a driver-friendly way to justify installation on a real car. It was a lot of fun to conceive, build, and program though, and as I said at the start, the concept was really a chance to learn a few new things. For a car that already has active aero, it could still be an interesting addition given there's not any retrofitting required other than the pneumatic tubing and sensors (assuming the existing actuation motors are fast enough). Maybe one day I'll have the time and money to make a full-scale test to get a proper conclusion...