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

Improve Virtual Speed in ERGmode #175

Closed
WouterJD opened this issue Dec 21, 2020 · 19 comments
Closed

Improve Virtual Speed in ERGmode #175

WouterJD opened this issue Dec 21, 2020 · 19 comments
Labels
enhancement New feature or request future development This is not on the backlog for the coming releases

Comments

@WouterJD
Copy link
Owner

@WouterJD @torpedox88 ... a quick, related after-note because I've just discovered this... thanks to @ericchristoffersen

GoldenCheetah has a sim model which correctly uses power over time to calculate a virtual bike speed. A recent change was made specifically to use this model in ERG mode, assuming a slope of 0%, so that ERG mode workouts have a consistent speed and distance.

Such a model shouldn't be difficult to implement (I've played with one) and could well be a little enhancement for FortiusANT, to replace the inverse calculation, which has the same aim, just the maths is different.

GoldenCheetah/GoldenCheetah#3712
https://groups.google.com/g/golden-cheetah-users/c/o3_0vHeGc1c/m/n0490j0aCAAJ

Originally posted by @mattipee in #125 (comment)

@WouterJD WouterJD added the enhancement New feature or request label Dec 21, 2020
@WouterJD
Copy link
Owner Author

Please suggest how to proceed

@mattipee
Copy link
Contributor

Thanks, Wouter.

I'll make some quick notes now, and come back to this at some point. It's low priority.

https://physics.stackexchange.com/questions/226854/how-can-i-model-the-acceleration-velocity-of-a-bicycle-knowing-only-the-power-ou/226856

Link above has example python code which I played with to good effect, and nice that it graphs. But the fundamentals are captured in this snippet from one of the comments...

Screenshot_20201221-090917~2.png

It's easier than it looks.

  • use supplied user+bike weight
  • assume 0% grade
  • assume either defaults or ANT+ provided values for aero/rolling coefficients.
  • every time we get a new power value in our loop, calculate our deltaT and update the model.

@mattipee
Copy link
Contributor

I think there's a typo in the snippet...
Pneeded = Ftotal . v

@mattipee
Copy link
Contributor

We have the function to calculate Pneeded already, it just needs to be parameterised (or copy-paste-modify 🤦‍♂️) so we can call it with the model's current sim-speed and grade 0%.

@switchabl
Copy link
Contributor

