Monthly Archives: February 2013

Calibration calculations – part 1

To recap the approach from the last post – in ergo mode we calculate a resistance level that should match a given speed and wattage. In slope mode we first need to estimate a wattage for the current speed and slope, and then use that estimated wattage in the ergo mode calculation.

Power required for a given speed & slope

In order to estimate the ‘real-world’ power required to hold a given speed on a gradient, I’m using calculations from www.analyticcycling.com.

// Wind Resistance     Fw = 1/2 A Cw Rho Vmps2
// Rolling Resistance  Frl = Wkg 9.8 Crr
// Gravity Forces      Fsl = Wkg 9.8 Slope
// Power             Power = (Fw + Frl + Fsl) Vmps

With a wattage estimate from this formula, I can now use the same resistance calculation for both slope and ergo mode.

Resistance required for a given wattage and speed

Level      1       2      3     4      5      6      7      8      9      10
Slope      3.73    5.33   6.87  8.27   10.07  11.40  13.13  14.40  15.93  17.73
Intercept -28.67 -36.67 -43.33 -47.33 -66.33 -67.00 -83.67 -82.00 -89.67 -114.67

Using curves derived from the published Satori graphs as a starting point, I’ve plotted a series of data points for each resistance level.

This gives a table of speed (x-axis), power (y-axis), and resistance (z-axis) – a small extract of this table is shown below. The original 1-10 levels of resistance from the handlebar lever have been converted to their matching levels in the 1-100 range that I’m using for this prototype.

x     y        z

10    34.37    45
15    84.72    45
20    135.07   45
25    185.42   45
30    235.77   45
35    286.12   45
40    336.47   45
45    386.82   45
50    437.17   45
55    487.52   45
60    537.87   45

Given this table as input, I then need to calculate the resistance level that matches any given speed and power combination. My initial approach has been to use the zunzun.com online curve & surface fitting site to provide the formula & coefficients.

One useful aspect of this site is that I don’t need to know the best equation type for matching the input data, I can simply paste the data into the “function finder” section, which will then search for the best fit across a whole range of equation types.

Here’s a contour plot, and an error plot for the selected function. Although the contour plot looks pretty close, you can see that the model starts to break down below about 200 watts.

ContourPlotPerErrVsPower

The site can also generate a complete C (or other language) function with the correct formula and coefficients that can be pasted into the project code (generated function body below).

double GetResistance(double x_in, double y_in)
{
    double temp;
    temp = 0.0;

    // coefficients
    double a = -1.5551745362491399E+01;
    double b = -1.4121261534412761E+00;
    double c = 1.9523034800130676E-01;

    temp = (a+y_in)/(b+c*x_in);
    return temp;
}

Results

Here’s a screenshot from my very first ergo session. You can see that it’s vaguely tracking the target load, but the calibration isn’t really close enough just yet…

ergo1

Next post

I’m sure I can do better on the accuracy, so am trying a couple of different approaches. Firstly, a series of calibration rides to populate the speed/power/resistance table based on my own PowerTap data. Secondly, instead of the surface fitting approach, I’m going to try indexing into the table directly, using bilinear interpolation to estimate the resistance value based on the surrounding four data points.

Advertisements

First calibration attempts

There are two modes that will need to be supported, slope and ergo. I’ve been concentrating on slope mode so far, and have it successfully working with Golden Cheetah following a course file (although not well calibrated against reality).

Slope

As you may expect, slope mode is used to match a specific gradient. This gradient could be set either manually or from a Computrainer course (.crs) file.

My initial thought had been to simply find a resistance level that matched a given gradient, without worrying about the realtime power & speed data. However (and obviously with hindsight), the power curve for the mag trainer is fairly linear, and so won’t match a curve from the real world where wind resistance increases exponentially. So to be more lifelike, the resistance level will need to be based on both the selected gradient and the current speed.

As we can only control the resistance down to it’s minimum level, and not actually apply any power like a fully electronic trainer might, I’m expecting to be quite limited in simulating downhills. I suspect we’ll get to minimum resistance quite quickly as we drop below 0% gradient.

Ergo

In ergo mode, we need to hold the rider to a specific power output – regardless of speed & gearing. For instance, at a given power output we could be turning over a big gear against a lower resistance, or a smaller gear against a higher resistance.

In this mode we can expect the applied resistance level to be reduced as the speed rises, and increased as speed drops (until we reach a cut-off speed where we’ll drop the resistance to the minimum level).

Calibration process

