05 Calibration system - sghctoma/sst GitHub Wiki
Intro
Sensor calibration is done through two entities: a calibration method and an actual calibration. A calibration method is basically a set of input variable names and a mathematical expression that describe how to convert a measurement to suspension movement, while calibrations define the actual values for those variables.
Besides a name and description, a calibration method has three fields:
inputs: a list of variable names that will be accessible in theexpression. Their values need to be set in a calibration object.intermediates: a dictionary of name:expression pairs. These expressions are evaluated only once, when the calibration method is loaded. We can use their values in theexpressionvia theirname. In other words, they are just precomputed parts of an expression. They exist purely for optimization purposes, but using them may also make a complex expression more readable.expression: A mathematical expression that is run on each measured sample, and the result is used as suspension movement.
Expressions
Gosst uses the Expr package to evaluate these expressions, but only a minimal subset of the language is supported in addition to some other constants and functions. In summary, expressions can contain the following:
Literals
| Description | Expression |
|---|---|
| Integers | e.g. 1337, 0xebfe |
| Floats | e.g. 0.13, .42 |
Constants
| Description | Expression |
|---|---|
| Calibration variable | name of inputs and intermediates |
| Maximum suspension stroke | MAX_STROKE |
| Maximum wheel movement | MAX_TRAVEL |
| The number π | pi |
| Current measurement datapoint | sample |
Operators
| Description | Expression |
|---|---|
| Arithmetic operators | +, -, *, /, % (modulus), ** (exponentiation) |
Functions
| Description | Expression |
|---|---|
| Trigonometric functions | sin, cos, tan, asin, acos, atan |
| Square root function | sqrt |
Examples
Let's see a JSON representation of a simple one, where the measurement is just the percentage of the full stroke:
{
"name": "percentage",
"description": "Measurement is in percentage of maximum suspension stroke.",
"inputs": [],
"intermediates": {}
"expression": "sample * MAX_STROKE / 100.0"
}
We can see that inputs is empty, which means this method does not require a configurable input, it simply returns sample % of the maximum stroke. It is obvious that MAX_STROKE / 100.0 is the same for every sample, so we can optimize this method using an intermediate:
{
"name": "percentage",
"description": "Measurement is in percentage of maximum suspension stroke.",
"inputs": [],
"intermediates": {
"factor": "MAX_STROKE / 100.0"
},
"expression": "sample * factor"
}
Let's see how the original isosceles triangle calibration mode looks like as a more complex example:
{
"name": "as5600-isosceles-triangle",
"description": "Isosceles triangle setup with the sensor between the unknown and one of the known sides (optimized version).",
"inputs": [
"arm",
"max"
],
"intermediates": {
"start_angle": "acos(max / 2.0 / arm)",
"to_rad_factor": "2.0 * pi / 4096",
"double_arm": "2.0 * arm"
},
"expression": "max - (double_arm * cos((to_rad_factor * sample) + start_angle))"
}
This one has configurable inputs, and a few intermediates, but still easy to understand. A calibration that uses this method looks something like this (where method_id is the database row id of the above calibration method)
"name": "clash-mezzer",
"method_id": 1,
"inputs": {
"arm": 134.9375,
"max":234.15625
}