Some notes:

  • If the resulting virtual speed is transmitted via ANT FE-C page 16, the virtual speed flag should be set
  • The ANT rolling/wind resistance should not be used. That would make behaviour depend on whatever random simulation mode you had running before (those are not sent in ERG mode). Using weight from ANT user data is probably fine (and won't make a big difference with 0% slope)
  • It is fine to use this integrated speed for display, it should not be used for resistance calculation (I know this has not been suggested so far, but just to make sure). If the calculated acceleration is out-of-sync with the physical acceleration of the flywheel, resistance can feel decoupled and laggy.

We have the function to calculate Pneeded already, it just needs to be parameterised (or copy-paste-modify 🤦‍♂️) [...]

The former, I think. There are several class methods that do straightforward calculations, but instead of taking their inputs as parameters and returning the result, they operate on member variables. That makes it harder to follow the data flow and harder to re-use. I would move them to a separate utility/maths module instead. If necessary, number of parameters passed around can be reduced by making a new "SimulationParameters" class grouping slope, rolling resistance, wind speed etc together.

@mattipee
Copy link
Contributor

mattipee commented Dec 21, 2020

@switchabl totally agree on the refactor. Decouple, decouple...

Other notes also good and appreciated.

@WouterJD WouterJD added future development This is not on the backlog for the coming releases and removed future development This is not on the backlog for the coming releases labels Jan 11, 2021
@mjunkmyjunk
Copy link

I'm looking at the code specifically on

if self.CurrentPower == 0:

whereby when power = 0, then vSpeed = 0 and wondering if this is related to this current enhancement being discussed?

Even tho during indoor trainer session, there is technically no coasting, It's not really representative of real world conditions, even the trainer flywheel still do turn after the user stop pedalling.

thanks...

@WouterJD
Copy link
Owner Author

Hi @mjunkmyjunk; interesting observation. This code is created with ERGmode training in mind so you are probably right. Code improvement suggestions welcome.

Welcome to the FortiusANT community


I'm always curious to know who I communicate with, where FortiusANT is used and what configuration is used.
Please tell me what bundle did you buy, and what brake and what head unit do you use?
I would therefore appreciate that you introduce yourself; perhaps leave a comment under issue #14.


@mjunkmyjunk
Copy link

@WouterJD ah.. in ergmode, slope is always 0% so that would be all right.

I was looking at this https://physics.stackexchange.com/questions/613209/calculating-speed-of-bicycle-down-a-hill-or-coasting-to-a-stop which does offer a physics based coastdown speed towards 0kmh from the initial velocity when user stops pedalling.

However, the equation doesn't work for slopes greater than -0.3%.

There's also this https://physics.stackexchange.com/questions/334654/how-to-correctly-determine-the-stopping-distance-of-a-coasting-bicycle-when-cons/334968?noredirect=1#comment1383300_334968 but again, part of the equation would come out as "error" due to a SQRT(-ve) number.

GoldenCheetah's code may be the best, but I am not well versed in it and can't get a good grasp of the implementation (esp this part of the solver code - GoldenCheetah/GoldenCheetah#3064)

@ericchristoffersen
Copy link

ericchristoffersen commented Feb 18, 2021

GoldenCheetah's code may be the best, but I am not well versed in it and can't get a good grasp of the implementation (esp this part of the solver code - GoldenCheetah/GoldenCheetah#3064)

I can maybe shed light on that gc code and your current situation.

First, that golden cheetah 'virtual speed' is the speed at which forces are balanced, it is the speed at which bicycle is neither accelerating or decelerating. The virtual speed is the speed the bicycle will converge to if your power and the bicycle state are kept constant. I don't think the virtual speed in gc is useful for anyone and should be deleted because its just confusing. If you try to use it for a simulated ride it will both over and underreport speed because it literally ignores acceleration. It would be your speed if you had no inertia.

The reason the golden cheetah virtual speed implementation is so complicated is because it is discovering the v term of the force equation, which is to the third power, otherwise known as a cubic. The old gc implementation had a closed form expression for v which can only be valid when the terms were in what we might call 'the climbing zone.' When I tried to use it for general simulation I was also descending and the previous closed form expression would produce nan and infinity because the values pushed the cubic into a different solution zone where the existing closed form is invalid.

If you're interested the Jim Blinn paper explains everything there is to know about solving cubics:

https://courses.cs.washington.edu/courses/cse590b/13au/lecture_notes/solvecubic_p5.pdf

I think one of the best papers I've ever read. Spend a few days to understand it, its very clever stuff. Probably the best fp precision management I've ever seen in a paper.

So... you almost certainly don't want that virtual speed calculation. Its not a good number unless for some reason you really wish 'speed at the limit'.

If you wish to simulate speed independent of trainer speed you will need to sum force over time. You should look at the SampleSpeed method at the end of bicyclesim.cpp. That integrates power over time and applies it to the simulated bicycle. That is how to correctly model a virtual bicycle's speed over time. Because the input power value isn't continuous the key is in how the integration works. I left a bunch of different integrators in the source so you can see compare how they behave. I used the kahan/li because it converges on correct so quickly that it is like magic.

@mjunkmyjunk
Copy link

mjunkmyjunk commented Feb 18, 2021

First, that golden cheetah 'virtual speed' is the speed at which forces are balanced, it is the speed at which bicycle is neither accelerating or decelerating. [snip] It would be your speed if you had no inertia.

Yes. I realise that although parts of your code also references KE values which would be the Kinetic Energy stored in the "system". (ref: https://groups.google.com/g/golden-cheetah-users/c/G7yYcXf-UD4)

The reason the golden cheetah virtual speed implementation is so complicated is because it is discovering the v term of the force equation, which is to the third power, otherwise known as a cubic.

Is this the same "virtual speed" which you are referencing and suggest needs to be deleted?

If you're interested the Jim Blinn paper explains everything there is to know about solving cubics:
https://courses.cs.washington.edu/courses/cse590b/13au/lecture_notes/solvecubic_p5.pdf

I tried reading it. (emphasis on tried.)

If you wish to simulate speed independent of trainer speed you will need to sum force over time. You should look at the SampleSpeed method at the end of bicyclesim.cpp. That integrates power over time and applies it to the simulated bicycle. That is how to correctly model a virtual bicycle's speed over time. Because the input power value isn't continuous the key is in how the integration works. I left a bunch of different integrators in the source so you can see compare how they behave. I used the kahan/li because it converges on correct so quickly that it is like magic.

yeah, I also looked at the sampleSpeed code and that of MotionStatePair and quite honestly, GC's code is based on C and my proficiency isn't quite up to par. https://github.com/GoldenCheetah/GoldenCheetah/blob/2d64166acd61612cba6684cd27d60cab813667f5/src/Train/BicycleSim.cpp#L457

My Simplistic attempt at modelling coasting downhill. (using this #175 (comment) to somewhat calculate for the value of a(acceleration))

Screenshot 2021-02-18 at 10 51 29 AM

It definitely can be made better, but now at least, I won't severely underreport my downhill coasting speed. (prev it was power = 0, speed = 0). However, like you said, the model is incomplete when we're looking at WattsForV and this is clearly evident when I model the user to start pedaling again. It goes from 35kmh at x seconds to 4kmh at x+1 sec when user applies a 50w power to the pedals. (50w at 5% gradient = 4w in my model). Inertia and KE is not taken into account.

@ericchristoffersen
Copy link

The reason the golden cheetah virtual speed implementation is so complicated is because it is discovering the v term of the force equation, which is to the third power, otherwise known as a cubic.

Is this the same "virtual speed" which you are referencing and suggest needs to be deleted?

Yes, exactly. I tried my best to do a perfect job on the virtual speed thing that is bad and should be deleted. At the time I was just getting started and didn't realize the closed form approach was completely wrong.

If you're interested the Jim Blinn paper explains everything there is to know about solving cubics:
https://courses.cs.washington.edu/courses/cse590b/13au/lecture_notes/solvecubic_p5.pdf

I tried reading it. (emphasis on tried.)

Its really good. You'll learn a lot. There are 5 papers, start with the first and go slow. You'll learn a lot.

yeah, I also looked at the sampleSpeed code and that of MotionStatePair and quite honestly, GC's code is based on C and my proficiency isn't quite up to par. https://github.com/GoldenCheetah/GoldenCheetah/blob/2d64166acd61612cba6684cd27d60cab813667f5/src/Train/BicycleSim.cpp#L457

Why don't you simply run it in debugger? Step through and see what it does. I promise the real magic is in that ::Integrate call. Hamiltonian symplectic integrator. It is incredible technology. That way of summing is able to predict and account for the change that must have occurred between the datapoints.

My Simplistic attempt at modelling coasting downhill. (using this #175 (comment) to somewhat calculate for the value of a(acceleration))

Screenshot 2021-02-18 at 10 51 29 AM

It definitely can be made better, but now at least, I won't severely underreport my downhill coasting speed. (prev it was power = 0, speed = 0). However, like you said, the model is incomplete when we're looking at WattsForV and this is clearly evident when I model the user to start pedaling again. It goes from 35kmh at x seconds to 4kmh at x+1 sec when user applies a 50w power to the pedals. (50w at 5% gradient = 4w in my model). Inertia and KE is not taken into account.

Sure that is better but I don't understand why you'd stop before you had it correct. Just copy the gc code.

@mjunkmyjunk
Copy link

mjunkmyjunk commented Feb 18, 2021

Why don't you simply run it in debugger? Step through and see what it does. I promise the real magic is in that ::Integrate call. Hamiltonian symplectic integrator. It is incredible technology. That way of summing is able to predict and account for the change that must have occurred between the datapoints.

TBH - I've yet to figure out how exactly to set up the IDE/Enviroment for my Mac.

Sure that is better but I don't understand why you'd stop before you had it correct. Just copy the gc code.

Once I learn how to compile it for Mac.

@switchabl
Copy link
Contributor

switchabl commented Feb 18, 2021

I have a somewhat different view than @ericchristoffersen (I think; at least I don't believe steady-state virtual speed should be removed altogether). Before I elaborate let me emphasize that there is not necessarily a right answer here. Virtual speed is a compromise that we make because the limited resistance range and (in some cases) inertia of the trainer doesn't allow for a realistic simulation in all situations. So this is as much about user/ride experience as it is about physics. With that out of the way, I think there are at least three different kinds of speeds that are potentially useful:

  • wheel speed: The physical speed of the wheel on the trainer. This is closest to what the user actually experiences but is otherwise not very helpful in terms of simulation once we hit the limits of the trainer. It is still needed internally to convert target power to force on certain trainers. (May be helpful if you want to track chain/tyre/trainer wear as well)
  • steady-state speed: The speed towards which the system would converge if the current power were sustained indefinitely as discussed by @ericchristoffersen above. This is more or less equivalent to simulating a bike with reduced inertia (matching that of the trainer). As long as the resistance range of the trainer is not exceeded, it is essentially equal to the wheel speed. So accelerations may be unrealistically easy, but the speed remains closely coupled to the wheel speed. I think there is a strong argument to be made that this is the speed that should be used for the force calculation in slope mode. It avoids a delay between acceleration and resistance adjustment that could make the trainer feel laggy and unresponsive. You could also argue that this is the virtual speed that should be displayed in slope mode so the displayed speed doesn't lag behind the perceived speed (although it is physically less accurate).
  • simulated speed: This is calculated by integrating the full equation of motion (numerically). It is the speed that an actual bike with the given parameters should have if the same (time-depedent) power were applied. So in this sense it is the most accurate choice. However it is also essentially decoupled from the actual wheel speed. On trainers with small flyhweels (like Flow and Vortex) it will lag behind wheel speed by a lot. In ERG-mode wheel speed doesn't matter anyway, so I agree that for this specific application this is definitely the way to go. And I think it is also a good choice for the display speed in slope mode because it is what software like Zwift uses so people are used to it anyway.

One detail I glossed over so far is the input power that is fed into calculation. For the above to work correctly, we also need to use slightly different power definitions:

  • steady-state power: The power that would be required to sustain the current wheel speed at the current resistance setting without accelerating/deccelerating (so brake/rolling resistance but no flywheel contribution). This is the correct power to use for the steady-state speed, otherwise you get spikes/dips when accelerating/deccelerating. For magnetic brakes this is also the power that we get from our power formula.
  • dynamic power: This is the actual power the rider puts out, including the additional power required to accelerate the flywheel. This is the power that an external power meter would measure. It is the correct power input to use for the simulated speed.

As for the numerical aspects, my personal impulse would be to stick to something simple because I expect neither accuracy nor performance to be critical here (and I am not sure that higher order methods necessarily improve performance with noisy measurement data where smoothness assumptions may not apply). But I haven't really looked into that, so I will defer to @ericchristoffersen on this.

@mjunkmyjunk
Copy link

Thank you for your thought contribution on this matter. It is really interesting to read through it all. You are absolutely right in the toss-up/balance between user experience (feel) and real world physics.

Indoors, the wheel speed would not necessarily correlate to actual speed outdoors whether it's a wheels on or direct drive trainer. (esp downhill simulations, Uphill I think should be OK). So, what does the user expect to happen to their speed? (typical trainers don't drive the flywheel to increase speed). Hence what should be done? Wheels Stop, (-10% downhill for eg) but real world, the cyclist would still be coasting down.

  1. wheels stop spinning but speed displayed will slowly ramp down to 0 or some number
  2. wheels stop and speed = 0?

I personally prefer item 1 even tho the wheels has stopped. (and I also track indoor miles for chain wear/longevity and coast down would confuse the tracking - but no matter, riding outdoors has the same issue anyways). Hence simulated speed. Simulated speed is what @ericchristoffersen is doing I believe. (tho I'm confused as to his comment on removing it, but giving it more thought, I think he meant removing "virtual speed" and retaining "simulated speed" - correct?)

the link from @mattipee is interesting, in that (I believe) it's modelling the acceleration as well. (I paste the link again)
https://physics.stackexchange.com/questions/226854/how-can-i-model-the-acceleration-velocity-of-a-bicycle-knowing-only-the-power-ou

Personally, since GC (and Fortius code is somewhat integrated into GC), I believe that the code should model the physics and present real-world scenarios when cycling in slope mode. That's just me.

@ericchristoffersen
Copy link

I am really not being understood. Maybe I'm using too much gc vernacular. "Virtual Speed" is not the same as "Simulated Speed". Mattipee is bringing up an entirely different discussion.

In GC virtual speed is a computed value stored on the realtimedata structure. At one point years ago it was used to compute virtual progress in gc. This is the expression that used to produce nan and inf, and doesn't now because it uses a proper cubic solver. The solver is irrelevant though because Virtual Speed does not model inertia. Virtual speed is a data field with a computed value in it. Its only use today is to be displayed. Because it is never 'right' I personally cannot envision when it is useful and people confuse it with other things, which is why I think it should be removed.

My only point here was that if you wish to model velocity while in erg mode, the virtual speed calculation is a bad choice because it will jump around a ton. It is convenient because it is stateless but it will suck. To get a realistic velocity you really need to model inertia.

@switchabl
Copy link
Contributor

switchabl commented Feb 18, 2021

@ericchristoffersen Thanks for the clarification. I have to admit I was not really familiar with "Virtual Speed" in GC (I use GC for analysis but I have never used the Training module). I can't see much use for this as a data field either, particularly assuming that it is calculated using the normal power data.

I also agree that proper simulated speed (with inertia) should be used for erg mode. But anyone who wants to implement this in FortiusANT will probably need to look at refactoring the (somewhat messy) existing virtual speed code that is using the steady-state approach. So I think a slightly broader perspective is helpful. The points raised by @mjunkmyjunk are not directly related to the original proposal but it probably makes sense to address all this together.

We will definitely need to keep the steady-state virtual speed in FortiusANT for resistance control even if we add a proper simulated speed for display/recording.

@WouterJD
Copy link
Owner Author

Wauw, there is a lot of communication here.

The current implementation is:

  • physical speed is displayed
  • the speed is reduced with the Digital Gearbox ratio

Although with a GUI, FortiusAnt is a device-driver converting USB to ANT/BLE and vice versa; speed is displayed by the CTP.

@WouterJD WouterJD added the future development This is not on the backlog for the coming releases label Mar 17, 2021
@WouterJD
Copy link
Owner Author

Since there is no communication here, I assume can be closed.
If not happy to reopen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request future development This is not on the backlog for the coming releases
Projects
None yet
Development

No branches or pull requests

5 participants