adds more settings, adds auto adjust for damping

This commit is contained in:
Lars Berning 2021-05-05 12:16:04 +02:00
parent 1b895972af
commit b019fb3bd4
No known key found for this signature in database
GPG Key ID: 028E73C9E1D8A0B3
5 changed files with 75 additions and 53 deletions

View File

@ -27,7 +27,7 @@ function createRowingEngine (rowerSettings) {
// However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below) // However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below)
// To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some // To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some
// strokes on the rower and estimate a value. // strokes on the rower and estimate a value.
const omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare let omegaDotDivOmegaSquare = rowerSettings.omegaDotDivOmegaSquare
// The moment of inertia of the flywheel kg*m^2 // The moment of inertia of the flywheel kg*m^2
// A way to measure it is outlined here: https://dvernooy.github.io/projects/ergware/, "Flywheel moment of inertia" // A way to measure it is outlined here: https://dvernooy.github.io/projects/ergware/, "Flywheel moment of inertia"
@ -49,13 +49,13 @@ function createRowingEngine (rowerSettings) {
const c = rowerSettings.magicConstant const c = rowerSettings.magicConstant
// jMoment * ωdot = -kDamp * ω^2 during non-power part of stroke // jMoment * ωdot = -kDamp * ω^2 during non-power part of stroke
const kDamp = jMoment * omegaDotDivOmegaSquare let kDamp = jMoment * omegaDotDivOmegaSquare
// s = (k/c)^(1/3)*θ // s = (k/c)^(1/3)*θ
const distancePerRevolution = 2.0 * Math.PI * Math.pow((kDamp / c), 1.0 / 3.0) const distancePerRevolution = 2.0 * Math.PI * Math.pow((kDamp / c), 1.0 / 3.0)
let workoutHandler let workoutHandler
const kDampEstimatorAverager = createWeightedAverager(3) const kDampEstimatorAverager = createWeightedAverager(5)
const flankDetector = createMovingFlankDetector(rowerSettings) const flankDetector = createMovingFlankDetector(rowerSettings)
let prevDt = rowerSettings.maximumTimeBetweenImpulses let prevDt = rowerSettings.maximumTimeBetweenImpulses
let kPower = 0.0 let kPower = 0.0
@ -169,8 +169,15 @@ function createRowingEngine (rowerSettings) {
if (strokeElapsed - driveElapsed !== 0) { if (strokeElapsed - driveElapsed !== 0) {
kDampEstimatorAverager.pushValue(kDampEstimator / (strokeElapsed - driveElapsed)) kDampEstimatorAverager.pushValue(kDampEstimator / (strokeElapsed - driveElapsed))
} }
log.debug(`estimated kDamp: ${jMoment * (-1 * kDampEstimatorAverager.weightedAverage())}`) const _kDamp = jMoment * (-1 * kDampEstimatorAverager.weightedAverage())
log.info(`estimated omegaDotDivOmegaSquare: ${-1 * kDampEstimatorAverager.weightedAverage()}`) const _omegaDotDivOmegaSquare = -1 * kDampEstimatorAverager.weightedAverage()
log.debug(`estimated kDamp: ${_kDamp}`)
log.info(`estimated omegaDotDivOmegaSquare: ${_omegaDotDivOmegaSquare}`)
if (rowerSettings.autoAdjustDampingConstant) {
log.debug('auto adjusting kDamp and omegaDotDivOmegaSquare to new values')
kDamp = _kDamp
omegaDotDivOmegaSquare = _omegaDotDivOmegaSquare
}
workoutHandler.handleStrokeStateChanged({ workoutHandler.handleStrokeStateChanged({
strokeState: 'DRIVING' strokeState: 'DRIVING'
}) })

View File

@ -15,24 +15,26 @@ import log from 'loglevel'
log.setLevel(config.loglevel.default) log.setLevel(config.loglevel.default)
export function createGpioTimerService () { export function createGpioTimerService () {
// setting top (near-real-time) priority for the Gpio process, as we don't want to miss anything
log.debug('setting priority for the Gpio-service to maximum (-20)')
try {
// setting priority of current process
os.setPriority(-20)
} catch (err) {
log.debug('need root permission to set priority of Gpio-Thread')
}
if (Gpio.accessible) { if (Gpio.accessible) {
// mode can be rising, falling, both if (config.gpioHighPriority) {
const reedSensor = new Gpio(17, 'in', 'rising') // setting top (near-real-time) priority for the Gpio process, as we don't want to miss anything
log.debug('setting priority for the Gpio-service to maximum (-20)')
try {
// setting priority of current process
os.setPriority(-20)
} catch (err) {
log.debug('need root permission to set priority of Gpio-Thread')
}
}
// read the sensor data from one of the Gpio pins of Raspberry Pi
const sensor = new Gpio(config.gpioPin, 'in', 'rising')
// use hrtime for time measurement to get a higher time precision // use hrtime for time measurement to get a higher time precision
let hrStartTime = process.hrtime() let hrStartTime = process.hrtime()
// assumes that GPIO-Port 17 is set to pullup and reed is connected to GND // assumes that GPIO-Port 17 is set to pullup and reed is connected to GND
// therefore the value is 1 if the reed sensor is open // therefore the value is 1 if the reed sensor is open
reedSensor.watch((err, value) => { sensor.watch((err, value) => {
if (err) { if (err) {
throw err throw err
} }

View File

@ -7,46 +7,60 @@
!!! Note that changes to this file will be OVERWRITTEN when you update to a new version !!! Note that changes to this file will be OVERWRITTEN when you update to a new version
of Open Rowing Monitor. !!! of Open Rowing Monitor. !!!
To change the settings you should modify the 'config.js' in this folder. Simply copy the To change the settings you should modify the 'config/config.js' file. Simply copy the
options that you would like to change into that file. If 'config.js' does not exist, you options that you would like to change into that file. If 'config.js' does not exist, you
can use the example file from the 'install' folder. can use the example file from the 'install' folder.
*/ */
import rowerProfiles from './rowerProfiles.js' import rowerProfiles from './rowerProfiles.js'
export default { export default {
// available log levels: trace, debug, info, warn, error, silent // Available log levels: trace, debug, info, warn, error, silent
loglevel: { loglevel: {
// the default loglevel // The default loglevel
default: 'info', default: 'info',
// the log level of of the rowing engine (stroke detection and physics model) // The log level of of the rowing engine (stroke detection and physics model)
RowingEngine: 'warn' RowingEngine: 'warn'
}, },
// selects the Bluetooth Low Energy Profile // Defines the GPIO Pin that is used to read the sensor data from the rowing machine
// supported modes: FTMS, FTMSBIKE, PM5 // see: https://www.raspberrypi.org/documentation/usage/gpio for the pin layout of the device
// If you want to use the internal pull-up resistor of the Raspberry Pi you should
// also configure the pin for that in /boot/config.txt, i.e. 'gpio=17=pu,ip'
// see: https://www.raspberrypi.org/documentation/configuration/config-txt/gpio.md
gpioPin: 17,
// Experimental setting: enable this to boost the system level priority of the thread that
// measures the rotation speed of the flywheel. This might improve the precision of the
// measurements (especially on rowers with a fast spinning flywheel)
gpioHighPriority: false,
// Selects the Bluetooth Low Energy Profile
// Supported modes: FTMS, FTMSBIKE, PM5
bluetoothMode: 'FTMS', bluetoothMode: 'FTMS',
// turn this on if you want support for Bluetooth Low Energy heart rate monitors // Turn this on if you want support for Bluetooth Low Energy heart rate monitors
// will currenty the hear rate and battery level of the first device found // Will currenty connect to the first device found
heartrateMonitorBLE: true, heartrateMonitorBLE: true,
// turn this on if you want support for ANT+ heart rate monitors // Turn this on if you want support for ANT+ heart rate monitors
// you will need an ANT+ USB stick for this to work, the following models might work: // You will need an ANT+ USB stick for this to work, the following models might work:
// - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008) // - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008)
// - Garmin mini ANT+ (ID 0x1009) // - Garmin mini ANT+ (ID 0x1009)
heartrateMonitorANT: false, heartrateMonitorANT: false,
// defines the name that is used to announce the FTMS Rower via Bluetooth Low Energy (BLE) // Defines the name that is used to announce the FTMS Rower via Bluetooth Low Energy (BLE)
// some rowing training applications expect that the rowing device is announced with a certain name // Some rowing training applications expect that the rowing device is announced with a certain name
ftmsRowerPeripheralName: 'OpenRowingMonitor', ftmsRowerPeripheralName: 'OpenRowingMonitor',
// defines the name that is used to announce the FTMS Bike via Bluetooth Low Energy (BLE) // Defines the name that is used to announce the FTMS Bike via Bluetooth Low Energy (BLE)
// most bike training applications are fine with any device name // Most bike training applications are fine with any device name
ftmsBikePeripheralName: 'OpenRowingBike', ftmsBikePeripheralName: 'OpenRowingBike',
// the rower specific settings. Either choose a profile from config/rowerProfiles.js or // The rower specific settings. Either choose a profile from config/rowerProfiles.js or
// define the settings individually. If you find good settings for a new rowing device // define the settings individually. If you find good settings for a new rowing device
// please send them to us (together with a raw recording of 10 strokes) so we can add // please send them to us (together with a raw recording of 10 strokes) so we can add
// the device to the profiles. // the device to the profiles.
// !! Only change this setting in the config/config.js file, and leave this on DEFAULT as that
// is the fallback for the default profile settings
rowerSettings: rowerProfiles.DEFAULT rowerSettings: rowerProfiles.DEFAULT
} }

View File

@ -22,19 +22,20 @@ export default {
minimumTimeBetweenImpulses: 0.014, minimumTimeBetweenImpulses: 0.014,
maximumTimeBetweenImpulses: 0.5, maximumTimeBetweenImpulses: 0.5,
// Percentage change between successive intervals // Percentage change between successive intervals
maximumDownwardChange: 0.2, // effectively the maximum deceleration maximumDownwardChange: 0.25, // effectively the maximum deceleration
maximumUpwardChange: 1.75, // effectively the maximum acceleration maximumUpwardChange: 1.75, // effectively the maximum acceleration
// Settings for the rowing phase detection (in seconds) flankLength: 2,
flankLength: 1,
numberOfErrorsAllowed: 0, numberOfErrorsAllowed: 0,
minimumDriveTime: 0.300, // Settings for the rowing phase detection (in seconds)
minimumRecoveryTime: 0.750, minimumDriveTime: 0.500,
minimumRecoveryTime: 0.800,
// Needed to determine the damping constant of the rowing machine. This value can be measured in the recovery phase // Needed to determine the damping constant of the rowing machine. This value can be measured in the recovery phase
// of the stroke (some ergometers do this constantly). // of the stroke.
// However I still keep it constant here, as I still have to figure out the damping physics of a water rower (see below) // To display it for your rowing machine, set the logging level of the RowingEngine to 'info'. Then start rowing and
// To measure it for your rowing machine, comment in the logging at the end of "startDrivePhase" function. Then do some // you will see the measured values in the log.
// strokes on the rower and estimate a value. // Open Rowing Monitor can also automatically adjust this value based on the measured damping. To do so, set the setting
// autoAdjustDampingConstant to true (see below).
omegaDotDivOmegaSquare: 0.02, omegaDotDivOmegaSquare: 0.02,
// The moment of inertia of the flywheel kg*m^2 // The moment of inertia of the flywheel kg*m^2
@ -43,6 +44,11 @@ export default {
// plausibility. Note that the power also depends on omegaDotDivOmegaSquare (see above). // plausibility. Note that the power also depends on omegaDotDivOmegaSquare (see above).
jMoment: 0.49, jMoment: 0.49,
// Set this to true, if you want to automatically update omegaDotDivOmegaSquare and kDamp based on the measured
// values in the stroke recovery phase. If your rower produces stable damping values, then this could be a good
// option to dynamically adjust your measurements to the damper setting of your rower.
autoAdjustDampingConstant: false,
// A constant that is commonly used to convert flywheel revolutions to a rowed distance // A constant that is commonly used to convert flywheel revolutions to a rowed distance
// see here: http://eodg.atm.ox.ac.uk/user/dudhia/rowing/physics/ergometer.html#section9 // see here: http://eodg.atm.ox.ac.uk/user/dudhia/rowing/physics/ergometer.html#section9
// Concept2 seems to use 2.8, which they admit is an arbitrary number which came close // Concept2 seems to use 2.8, which they admit is an arbitrary number which came close
@ -66,12 +72,6 @@ export default {
numOfImpulsesPerRevolution: 2, numOfImpulsesPerRevolution: 2,
minimumTimeBetweenImpulses: 0.05, minimumTimeBetweenImpulses: 0.05,
maximumTimeBetweenImpulses: 1, maximumTimeBetweenImpulses: 1,
maximumDownwardChange: 0.25,
maximumUpwardChange: 2,
flankLength: 2,
numberOfErrorsAllowed: 0,
minimumDriveTime: 0.500,
minimumRecoveryTime: 0.800,
omegaDotDivOmegaSquare: 0.046, omegaDotDivOmegaSquare: 0.046,
jMoment: 0.49, jMoment: 0.49,
liquidFlywheel: true liquidFlywheel: true
@ -82,12 +82,6 @@ export default {
numOfImpulsesPerRevolution: 1, numOfImpulsesPerRevolution: 1,
minimumTimeBetweenImpulses: 0.15, minimumTimeBetweenImpulses: 0.15,
maximumTimeBetweenImpulses: 0.5, maximumTimeBetweenImpulses: 0.5,
maximumDownwardChange: 0.25,
maximumUpwardChange: 1.75,
flankLength: 2,
numberOfErrorsAllowed: 0,
minimumDriveTime: 0.500,
minimumRecoveryTime: 0.800,
omegaDotDivOmegaSquare: 0.019, omegaDotDivOmegaSquare: 0.019,
jMoment: 0.4, jMoment: 0.4,
liquidFlywheel: true liquidFlywheel: true

View File

@ -30,5 +30,10 @@ export default {
jMoment: 0.3, jMoment: 0.3,
liquidFlywheel: false liquidFlywheel: false
} }
// example: set a rower profile, but overwrite some settings:
rowerSettings: Object.assign(rowerProfiles.DKNR320, {
autoAdjustDampingConstant: true
})
*/ */
} }