In order to set the required resistance levels, I’m attempting to build a three dimensional model, with speed on the x-axis,  power on the y-axis, and the resistance required to hold that state on the z-axis.

My starting point was power data based on the graphs from the tacx website. (It’s worth noting up front that this data does not correlate particularly closely to my own PowerTap measurements, so I’ll be following up with some more manual calibration sessions).tacx_curves

Others have already calculated the curves for each resistance level, so using the formula for each level, I was able to populate a speed/power/resistance table (initially using a speed range of 10-60kph in increments of 5kph, at all ten standard resistance levels).

The next question is then how to ‘fill in the gaps’ between the points in the table. I think there are two options. (1) Use a 3D curve/surface fitting program to give us a formula for estimating the z-axis value for any given x & y axis values. (2) Index directly into this table, using the current speed and power, and interpolate between the bounding resistance values.

Future Improvements

To keep things moving at this stage, I’m just hard coding the calibration for both modes. However, to make this more flexible in the future, I plan to move towards a PC based calibration routine, with the calibration output uploaded to the microcontroller at runtime.

As well as speed, we’re also receiving the realtime power data, which could potentially be further used to trim the estimated load. However, this will be something for a later day once the basics are in place!

Next post

I expect to be working on the calculations to estimate power for a given speed & slope, and for mapping power & speed to a resistance level.

First ride

I’d wanted to get onto calibration this week, but have been bogged down with a couple of issues.. So instead, will concentrate on the things that have made some progress.

Embedded development

The test harness code has been migrated into a new project, which is handling the real-time training data and buttons for GC, and setting the trainer resistance accordingly (just in slope mode for now).

It’s not properly calibrated yet, but can at least follow a course file, which I do find quite exciting – I still get a buzz out of the resistance changing automatically!

I’ve pushed the initial embedded source code (released under an MIT license) to github https://github.com/dresco/BudgetTrainer.

Remote

remoteIt soon became clear that sweating over my laptop or dev board, trying to press buttons while training was going to end badly, so I needed a way to more easily enter lap markers and manually change gradient while on the bike.

A rummage through the drawer of “things that may come in useful one day” turned up a spare remote from an old fan heater, which was quickly hacked up to provide a wired remote for some up/down, lap, and stop buttons.

Servo

proto3The original servo didn’t have the power to get a full range of motion of the resistance cable. This week a replacement arrived from China, and I’d have to say I’m impressed. For less than £10 delivered, this metal geared analog servo seems solid, quiet, and has plenty of power (rated at 12.2kg*cm).

This seemed like a good opportunity to improve on my original coupling between the resistance cable and the servo arm, so I’ve made up a better linkage using a scrap of nylon sheet and coat hanger wire, and also repositioned the cable adjuster for a straighter run to the servo arm.

Next post I really will try to get onto the calibration! ;)

Development environment

Embedded development environment

This project will be using 8-bit Atmel AVR microcontrollers, simply because they’re what I’m most familiar with. Am developing in C using the AVR port of the GCC compiler.

Initial development was started on an ATmega168 on an Atmel STK500 development board, which provides dual serial ports (programming & application comms), plus leds and buttons for testing. I think it’s fair to say this board is a bit out of date, I suspect most people these days would just opt for an Arduino. The plan is to migrate to an AVR with native USB support for the next prototype, such as the ATmega32U4.

So far, the development on the embedded side has just been a couple of test harnesses. The first receives a number between 1 & 100 over the PC serial link, and sets the cable position accordingly. The second receives the Golden Cheetah real-time training messages, and passes back the button status.

Golden Cheetah development

Adding the new device support to Golden Cheetah was probably the area I was least confident about, but has been going reasonably smoothly (after the usual head scratching when looking at a complex new code base for the first time!). I imported the project into the Eclipse IDE, which has been a big help with following and debugging the code execution.

The new code is based on the existing Comptrainer and Fortius classes, which were fairly easy to get to grips with. I suppose the main difference is that instead of all the usual telemetry being retrieved from the trainer, it’s being retrieved from another real-time source and sent to the trainer (where it can be used by the firmware for the resistance calculations).

Currently, am just working in a local git repository, but when things are a bit more stable, I’ll create a fork on GitHub and push my branch there for more visibility.

Calibration

With the high level motor control and communications code falling into place, probably the next thing to consider is going to be the mapping between GC requests for load/slope and the selected resistance. Next post will cover some first stabs at calibration & creating a speed/power/resistance map.