Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atmospheric flight #4556

Merged
merged 17 commits into from
Apr 28, 2019
Merged

Atmospheric flight #4556

merged 17 commits into from
Apr 28, 2019

Conversation

WKFO
Copy link
Contributor

@WKFO WKFO commented Mar 7, 2019

Adds simple atmospheric flight capabilities to ships. It might not be as good as X-Plane or DCS right now, but it can easily win against Star Wars physics. I had a lot of more work in my mind, but if I tried to implement everything I wanted, I couldn't open this PR before I got bored of the idea already. So instead of trying out complex formulas, I nerfed everything down to their simple and safe forms. (Less complex, less likely to break the game, can actually be reviewed and merged before the sun goes supernova)

Most people can understand the code at first look (I took my time writing descriptive variable names), but here is a more human-friendly summary of the simplest flight dynamics I came up with:

  • Atmospheric lift

  • Passive aerodynamic control -> ship tends to go where you point it at

  • Aerodynamic stability -> ship tends to rotate nose-first into the velocity vector

I'm not an expert on the subject, so if there is something wrong, don't hesitate to yell.

Some experimenting is needed to find viable ship constants for aerodynamically-designed ships. Give me some time and I can come up with those values.

Some bits I might need to explain:

  • Atmospheric torque is not applied below 150 m/s because although it is not unrealistic, it could be a bit too annoying.

  • Yes, you can scoop fuel. This even makes it a bit easier if you know what you are doing.

  • Drag is calculated by multiplying the same things (density, speed squared) you do when calculating lift, so there is nothing wrong with that part.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 7, 2019

@bszlrd
Copy link
Contributor

bszlrd commented Mar 7, 2019

That's an interesting method. I just flattened three copies of the deneb and cleaned them up to a single ngon. Tht method wasn't quick though, but also it did not need the 3d printing addon, which is still broken in 2.8 I think.

@mike-f1
Copy link
Contributor

mike-f1 commented Mar 8, 2019

I would note there are already some calculations of these forces (specifically "Drag"):

pioneer/src/DynamicBody.cpp

Lines 190 to 204 in 169f954

double DynamicBody::CalcAtmosphericForce(double dragCoeff) const
{
Body *body = GetFrame()->GetBody();
if (!body || !GetFrame()->IsRotFrame() || !body->IsType(Object::PLANET))
return 0.0;
Planet *planet = static_cast<Planet *>(body);
double dist = GetPosition().Length();
double speed = m_vel.Length();
double pressure, density;
planet->GetAtmosphericState(dist, &pressure, &density);
const double radius = GetClipRadius(); // bogus, preserving behaviour
const double area = radius;
// ^^^ yes that is as stupid as it looks
return 0.5 * density * speed * speed * area * dragCoeff;
}

..:Just to get you aware of that, in case you didn't know that already

@impaktor
Copy link
Member

impaktor commented Mar 8, 2019

Sweet!

So instead of trying out complex formulas, I nerfed everything down to their simple and safe forms. (Less complex, less likely to break the game, can actually be reviewed and merged before the sun goes supernova)

Yes, this is exactly how I/we prefer. Start simple, then iterate improvements. For compilcated PRs, chance is too great the author's interest wanes, and it just goes no where.

Atmospheric torque is not applied below 150 m/s because although it is not unrealistic, it could be a bit too annoying.

Maybe have a sigmoid (or tanh) that successively turns this on/off, instead of a hard limit? I'm not suggesting it for this PR, but for your next / keep in mind. Note: I don't know what I'm talking about, and I haven't tried to see if I notice anything happening at 150+/-1, so if it doesn't make sense, please ignore.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 8, 2019

Atmospheric torque is not applied below 150 m/s because although it is not unrealistic, it could be a bit too annoying.

Maybe have a sigmoid (or tanh) that successively turns this on/off, instead of a hard limit? I'm not suggesting it for this PR, but for your next / keep in mind. Note: I don't know what I'm talking about, and I haven't tried to see if I notice anything happening at 150+/-1, so if it doesn't make sense, please ignore.

The force is so weak at 150 m/s that I don't actually think I needed that. I wanted to put it there just to be safe, out of paranoia. But I think you have a pretty good point, there must be a better way instead of hard coding the value.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 10, 2019

