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

ERG Mode first try #277

Merged
merged 45 commits into from
Dec 24, 2021
Merged

ERG Mode first try #277

merged 45 commits into from
Dec 24, 2021

Conversation

MarkusSchneider
Copy link
Collaborator

@MarkusSchneider MarkusSchneider commented Dec 17, 2021

Hi Anthony,
I've tried a lot of things, at the end I'm doing the same things that you did in the ERG-Function.
I've create a new Task to do the ERG calculation.
For a "real" PID Control, the power value is not measured frequent enough. For a digital control algorithm you need the input values ~10 Times faster then the control loop takes. In our case, we would need the power values every 100ms.

Only possibility to improve ERG is to guess the Incline better based on the Setpoint. This could make ERG more responsive, faster.

What did I change:

  • split userParameter into userParameters (those, that must persisted) and runtimeParameters
  • decoupled ERG Mode from BLE_server (Zwift) cycle by adding a new Task.
  • Added a new runtimeParameter for targetWatts (coming from Zwift's ERG)
  • Added a new Setting to btSimulator.html to adjust TargetWatts and enable ERG. Now we can test ERG without Zwift, or use it for training.
    image
  • in main.cpp I've changed a little bit the moveStepper Function. I think we should give the Incline to this function and should not do any power/incline calculation there.

In the ERG-Task, there is a logging function implemented and I did some evaluations.
I will also add a further log to detect the bike characteristics by using the shifters to set the incline.
With this log, we can try to find the correlation between cadence, power and incline (stepper-position).

image
X-Axis: Watts
Y-Axis: Incline
On my spinning bike, the correlation between cadance + power seems to be linear.

What I did is not production ready. Is a first implementation for discussion.

I believe that the measurement of the watts is not very accurate:
image

As you can see in the chart, we have a lot of value changes between the measurement cycles.
It's maybe an other point for improvement.
We can maybe use a Kalmanfilter .

What else can we do?

  • Try to measure the factor between cadence + power and use this factor for predictions.
  • Find out what effect a high cadence, incline, watt has and try to adjust incline based on this.
  • Try to use cadence values too. As far as I know does Zwift not send a Target Candence....
  • Create a Table of Power/Incline. Normal in a training session is spitted in repeated block. With the first execution of one of this intervals, we could know the Incline value for this setting. If Zwift request the Target Power the next time, we have the correct Incline.
  • Create a State Machine with different behaviors for each state e.g.
    • Init State: ERG Mode starts. Stepper must move fast to get the first power values
    • Target Watts Changed: Make high changes to reach the set-point quick
    • Target Watts Unchanged: Make only small adjustments to balance cadence changes
    • Cadence Changed: Make high changes to reach the set-point quick
    • Target Watts drop: Set setpoint to previous value and wait a few cycles. If watts drops user does not cycle with power -> power values wrong.

If you like, you can push this to a new erg_development branch.
I don't know if all of my changes really helpful. Just take a look and let me know.

@doudar
Copy link
Owner

doudar commented Dec 18, 2021

Lots of good thoughts here!!

Yes, since it’s a major change, I think we should push it to a new branch for the moment. I really like your ideas and the direction it’s going though.

The idea of using a state machine to make large changes only when the target changes is great.

I also like the ERG setting on the ble simulator page.

Nice work- I can’t wait to look at it further this weekend.

Edit: Nice idea putting a time stamp on the watt reading as well!

@doudar doudar changed the base branch from develop to ERG-Respin December 18, 2021 03:23
@doudar
Copy link
Owner

doudar commented Dec 18, 2021

If we build a table, we could use absolute steps to build the table then should use relative steps subsequently in the workout session. That way skipped steps (from hitting end stop) don’t disrupt the rest of the session.

i.e. If it takes 300 steps to turn the knob one revolution, You’ll still know from the table that two turns of the knob to the left (-600 steps) will reduce power from 300w to 200w even after skipped steps if your table contains {200w, 1600steps, 300w, 2200steps}

Edit: also, a table might be an effective way of homing. If the knob is turning left and CAD>=previousCAD and watts don’t decrease, that’s minimum position.

Similarly if knob is turning right and CAD>=previousCAD and watts don’t increase, that’s Maximum position. (Max may not be possible to determine if rider can’t overpower the bike)

Then if we knew minimum position relative to current watts, we could always guess at current absolute position. Then it might be possible for a user to manually set/enter maximum position.

Then the table could be useful outside ERG mode as well to keep from skipping steps by (avoiding) hitting an end stop.

@MarkusSchneider
Copy link
Collaborator Author

MarkusSchneider commented Dec 18, 2021

Having the min and max position would be helpful. We could then avoid damage by spinning the stepper to far.
What do you think about a setup routine.
Every time when SmartSpin is mounted to the bike, the Setup must run.
Setup routine could check for max and min position and we could also check what difference a cadence change made.
Setup could looks like:

  • User start setup: Resistance knob is turned a little bit before the absolute minimum position. We take this as total min
  • User starts to pedal and increases the incline by shifter. As soon as we read e.g. 60 watts. We take this as minimum incline.
  • User increases incline as long as he can not pedal further. We take this as maximum incline.

Getting the effect of cadence changes could be determined by pedaling at a certain Incline with different cadences.

Cadance Watts Incline (fix)
70 80 3000
80 95 3000
90 110 3000

If we make the Incline/Power table we also have to normalize the power to a certain cadence or we have to store the cadence too. If the relation between cadence and power is linear, we only need to have the factor.
Would also interesting to figure out if this factor changes by high power / incline.

@doudar
Copy link
Owner

doudar commented Dec 18, 2021

Having the min and max position would be helpful. We could then avoid damage by spinning the stepper to far. What do you think about a setup routine. Every time when SmartSpin is mounted to the bike, the Setup must run. Setup routine could check for max and min position and we could also check what difference a cadence change made. Setup could looks like:

  • User start setup: Resistance knob is turned a little bit before the absolute minimum position. We take this as total min
  • User starts to pedal and increases the incline by shifter. As soon as we read e.g. 60 watts. We take this as minimum incline.
  • User increases incline as long as he can not pedal further. We take this as maximum incline.

Getting the effect of cadence changes could be determined by pedaling at a certain Incline with different cadences.

Cadance Watts Incline (fix)
70 80 3000
80 95 3000
90 110 3000
If we make the Incline/Power table we also have to normalize the power to a certain cadence or we have to store the cadence too. If the relation between cadence and power is linear, we only need to have the factor. Would also interesting to figure out if this factor changes by high power / incline.

I think this could work ^^

It would be fun to sneak it into the FTMS spin-down/calibration routine as well :

case 0x13: { // Spin Down

Maybe turn knob one direction until user presses a shift button, then turn the other direction until user presses a shift button? Or like you said, turn CCW until <50w registered (or user presses shift) then turn the other direction (CW) until user presses shift.

@doudar
Copy link
Owner

doudar commented Dec 18, 2021

Having the min and max position would be helpful. We could then avoid damage by spinning the stepper to far. What do you think about a setup routine. Every time when SmartSpin is mounted to the bike, the Setup must run. Setup routine could check for max and min position and we could also check what difference a cadence change made. Setup could looks like:

  • User start setup: Resistance knob is turned a little bit before the absolute minimum position. We take this as total min
  • User starts to pedal and increases the incline by shifter. As soon as we read e.g. 60 watts. We take this as minimum incline.
  • User increases incline as long as he can not pedal further. We take this as maximum incline.

Getting the effect of cadence changes could be determined by pedaling at a certain Incline with different cadences.
Cadance Watts Incline (fix)
70 80 3000
80 95 3000
90 110 3000
If we make the Incline/Power table we also have to normalize the power to a certain cadence or we have to store the cadence too. If the relation between cadence and power is linear, we only need to have the factor. Would also interesting to figure out if this factor changes by high power / incline.

I think this could work ^^

It would be fun to sneak it into the FTMS spin-down/calibration routine as well :

case 0x13: { // Spin Down

Maybe turn knob one direction until user presses a shift button, then turn the other direction until user presses a shift button? Or like you said, turn CCW until <50w registered (or user presses shift) then turn the other direction (CW) until user presses shift.

You can take as much time as needed in the spin-down procedure, just send:

ftmsTrainingStatus[1] = 0x00;

When it's done.

What's nice about using spin down is the app will send a pedal/stop pedaling prompt depending on when the speed target lines from that code are sent.
FTMS_v1.0.pdf

@doudar
Copy link
Owner

doudar commented Dec 19, 2021

@MarkusSchneider , check out the work I did today. I probably won't be able to do more until tomorrow night, so feel free to expand on it.

I think building the power table should run continuously in the background in SIM mode and in ERG mode.

What I need help with is how to update ergPowerTable only when power has been stable for 3 seconds. Also, once you uncomment the code, you'll need to increase the task stack size appropriately. I haven't calculated it but the table is pretty big. It shouldn't be a worry however because since I deleted the main task loop, we have a lot of free heap.

Links to relevant lines are:

https://github.com/MarkusSchneider/SmartSpin2k/blob/32a2197897b91acf3c03e7cc4d09afd66f0fb73c/include/ERG_Mode.h#L39
https://github.com/MarkusSchneider/SmartSpin2k/blob/32a2197897b91acf3c03e7cc4d09afd66f0fb73c/src/ERG_Mode.cpp#L63

@doudar
Copy link
Owner

doudar commented Dec 20, 2021

@MarkusSchneider , I just had another thought about homing.

If we had a parameter for the total number of steps from 0-max resistance, we could home by assuming the unit is at max resistance and moving to minimum resistance while decreasing motor amperage so that when the SS2K grinds against the minimum limit, it’s at very little torque. Most bikes require very little torque to turn the knob ccw, so maybe this is an easy simple solution?

@MarkusSchneider
Copy link
Collaborator Author

@doudar I'll check your commits tomorrow. Looks very promising at first glance. Really cool that you figured out the factor between CAD and Power. What we can also do is to normalize the power table e.g. take a normalized CAD from 85 and transform the measured values based on the normalized values.

Switched PowerTable to using Stepper Position vs incline- That way it can record in all modes.

Initial debugging and testing of table write functions - seems to be working!
@doudar
Copy link
Owner

doudar commented Dec 21, 2021

Progress! I've successfully generated a PowertTable (using the simulator, I'll try riding it in a second.)!

It updates on the fly and just keeps averaging any new data that it collects as long as it meets a few second stability test.

image

1st Row is Watts, Second Row is CAD, third row is (stepper) TargetPosition.

The down side is we're going to need to rework ERG a bit to run off of ss2k.targetPosition instead of rtConfig.incline because I couldn't think of a good way to gather the needed information in all modes using incline only.

The up side is that with two or more data points, the output routine (not tested yet) will pick the two closest data points and interpolate or extrapolate a stepper target position even if that wattage isn't in the table yet. That might be better than a blind linear guess like we were doing previously.

Edit - I see you had a way of converting targetposition to ERG incline already so implementation might be easier.

@doudar
Copy link
Owner

doudar commented Dec 22, 2021

Initial testing thoughts: The powertable building and lookup are buggy but sorta work. The good news is that with .5 ERG sensitivity, it controls my bike very well from @MarkusSchneider 's code alone even with powertable throwing a wrench into it occasionally. :)

One issue (maybe @MarkusSchneider can look into it) is that occasionally the table is getting weird numbers injected into it.
See the first log for that. I never pedaled above 350w in this session.

Secondly, I think occasionally (mostly actually) my interpolation is getting the above and below variables flip flopped and spits out negative numbers, so we need to figure out how to keep that from happening. See the second log for that.

https://pastebin.com/6WUBuEay

https://pastebin.com/qsEi1n27

EDIT: Issue 2 I think is fixed. The line below was subtracting to average instead of adding 🤦‍♂️
https://github.com/MarkusSchneider/SmartSpin2k/blob/e8591910c3d1d9147598dd4e214395894bf9973c/src/ERG_Mode.cpp#L244

The first issue is still open though if anyone has time tonight while I sleep :)

@doudar
Copy link
Owner

doudar commented Dec 24, 2021

I'm going to merge this into Develop since my ride today was pretty perfect. We will still need to fix status.html (it's still looking at userconfig where it should be looking for rtconfig), but we can fix that easy.

@doudar doudar marked this pull request as ready for review December 24, 2021 19:17
@doudar doudar merged commit 041b9f2 into doudar:ERG-Respin Dec 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants