API Question Implementing a Key Press/Release to Toggle Surface Friction Coefficients

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
I have been working on an air-breathing jet engine model for Orbiter, and have been using Kev33's Mirage2000 add-on model basically as a test bed to flight test that model. While doing so, I am trying to polish up some of the other functionality of that particular add-on, for self amusement if nothing else.

One thing I am trying to do is enable sensible steering and braking behavior while taxiing, which I know is a challenge with the current API tools. I've found that SetSurfaceFrictionCoeff, SetMaxWheelbrakeForce, and SetNosewheelSteering don't play well with each other at all.

As an example, if you set touchdown points and want to have them behave like a freely rotating wheel, you might set the friction coefficients like this example:

SetSurfaceFrictionCoeff(0.01, 1);

This makes resistance in the longitudinal direction small (rolling wheel), but high laterally (so the wheels don't scrub sideways).

This is all well and good, until you want to apply the brakes or steer. I've done some experimentation with SetMaxWheelbrakeForce, which the API reference says applies a specified brake force in Newtons, but it actually appears to be modified by the longitudinal friction coefficient set by SetSurfaceFrictionCoeff.

The Problems

Braking

So let's say you set the friction coefficients as I did above, and want to specify a braking force of 1000 N. You would think that SetMaxWheelbrakeForce(1000) would deliver 1000 N of braking force when SetWheelBrakeLevel to 1. But what you actually get is 1000 N * 0.01 = 10 N. It's roughly akin to locking up your car brakes on an icy road. So brakes are rendered effective.

Steering

You get the reverse problem when you attempt to use nose wheel steering with the SetNoseWheelSteering function. This applies a lateral steering force to the nose wheel in order to push it left or right based on yaw inputs. But if you specify the surface friction coefficients as I did above to prevent side slipping of the wheels, this actively opposes the nose steering forces. So nose wheel steering is rendered ineffective as well.

Potential Fixes

The braking issue by itself isn't too hard to fix - you could leave the longitudinal friction coefficient alone and just make your braking force 100 times higher (using my example numbers) to deliver the correct braking force. Most folks do this and call it a day.

But that still leaves you with steering problems. If you leave the lateral friction coefficient at 1 to prevent side slipping, it overwhelms the nose steering forces (which, as far as I can tell, are not possible to change or override like the brake forces).

What I am trying to do

So what I am trying to do to beat these issues is to implement differential braking and forgo the use of nose wheel steering, but change the surface friction coefficients when I apply the brakes to enable effective braking and steering. In SetClassCaps I have this entered:

C++:
SetSurfaceFrictionCoeff(0.01, 1.0);
    SetMaxWheelbrakeForce(empty_mass * 2.0 * 9.81); //should provide ~2 G braking acceleration

This sets the friction coefficients to mimic a rolling wheel, and sets the brake force to a sensible nominal value. However, I know this won't work as-is for the above reasons. To fix this, in clbkConsumeBufferedKey I have modified the , and . keys to apply brakes like this:

C++:
    case OAPI_KEY_COMMA: //Apply left wheel brake using Orbiter default brake key
    {
        //Increase longitudinal friction so brakes work, but lower lateral friction so nose turns
        //Need to reset these coefficients when key is release to mimic rolling wheels.
        SetSurfaceFrictionCoeff(1.0, 0.01);
        //Apply the brake
        SetWheelbrakeLevel(1, 1, false);
        return 1;
    }

    case OAPI_KEY_PERIOD: //Apply right wheel brake using Orbiter default brake key
    {
        //Increase longitudinal friction so brakes work, but lower lateral friction so nose turns
        //Need to reset these coefficients when key is release to mimic rolling wheels.
        SetSurfaceFrictionCoeff(1.0, 0.01);
        //Apply the brake
        SetWheelbrakeLevel(1, 2, false);
        return 1;
    }

Note that this increases the longitudinal friction coefficient to 1.0 (to enable full braking force application), but also reduces the lateral friction coefficient (to allow the nose wheel to slide sideways).

This works, but I need to restore the surface friction coefficients to their original values when I release the brake keys. Right now I am brute forcing it by this bit of code in clbkPreStep:

C++:
    if (GroundContact() == true)
    {
        SetSurfaceFrictionCoeff(0.01, 1.0);
    }

This works, but it's obviously clunky as the code has to keep resetting the coefficients each time step, whether needed or not.

The Question


So, the question is, is there a way that I can simply restore the surface coefficients once when I release a key? In pseudo code I'm dreaming for something like this:

Code:
if (key pressed)
    SetSurfaceFrictionCoeff(1.0, 0.01); //set up to brake and steer effectively
    SetWheelBrakeForce(1,1); //I have this bit sorted out
if (key released)
    SetSurfaceFrictionCoeff(0.01, 1.0); //resume free rolling wheel behavior

Is there a way to use the key release as a trigger to resume the normal coefficients like this?
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
Actually, I'm still not sure what the SetBrakeLevel function does. Some simple timed braking tests don't seem to show any change in braking force due to it.

If I set the friction coefficient to 1 and disable SetWheelBrakeLevel, I'd expect a braking force equal to the weight of the aircraft, which should yield a braking deceleration of 1 g or -9.81 m/s2. So, if I ran the jet up to 100 m/s and slammed on the brakes I'd expect to stop in t = v0/a ~ 10-11 seconds. In practice it takes nearly 30 seconds to come to a halt.

If I set the friction coefficient to 0 and apply a braking force equal to the weight of the aircraft, I'd also expect to come to a halt from 100 m/s in about 10-11 seconds. But again, in practice it takes nearly 30 seconds to come to a halt?

Just for laughs, I disabled friction and braking forces in my addon and did this experiment, and I get exactly the same behavior! There seems to be some default Orbiter braking force associated with the , and . keys that completely ignores the addon brake and friction settings? What is going on? What is the point of having these API functions?
 
Last edited:

BrianJ

Addon Developer
Addon Developer
Joined
Apr 19, 2008
Messages
1,500
Reaction score
600
Points
128
Location
Code 347
Hi,
just a thought - which method are you using to define your touchdown points? Is it possible that the SetSurfaceFrictionCoeff(...) function only applies to the old way of setting touchdown points (three vectors) - not the new way using a touchdown vertex array? Sounds like there are more than one issue here, anyway. Good luck!
BrianJ
 

GLS

Well-known member
Orbiter Contributor
Addon Developer
Joined
Mar 22, 2008
Messages
5,010
Reaction score
1,644
Points
188
Website
github.com
Just putting here my knowledge of the brake functions: some years ago I noticed that the argument of SetMaxWheelbrakeForce() didn't work as the braking force was always the same (this was posted in a ticket in the old forum). Since then, the Orbiter sources went public and I've spent just a couple of minutes looking into this, and it seems the new touchdown system doesn't use the parameter passed by SetMaxWheelbrakeForce().
I managed to bypass the default brake keys, by "eating" them in clbkConsumeDirectKey() with the RESETKEY macro. I could then "integrate" the key presses and have a linear rise and fall to the braking, instead of the default "on or off" way, and then call SetWheelbrakeLevel() with my "integrated" value. As I was having too much brake force, I added a <1 factor to decrease the value passed to SetWheelbrakeLevel(), so the resulting braking force is +/- what I need.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
I am using the old 3-point contact model with SetSurfaceFrictionCoefficient for now. The friction coefficients are set inside the new vertex model. It does not appear that the new vertex model generates guess values of the spring coefficients as advertised, so it's just easier to deal with the old model.

The RESETKEY macro may be what I am missing to replace the braking function of the . and , keys. I still would like a better way of restoring the unbraked friction coefficients when the brake keys are released.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
The RESETKEY macro did the trick to let my braking model use the , and . keys.

One thing I have realized is that there is only a single definition of surface friction coefficient, and it appears to be for dynamic friction only. In general, for two contacting solid surfaces, there should be a static friction coefficient and a dynamic or kinetic coefficient. The static coefficient defines the frictional force needed to be overcome to get a stationary object to start to slide, and the dynamic coefficient defines the frictional force opposing the motion of a sliding object.

What this means is that any thrust force applied to a vessel in Orbiter will accelerate the vessel from rest, even if the brakes are fully applied. So it is impossible to model holding brakes and running up the engines without moving. Blah.
 

JMW

Aspiring Addon Developer
Joined
Aug 5, 2008
Messages
580
Reaction score
40
Points
43
Location
Happy Wherever
Just putting here my knowledge of the brake functions: some years ago I noticed that the argument of SetMaxWheelbrakeForce() didn't work as the braking force was always the same (this was posted in a ticket in the old forum). Since then, the Orbiter sources went public and I've spent just a couple of minutes looking into this, and it seems the new touchdown system doesn't use the parameter passed by SetMaxWheelbrakeForce().
I managed to bypass the default brake keys, by "eating" them in clbkConsumeDirectKey() with the RESETKEY macro. I could then "integrate" the key presses and have a linear rise and fall to the braking, instead of the default "on or off" way, and then call SetWheelbrakeLevel() with my "integrated" value. As I was having too much brake force, I added a <1 factor to decrease the value passed to SetWheelbrakeLevel(), so the resulting braking force is +/- what I need.
Hi All.
Where do you put the RESETKEY macro to disable default brake key ?
Have tried in clbkConsumeDirectKey but doesn't disable the regular COMMA / PERIOD function,
is still FULL ON/OFF.........
What am I missing ?
int ShuttlePB::clbkConsumeDirectKey (char *kstate) { RESETKEY (kstate, OAPI_KEY_COMMA); RESETKEY (kstate, OAPI_KEY_PERIOD); if (KEYDOWN (kstate, OAPI_KEY_COMMA &&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))) // Less Wheelbrake left { blevell += 0.000001; if (blevell > 0.4) { blevell = 0.4; } SetWheelbrakeLevel(blevell,1, false); return 1; } if (KEYDOWN (kstate, OAPI_KEY_PERIOD &&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))) // Less Wheelbrake right //if (blevelr == 0) { blevelr += 0.000001; if (blevelr > 0.4) { blevelr = 0.4; } SetWheelbrakeLevel(blevelr,2, false); return 1; } .............................................................
 
Last edited:

GLS

Well-known member
Orbiter Contributor
Addon Developer
Joined
Mar 22, 2008
Messages
5,010
Reaction score
1,644
Points
188
Website
github.com
Hi All.
Where do you put the RESETKEY macro to disable default brake key ?
Have tried in clbkConsumeDirectKey but doesn't disable the regular COMMA / PERIOD function,
is still FULL ON/OFF.........
What am I missing ?
int ShuttlePB::clbkConsumeDirectKey (char *kstate) { RESETKEY (kstate, OAPI_KEY_COMMA); RESETKEY (kstate, OAPI_KEY_PERIOD); if (KEYDOWN (kstate, OAPI_KEY_COMMA &&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))) // Less Wheelbrake left { blevell += 0.000001; if (blevell > 0.4) { blevell = 0.4; } SetWheelbrakeLevel(blevell,1, false); return 1; } if (KEYDOWN (kstate, OAPI_KEY_PERIOD &&!KEYMOD_SHIFT(kstate)&&!KEYMOD_CONTROL (kstate))) // Less Wheelbrake right //if (blevelr == 0) { blevelr += 0.000001; if (blevelr > 0.4) { blevelr = 0.4; } SetWheelbrakeLevel(blevelr,2, false); return 1; } .............................................................
C++:
{
    if (KEYDOWN(kstate, OAPI_KEY_COMMA))
    {
        RESETKEY(kstate, OAPI_KEY_COMMA);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    if (KEYDOWN(kstate, OAPI_KEY_PERIOD))
    {
        RESETKEY(kstate, OAPI_KEY_PERIOD);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    return 0;
}
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
C++:
{
    if (KEYDOWN(kstate, OAPI_KEY_COMMA))
    {
        RESETKEY(kstate, OAPI_KEY_COMMA);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    if (KEYDOWN(kstate, OAPI_KEY_PERIOD))
    {
        RESETKEY(kstate, OAPI_KEY_PERIOD);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    return 0;
}

I thought I understood what was going on, but now I think I don't. I thought you just ran RESETKEY in clbkConsumeDirectKey but then defined the key events as normal in clbkConsumeBufferedKey? In actuality, it is all done in clbkConsumeDirectKey for the keys that are being reset?
 

GLS

Well-known member
Orbiter Contributor
Addon Developer
Joined
Mar 22, 2008
Messages
5,010
Reaction score
1,644
Points
188
Website
github.com
I thought I understood what was going on, but now I think I don't. I thought you just ran RESETKEY in clbkConsumeDirectKey but then defined the key events as normal in clbkConsumeBufferedKey? In actuality, it is all done in clbkConsumeDirectKey for the keys that are being reset?
Yeah, that is all in clbkConsumeDirectKey(). Maybe it works just eating the key there, and having the rest of the logic in clbkConsumeBufferedKey(), but I never tried it.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
Yeah, that is all in clbkConsumeDirectKey(). Maybe it works just eating the key there, and having the rest of the logic in clbkConsumeBufferedKey(), but I never tried it.
I'll have to try that. It seems that my braking action died when I tried using clbkConsumeBufferedKey().
 

JMW

Aspiring Addon Developer
Joined
Aug 5, 2008
Messages
580
Reaction score
40
Points
43
Location
Happy Wherever
C++:
{
    if (KEYDOWN(kstate, OAPI_KEY_COMMA))
    {
        RESETKEY(kstate, OAPI_KEY_COMMA);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    if (KEYDOWN(kstate, OAPI_KEY_PERIOD))
    {
        RESETKEY(kstate, OAPI_KEY_PERIOD);// prevent default processing of the key
        // do things
    }
    else
    {
        // reset things
    }

    return 0;
}
Hi there,
Still no joy with this, can't get it to disable default operation of the brakes.
Code:
// In
int ShuttlePB::clbkConsumeDirectKey (char *kstate)

{
if (KEYDOWN (kstate, OAPI_KEY_COMMA)) // Less Wheelbrake left
        {       
                    blevell += 0.000001;
                    if (blevell > 0.4) {
                        blevell = 0.4;
                    }
                    SetWheelbrakeLevel(blevell,1, false);
                    RESETKEY (kstate, OAPI_KEY_COMMA);
                    }
                    //return 0;
                
    if (KEYDOWN (kstate, OAPI_KEY_PERIOD)) // Less Wheelbrake right
        {       
    
                    blevelr += 0.000001;
                    if (blevelr > 0.4) {
                        blevelr = 0.4;
                    }
                    SetWheelbrakeLevel(blevelr,2, false);
                    RESETKEY (kstate, OAPI_KEY_PERIOD);
                    }
                    //return 0;               
}
return 0;
But this works - Disables default operation.
I don't get it o_O


Code:
if (KEYDOWN (kstate, OAPI_KEY_NUMPAD2 ))
            {
                if(spotcntrl >-15)
                {spotcntrl = spotcntrl - 0.51* oapiGetSimStep();
                if(spotcntrl <-1.50)
                spotcntrl = spotcntrl - 4.51* oapiGetSimStep();}
            
        RESETKEY (kstate, OAPI_KEY_NUMPAD2);
            }
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
I'd think the first code would work if you ran RESETKEY first, before trying to set your own braking model.
 

GLS

Well-known member
Orbiter Contributor
Addon Developer
Joined
Mar 22, 2008
Messages
5,010
Reaction score
1,644
Points
188
Website
github.com
I'd think the first code would work if you ran RESETKEY first, before trying to set your own braking model.
This is probably it.
Also of note, my calls to SetWheelbrakeLevel() are clbkPostStep().
 

JMW

Aspiring Addon Developer
Joined
Aug 5, 2008
Messages
580
Reaction score
40
Points
43
Location
Happy Wherever
This is probably it.
Also of note, my calls to SetWheelbrakeLevel() are clbkPostStep().
Following your post if I understand correctly .......
In clbkConsumeDirectKey()
Code:
if (KEYDOWN (kstate, OAPI_KEY_COMMA)) // Less Wheelbrake left 
        {        
                    
                    RESETKEY (kstate, OAPI_KEY_COMMA);
                    if(CommaKey == 0) CommaKey = 1;
                    
                    }
    else {    CommaKey = 0;}
                
    if (KEYDOWN (kstate, OAPI_KEY_PERIOD)) // Less Wheelbrake right
        {        
    
                    RESETKEY (kstate, OAPI_KEY_PERIOD);
                    if(PeriodKey == 0) PeriodKey = 1;
                    
                    }
    else {    PeriodKey = 0;}                            
}
return 0;

In clbkPostStep()
Code:
if (CommaKey == 1) // Less Wheelbrake left
        {       
                    blevell += 0.000001;
                    if (blevell > 0.4) {
                        blevell = 0.4;
                    }
                    SetWheelbrakeLevel(blevell,1, false);
                    
                    }
                
                
    if (PeriodKey == 1) // Less Wheelbrake right
        {       
    
                    blevelr += 0.000001;
                    if (blevelr > 0.4) {
                        blevelr = 0.4;
                    }
                    SetWheelbrakeLevel(blevelr,2, false);
                    
                    }

Still doesn't disable default braking.........
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
You need to read my original post at the top of this thread. It seems the maximum braking force you can get is limited by the friction coefficient in the direction of travel. The maximum braking force you can realistically get is (1.0) x vessel weight. It seems that the SetMaxWheelbrakeForce value is limited by the friction to (1.0) x vessel weight.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
3,043
Reaction score
1,382
Points
138
Location
Massachusetts
SetMaxWheelbrakeForce and SetWheelbrakeLevel don't work the way that you think they do. They are ultimately limited by the friction coefficient of the contact points.

My brake model doesn't use SetMaxWheelbrakeForce and SetWheelbrakeLevel at all anymore - it simply switches the friction coefficients of the contact points.

The friction drag force F exerted by the contact points is equal to mu * W, where mu is the dynamic friction coefficient and W is the total vessel weight. No matter your SetMaxWheelbrakeForce and SetWheelbrakeLevel settings, your braking force will never exceed mu * W. So if you have contact points that you want to have behave as rolling wheels, you need to set the longitudinal and lateral coefficients like this:

C++:
SetSurfaceFrictionCoeff(0.01, 1.0);  //low friction in direction of motion (a rolling wheel)

But if you want to slow down the vessel, you need to increase the coefficient in the direction of travel. And if you want to steer using differential braking, you need to be able to let the nose wheel slide laterally, so you need to reduce the lateral coefficient, like this:

C++:
SetSurfaceFrictionCoeff(1.0, 0.01);  //maximum friction in direction of motion (braking), but low laterally (for steering)

The upshot is - there is no way to apply a higher drag or braking force greater than the weight of the aircraft without resorting to AddForce. By itself this is OK as it is somewhat realistic. But sadly, the contact model does not provide a static friction coefficient capability, so you can't "lock up" the brakes like if you wanted to run up engines before take-off. Any thrust at all will cause the vessel to slide.
 

JMW

Aspiring Addon Developer
Joined
Aug 5, 2008
Messages
580
Reaction score
40
Points
43
Location
Happy Wherever
You need to read my original post at the top of this thread. It seems the maximum braking force you can get is limited by the friction coefficient in the direction of travel. The maximum braking force you can realistically get is (1.0) x vessel weight. It seems that the SetMaxWheelbrakeForce value is limited by the friction to (1.0) x vessel weight.
Thanks for your help with this.
I'm trying to get less braking effort, rather than the maximum, but whatever figure I put in SetWheelbrakeLevel() however low, always results in the same effect - maximum braking.

Even if you comment SetWheelbrakeLevel()?
Yes, still get the maximum braking effect even commenting out the whole chabang in clbkPostStep(). :LOL:

you can't "lock up" the brakes like if you wanted to run up engines before take-off. Any thrust at all will cause the vessel to slide.
So that's why it's not possible to keep stationary on an incline too, even with brakes applied ?
 

GLS

Well-known member
Orbiter Contributor
Addon Developer
Joined
Mar 22, 2008
Messages
5,010
Reaction score
1,644
Points
188
Website
github.com
Yes, still get the maximum braking effect even commenting out the whole chabang in clbkPostStep(). :LOL:
Can you confirm that the code execution passes RESETKEY?
 
Top