I added the ship-specific values to the *.json files. I think I've tested them all, I think most of them can be rated "not bad" at this state.

This PR has everything I want to implement for now.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 10, 2019

I've tweaked the values so that most aerodynamically-designed ships are best flown at high speed cruise. (At around 2 km/s to 7 km/s in Earth's skies. For reference, the cruise speed of a 737 is around 300 knots, which is only about 155 m/s.)

Ships which can produce lift are Wave, Deneb, AC33 Dropstar, Venturestar, and Lodos. By far, Wave produces the most proportional lift among all the vessels. (The ship itself is a wing with an engine strapped on below.)

I must say that our atmosphere generation is not very good. They are too thin.

@sturnclaw
Copy link
Member

@WKFO

I must say that our atmosphere generation is not very good. They are too thin.

How so, and at what height are you talking? IIRC most atmospheres have 90% of their density near the bottom 20% of height, and it drops off fairly quickly after that.

@bszlrd
Copy link
Contributor

bszlrd commented Mar 10, 2019

Cool! I'v just tried it, and it feels like you are actutally in an atmospehere.
I noticed that the thrusters are lighting up (on the wrong side even), as the ship turns into the flight direction. (I'm not touching the controls. I'd guess that's not intentional.
screenshot-20190310-221348

@WKFO
Copy link
Contributor Author

WKFO commented Mar 10, 2019

@Web-eWorks I meant "thin" as in maximum height. For example the Earth's atmosphere could "start" at around 70 - 100km altitude instead of 20km. No matter how thin (in means of density) the atmosphere is, it makes a lot of difference at orbital speeds.

For reference, most of the US space shuttle's reentry phase is considered to begin around 120km altitude, and the dramatic heating and deceleration happens mostly above 20km before the shuttle commander takes control. https://www.nasa.gov/centers/johnson/pdf/584730main_Wings-ch4d-pgs226-241.pdf

@sturnclaw
Copy link
Member

sturnclaw commented Mar 10, 2019

@WKFO do you mean 'low' instead of 'thin'? Thin refers to the density, while low refers to the height at which the atmosphere starts. I do agree that we need to move the upper-bound of the atmosphere up if it's at 20km, probably to the Kármán line at 100km.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 10, 2019

@Web-eWorks Oh yes, I meant "low". Sorry for the confusion.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 10, 2019

@nozmajner I think it is the ship trying to compensate for the atmospheric torque.

@sturnclaw
Copy link
Member

@WKFO Your implementation is subtly incorrect, mostly due to the fact that drag is internally calculated with the radius of a sphere for area because reasons. The other part lies in the fact that some of the values you're applying to the generated drag scalar have no root in physical properties and are completely arbitrary (DEFAULT_LIFT_TO_DRAG_RATIO and mAoAMultiplier).

However, it's good enough to fool the eye and worth merging anyways. @impaktor consider this my endorsement of merge.

I've got a fixup PR in testing, but I'd rather WKFO get the credit for the initial implementation.

@impaktor
Copy link
Member

impaktor commented Mar 12, 2019

I should read the IRC backlog from yesterday, but seems like @jaj22 raised some valid points:

  • now ships will be under forces the AI doesn't know about
  • i.e. autopilot / NPC ships will be wonky when under high time acceleration

I assume this leads to problems with mission ships not docking as they should, or similar.

consider this my endorsement of merge.

Yeah, we iterate improvements once merged, with input from those in the loop, e.g. original author, and who ever feels like it

EDIT Backlog:

<JohnJ_> That patch will definitely break the autopilot at higher time accel 
         values.
<impaktor> JohnJ_: any way around it / any fix? 
<JohnJ_> In principle yes, although anything that can generate extremely large 
         instantaneous lateral forces is problematic. 
<JohnJ_> There are ordering issues that can be resolved.
<JohnJ_> The two basic rules are that the autopilot needs to know (accurately) 
         about external	forces in advance, and those external forces can't 
         change rapidly
<JohnJ_> With an incremental simulation at 10000x, atmospheric forces do 
         change	rapidly, so there's a fudge in dynamicbody.cpp (IIRC) to limit 
         that. Whether that's also sufficient to handle lateral forces, I'm not 
         sure. 

@WKFO
Copy link
Contributor Author

WKFO commented Mar 12, 2019

Well, I could use some guidance on what to do about that. I know don't know much about time accel and the autopilot behaviour other than the "field tests" I've conducted. Maybe I can do something similar to the fudge in dynamicbody.cpp

@fluffyfreak
Copy link
Contributor

@WKFO one option might be to simply disable it, or scale it down massively towards zero effect, at higher time accelerations? We can accept approximations as it gets faster.

@mike-f1
Copy link
Contributor

mike-f1 commented Mar 12, 2019

The other part lies in the fact that some of the values you're applying to the generated drag scalar have no root in physical properties

I would say that there some root beyond that math: @WKFO simply don't convert values to angles...
Then you could find lot of papers over internet speaking about how lift to drag coefficent works.
Also the thing about drag may be easily solved putting some 'evolved' formulas.

But apart from math stuffs, I would say that I feel like this PR isn't completed because I see, as a natural step further, a little refactoring.
IMHO You should move all that thing in a separate class with at least a ctor in which you store data parsed from shiptype, and an uodate in which you pass a DynBody. Then you could use that class as a component in ship or whatever.
I would also recomend "update" will return a struct or tuple so you may call it once, and a check in that function against pressure and speed before any more math (below some pressure and speed, return nothing...)
Lastly, you could also use the trick mentioned by @jaj22 to disable updates or the whole component

@sturnclaw
Copy link
Member

@mike-f1 most airplanes have lift-to-drag ratios of 7+, even at suitably hypersonic speeds. I'd rather lift be correct (with respect to the altitude / atmosphere as well) and we properly implement parasitic drag instead.

Also, @WKFO upon further inspection, your PR is blatantly physically incorrect - under your model, the lift forces are highest at 0 AoA, whereas in reality they fall off to 5-10% and are highest around 20 degrees. You're also not taking into account the case in which the nose is below the AOA, which should be generating downward thrust due to wing deflection.

I've got the requisite algorithms mostly re-written, so don't worry too much about fixing it right now.

@mike-f1
I can't say I agree with you about the refactor right now. While it's certainly tempting to consider, I'm going to have to come along later and duplicate all of that work anyways when we fully move to the ECS paradigm. Additionally the DynamicBody "component" system is... the less said about it, the better.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 12, 2019

CalcAtmoLift ONLY calculates lift caused by difference in pressure; total lift is a combination of fDragControl and fLift.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 12, 2019

The thing I did very wrong is that fLift makes the most of the lift instead of fDragControl. fDragControl should be much much more powerful than fLift.

@sturnclaw
Copy link
Member

@WKFO here's a lift graph under my revised flight model. (Lift in this case means force in the local Y axis, including drag at high AoA.) This takes into account the various resources on lift physics I found, including the link you sent me.

flight_lift_model

Green indicates lift from wing pressure/displacement in horizontal flight, red indicates drag forces acting on the Y axis, and blue indicates "lift efficiency", or the lift force divided by the overall loss of velocity.

The "peak lift" point can be adjusted with the AoA falloff (e.g. if 25 or 15 degrees is better), while the drag contribution equation is strictly physically accurate, as I understand it.

(I would have sent this to you on IRC, but you kept quitting before I got a chance.)

@WKFO
Copy link
Contributor Author

WKFO commented Mar 17, 2019

I merged two branches into one.

@sturnclaw
Copy link
Member

@WKFO looks pretty good to me. I'll get the other PR dealt with when I get back to my main box.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 17, 2019

@Web-eWorks Are there any other core mechanics you want to modify? Otherwise, I (we?) just need to address the possible issues with autopilot and time acceleration.

Yes, I know I've had objections before, but I'm mostly satisfied with the ships now (not because I changed the ships a lot, my thoughts have changed mostly). I kind of forgot that most real world aircraft were designed to fly below 150 m/s (and the space shuttle was just a flying brick) while our spaceships were flying at multiple km/s airspeed all this time.

For future reference: The autopilot already avoids flying in atmosphere except for the landing, during which the AI comes straight down almost 90 degrees from the sky.

@sturnclaw
Copy link
Member

If the autopilot is landing at 90deg, the new calculations shouldn't affect it at all. I think @jaj22 was going to handle updating the autopilot, but feel free to correct me if I'm wrong.

@WKFO I don't think there's anything else I need to change, though I do want to get better simulation of hypersonics at some point.

@impaktor
Copy link
Member

I think @jaj22 was going to handle updating the autopilot, but feel free to correct me if I'm wrong.

I don't think he's said anything to that effect. He mostly keeps an eye on us, and comes to our rescue when we have difficult bugs.

// TODO: use a different drag constant for each side (front, back, etc).
// This also handles (most of) the lift due to wing deflection.
vector3d fAtmosDrag = vector3d(
CalcAtmosphericDrag(m_lVSqr, m_sideCrossSec, m_sideDragCoeff),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if CalcAtmosphericDrag will return a vector3d and accept as second and third elements a vector3d
then you could avoid to treble calls and code become more readable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think overloading the function to accept vector3d would be best suited in this case, and it'd make things a little easier to read.

src/Ship.cpp Outdated
double m_shipLiftCoeff = GetShipType()->shipLiftCoefficient;

// By converting the velocity into local space, we can apply the drag individually to each component.
auto m_localVel = GetOrient().Inverse() * GetVelocity();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, auto m_localVel = GetOrient().Inverse() * GetVelocity(); is the same as
auto m_localVel = GetVelocity() * GetOrient(); but without the needs of Inverse

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My matrix math admittedly isn't quite up to par, but operator* (Vector<T>, matrix4x4) does a transpose operation, not a matrix inversion. We're specifically converting a vector into local space (the Orient matrix is local-to-world), so the inverse of the world-space orientation matrix is what is required here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rotation/orientation matrices are orthogonal. For an orthogonal matrix, the inverse and transpose are identical. Hence the Inverse() has the same results as Transpose() but is far slower.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the line with what @mike-f1 suggested.

@mike-f1
Copy link
Contributor

mike-f1 commented Mar 18, 2019

I know it seems I'm getting pedantic, but:

pioneer/src/vector3.h

Lines 134 to 144 in 12e5be1

vector3 NormalizedSafe() const
{
const T lenSqr = x * x + y * y + z * z;
if (lenSqr < 1e-18) // sqrt(lenSqr) < 1e-9
return vector3(1, 0, 0);
else {
const T l = sqrt(lenSqr);
return vector3(x / l, y / l, z / l);
}
}

So, the use of this function should be reserved only when there aren't alternative.

And this is not the case (IMO), because when velocity decrease then all aerodynamic things can be
thrown away as they became small enought to be trashed.

Second: I don't think it is good to postpone something "good" because there's something
"big" that is expected to change things: even more so because in this case for an "ECS" you
should then start refactoring and grouping...

Third: I note a lot of "jump" between function in different file (from Ship to DynBody then back),
it make difficult understand code, at least for me...

Anyway, to me is not mandatory to implement these changes or the last comments I did
on code. I'm just suggesting for future reference.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 24, 2019

I had to force push my last commit because I screwed something up in my local repo. Nothing to worry about.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 24, 2019

The last commit fixes the increasing atmospheric torque on the craft when in time accel.

@impaktor
Copy link
Member

Feels like this PR might need some serious rebasing, and clean up. Prefereably, all "fixes" should be mered into the commits they fix/tweak.

@sturnclaw
Copy link
Member

@WKFO I've got a couple of tweaks I want to make to the PR, then we should be ready for squash+merge.

@WKFO
Copy link
Contributor Author

WKFO commented Mar 29, 2019

@Web-eWorks Okay.

@WKFO
Copy link
Contributor Author

WKFO commented Apr 6, 2019

@Web-eWorks Asking just out of curiosity, what sort of tweaks do you have in mind?

@sturnclaw
Copy link
Member

Asking just out of curiosity, what sort of tweaks do you have in mind?

I want to go through each ship and set them up with appropriate drag and lift constants. I've already done a cleanup pass on the new code to finalize comments and optimize the function (early out when no velocity, etc.).

Unfortunately, it looks like I'm not going to be able to get to that before next Sunday at the very earliest, so feel free to merge this PR and I'll come along later with the final tweaks in a separate PR.

@fluffyfreak
Copy link
Contributor

I would prefer to wait until thing were tweaked before merging this PR.
Don't get me wrong, it's a great PR already, but if we know that there's a few more pieces coming soon (-ish) then I say we wait for them and everything goes in together.

We don't have a "release date" to hit or publishers to molify just ourselves :)

@impaktor
Copy link
Member

I would prefer to wait until thing were tweaked before merging this PR.

I don't necessarily agree with that, we have too long history of getting PRs stuck in perfection-limbo.

...however, the messy commit history is making me cold-sweat.

(yes, I'm aware of the inherent contradiction of my two statements)

@laarmen
Copy link
Contributor

laarmen commented Apr 16, 2019 via email

@fluffyfreak
Copy link
Contributor

Damn perfection, merging

@fluffyfreak fluffyfreak merged commit d4bee3b into pioneerspacesim:master Apr 28, 2019
@WKFO WKFO mentioned this pull request Mar 29, 2020
@starling13
Copy link
Contributor

starling13 commented Aug 23, 2020

Hello. I think there is a little mistake in calculations of the drag basis components.

This is current implementation:

...
auto m_lVSqr = m_localVel.LengthSqr();

// The drag forces applied to the craft, in local space.
// TODO: verify dimensional accuracy and that we're not generating more drag than physically possible.
// TODO: use a different drag constant for each side (front, back, etc).
// This also handles (most of) the lift due to wing deflection.
vector3d fAtmosDrag = vector3d(
	        CalcAtmosphericDrag(m_lVSqr, m_sideCrossSec, m_sideDragCoeff),
	        CalcAtmosphericDrag(m_lVSqr, m_topCrossSec, m_topDragCoeff),
	        CalcAtmosphericDrag(m_lVSqr, m_frontCrossSec, m_frontDragCoeff));

// The direction vector of the velocity also serves to scale and sign the generated drag.
	fAtmosDrag = fAtmosDrag * -m_localVel.NormalizedSafe();
...

Here we use whole vector squared length to calculate each component, then scale by normalized airspeed vector which seems incorrect.

I propose this change:

// Get current atmosphere parameters
double pressure, density;
GetCurrentAtmosphereParameters(pressure, density);

// Precalculate common part of drag components calculation (rho / 2)
const double rho_2 = density / 2.;
// Vector, which components are square of local airspeed vector components
const vector3d lv2(m_localVel * m_localVel);
vector3d fAtmosDrag(
	       -sign(m_localVel.x) * rho_2 * lv2.x * m_sideCrossSec * m_sideDragCoeff,
	       -sign(m_localVel.y) * rho_2 * lv2.y * m_topCrossSec * m_topDragCoeff,
	       -sign(m_localVel.z) * rho_2 * lv2.z * m_frontCrossSec * m_frontDragCoeff);

(I'v also added method GetCurrentAtmosphereParameters to class DynamicBody.)
Next I place the debug output with comparision of old and new drag calculation.
I used sinonatrix on Mars with modified drag coefficients.

Values from one time step:

Cross section: 40 97 26
Drag coefficients: 0.4 1.1 0.2
Local velocity: 0.143433 -0.35287 -332.503
Pressure: 0.39416 Density: 0.476633
Old drag:  -181.853        2983.52  137009
New drag: -0.0784467    3.16627  137009

Look at the huge side and top drag of -181.853 and 2983.52 N with much less then 1 m/s airspeed on that axes in old value. I think new values are more realistic.

@impaktor
Copy link
Member

@starling13 How does the ship handle under the new change? So head-on drag is unchanged? Then I assume this is a corner case, thus it not having been noticed?

ping @WKFO, thoughts?

(if no further response on this is made, within the next few days, you can open a PR and we take the discussion there)

(also bonus: if you get the drag debug output into the debug Imgui window, hmm, maybe even as graphs, that would be cool)

@WKFO
Copy link
Contributor Author

WKFO commented Aug 24, 2020

Glancing at it, @starling13 's change looks correct to me. Head-on drag should stay unchanged, but the ships' response to Angle-of-Attack would change.

I would like to see a PR.

@starling13
Copy link
Contributor

I'll do PR in short time.

also bonus: if you get the drag debug output into the debug Imgui window, hmm, maybe even as graphs, that would be cool

I afraid that I'm not that cool at the moment, but it's worth to try :)

@sturnclaw
Copy link
Member

There are sadly still a number of hurdles to cross (mostly related to newUI / oldUI) before debug visualization in C++ is possible - mostly because opening a new Imgui frame before input handling happens has issues. It's on my backlog...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants