@sevthjs/kalman-filter
Version:
Kalman filter (and Extended Kalman Filter) Multi-dimensional implementation in Javascript
559 lines (537 loc) • 18.4 kB
TypeScript
/**
* Enables to register observation model and store it
* @param {String} name
* @callback fn the function corresponding to the desired model
*/
declare function registerObservation(name: string, fn: any): void;
/**
* Enables to register dynamic model and store it
* @param {String} name
* @callback fn the function corresponding to the desired model
*/
declare function registerDynamic(name: string, fn: any): void;
/**
* Build a model given an observation configuration
* @param {ObservationConfig} observation
* @returns {ObservationConfig} the configuration with respect to the model
*/
declare function buildObservation(observation: any): any;
/**
* Build a model given dynamic and observation configurations
* @param {DynamicConfig} dynamic
* @param {ObservationConfig} observation
* @returns {DynamicConfig} the dynamic configuration with respect to the model
*/
declare function buildDynamic(dynamic: any, observation: any): any;
/**
* Creates a dynamic model, following constant position model with respect with the dimensions provided in the observation parameters
* @param {DynamicConfig} dynamic
* @param {ObservationConfig} observation
* @returns {DynamicConfig}
*/
declare function constantPosition(dynamic: any, observation: any): any;
/**
*Creates a dynamic model, following constant position model with respect with the dimensions provided in the observation parameters
* @param {DynamicConfig} dynamic
* @param {ObservationConfig} observation
* @returns {DynamicConfig}
*/
declare function constantSpeed(dynamic: any, observation: any): any;
/**
* Creates a dynamic model, following constant acceleration model with respect with the dimensions provided in the observation parameters
* @param {DynamicConfig} dynamic
* @param {ObservationConfig} observation
* @returns {DynamicConfig}
*/
declare function constantAcceleration(dynamic: any, observation: any): any;
/**
* @typedef {Object.<DynamicName, DynamicConfig>} PerNameConfigs
*/
/**
* @typedef {Object} DynamicConfig
* @param {Array.<Number>} obsIndexes
* @param {Covariance} staticCovariance
*/
/**
* Creates a dynamic model, considering the null in order to make the predictions
* @param {Object} main
* @param {Object.<String, DynamicConfig>} main.perName
* @param {ObservationConfig} observation
* @param {Array.<Array.<Number>>} opts.observedProjection
* @returns {DynamicConfig}
*/
declare function composition({ perName }: {
perName: any;
}, observation: any): {
dimension: any;
init: {
index: number;
mean: any[];
covariance: any[][];
};
transition(options: any): any[][];
covariance(options: any): any[][];
};
/**
* Creates a dynamic model, considering the null in order to make the predictions
* @param {Array.<Array.<Number>>} staticCovariance generated with moving average
* @param {Number} observationDimension
* @returns {DynamicConfig}
*/
declare function constantPositionWithNull({ staticCovariance, obsDynaIndexes, init }: {
staticCovariance: any;
obsDynaIndexes: any;
init: any;
}): {
dimension: any;
transition(): number[][];
covariance({ previousCorrected, index }: {
previousCorrected: any;
index: any;
}): any;
init: any;
};
interface StateLT {
mean: number[][];
covariance: number[][];
index?: number;
}
interface Observation {
name: string;
}
type PreviousCorrectedCallback = (opts: {
index: number;
previousCorrected?: StateLT;
predicted: StateLT;
variance?: number[];
}) => number[][];
type PredictedCallback = (opts: {
index: number;
previousCorrected?: StateLT;
predicted: StateLT;
observation?: Observation;
}) => number[][];
interface WinstonLogger {
info: (...args: any[]) => void;
debug: (...args: any[]) => void;
warn: (...args: any[]) => void;
error: (...args: any[]) => void;
}
interface DynamicConfig {
/**
* named this config.
*/
name?: string;
dimension?: number;
/**
* a function that returns the control parameter B_k*u_k of the kalman filter
*/
constant?: PreviousCorrectedCallback;
/**
* for extended kalman filter only, the non-linear state-transition model
*/
fn?: PreviousCorrectedCallback;
/**
* the state-transition model (or for EKF the jacobian of the fn)
*/
transition: number[][] | PredictedCallback;
/**
* covariance the covariance of the process noise
*/
covariance: number[] | number[][] | PredictedCallback;
/**
*
*/
init: StateLT;
timeStep?: number;
}
interface CoreConfig {
/**
* dynamic the system's dynamic model
*/
dynamic: DynamicConfig;
/**
* the system's observation model
*/
observation: ObservationConfig;
/**
* a Winston-like logger
*/
logger?: WinstonLogger;
}
interface ObservationConfig {
sensorDimension?: number;
dimension: number;
nSensors?: number;
observedProjection?: any;
fn?: PredictedCallback;
/**
* stateProjection the matrix to transform state to observation (for EKF, the jacobian of the fn)
*/
stateProjection?: number | number[] | number[][] | PreviousCorrectedCallback;
/**
* covariance the covariance of the observation noise
*/
covariance: number[] | number[][] | PreviousCorrectedCallback;
sensorCovariance?: number[];
name?: 'sensor' | string;
}
declare class CoreKalmanFilter {
dynamic: DynamicConfig;
observation: ObservationConfig;
logger: WinstonLogger;
constructor(options: CoreConfig);
getValue(fn: number[][] | PreviousCorrectedCallback | PredictedCallback, options: any): number[][];
getInitState(): State;
/**
This will return the predicted covariance of a given previousCorrected State, this will help us to build the asymptoticState.
* @param {State} previousCorrected
* @returns{Array.<Array.<Number>>}
*/
getPredictedCovariance(options?: {
previousCorrected?: State;
index?: number;
}): number[][];
predictMean(o: {
opts: any;
transition: number[][];
}): number[][];
predictMeanWithoutControl(args: {
opts: any;
transition: number[][];
}): number[][];
/**
This will return the new prediction, relatively to the dynamic model chosen
* @param {State} previousCorrected State relative to our dynamic model
* @returns{State} predicted State
*/
predict(options?: {
previousCorrected?: State;
index?: number;
observation?: number[] | number[][];
}): State;
/**
* This will return the new correction, taking into account the prediction made
* and the observation of the sensor
* param {State} predicted the previous State
* @param options
* @returns kalmanGain
*/
getGain(options: {
predicted: State;
stateProjection?: number[][];
}): number[][];
/**
* This will return the corrected covariance of a given predicted State, this will help us to build the asymptoticState.
* @param {State} predicted the previous State
* @returns{Array.<Array.<Number>>}
*/
getCorrectedCovariance(options: {
predicted: State;
optimalKalmanGain?: any;
stateProjection?: any;
}): number[][];
getPredictedObservation(args: {
opts: any;
stateProjection: number[][];
}): number[][];
/**
This will return the new correction, taking into account the prediction made
and the observation of the sensor
* @param {State} predicted the previous State
* @param {Array} observation the observation of the sensor
* @returns{State} corrected State of the Kalman Filter
*/
correct(options: {
predicted: any;
observation: any;
}): State;
}
declare class KalmanFilter extends CoreKalmanFilter {
/**
* @typedef {Object} Config
* @property {DynamicObjectConfig | DynamicNonObjectConfig} dynamic
* @property {ObservationObjectConfig | ObservationNonObjectConfig} observation
*/
/**
* @param {Config} options
*/
constructor(options?: {
observation?: any | {
name: string;
};
dynamic?: any | {
name: string;
};
logger?: WinstonLogger;
});
correct(options: {
predicted: State;
observation: number[] | number[][];
}): State;
/**
* Performs the prediction and the correction steps
* @param {State} previousCorrected
* @param {<Array.<Number>>} observation
* @returns {Array.<Number>} the mean of the corrections
*/
filter(options: {
previousCorrected?: State;
index?: number;
observation: number[] | number[][];
}): State;
/**
* Filters all the observations
* @param {Array.<Array.<Number>>} observations
* @returns {Array.<Array.<Number>>} the mean of the corrections
*/
filterAll(observations: any): number[][];
/**
* Returns an estimation of the asymptotic state covariance as explained in https://en.wikipedia.org/wiki/Kalman_filter#Asymptotic_form
* in practice this can be used as a init.covariance value but is very costful calculation (that's why this is not made by default)
* @param {Number} [limitIterations=1e2] max number of iterations
* @param {Number} [tolerance=1e-6] returns when the last values differences are less than tolerance
* @return {Array.<Array.<Number>>} covariance
*/
asymptoticStateCovariance({ limitIterations, tolerance }?: {
limitIterations?: number;
tolerance?: number;
}): number[][];
/**
* Returns an estimation of the asymptotic gain, as explained in https://en.wikipedia.org/wiki/Kalman_filter#Asymptotic_form
* @param {Number} [tolerance=1e-6] returns when the last values differences are less than tolerance
* @return {Array.<Array.<Number>>} gain
*/
asymptoticGain({ tolerance }?: {
tolerance?: number;
}): number[][];
}
/**
* Class representing a multi dimensionnal gaussian, with his mean and his covariance
* @property {Number} [index=0] the index of the State in the process, this is not mandatory for simple Kalman Filter, but is needed for most of the use case of extended kalman filter
* @property {Array.<Array.<Number>>} covariance square matrix of size dimension
* @property {Array.<Array<Number>>} mean column matrix of size dimension x 1
*/
declare class State implements StateLT {
mean: number[][];
covariance: number[][];
index: number | undefined;
constructor(args: {
mean: number[][];
covariance: number[][];
index?: number;
});
/**
* Check the consistency of the State
* @param {Object} options
* @see check
*/
check(options?: {
dimension?: number | null;
title?: string;
eigen?: boolean;
}): void;
/**
* Check the consistency of the State's attributes
* @param {State} state
* @param {Object} [options={}]
* @param {Array} [options.dimension=null] if defined check the dimension of the state
* @param {String} [options.title=null] used to log error mor explicitly
* @param {Boolean} options.eigen
* @returns {Null}
*/
static check(state: State, args?: {
dimension?: number | null;
title?: string;
eigen?: boolean;
}): void;
/**
* Multiply state with matrix
* @param {State} state
* @param {Array.<Array.<Number>>} matrix
* @returns {State}
*/
static matMul(args: {
state: State;
matrix: number[][];
}): State;
/**
* From a state in n-dimension create a state in a subspace
* If you see the state as a N-dimension gaussian,
* this can be viewed as the sub M-dimension gaussian (M < N)
* @param {Array.<Number>} obsIndexes list of dimension to extract, (M < N <=> obsIndexes.length < this.mean.length)
* @returns {State} subState in subspace, with subState.mean.length === obsIndexes.length
*/
subState(obsIndexes: number[]): State;
/**
* @typedef {Object} DetailedMahalanobis
* @property {Array.<[Number]>} diff
* @property {Array.<Array.<Number>>} covarianceInvert
* @property {Number} value
*/
/**
* Simple Malahanobis distance between the distribution (this) and a point
* @param {Array.<[Number]>} point a Nx1 matrix representing a point
* @returns {DetailedMahalanobis}
*/
rawDetailedMahalanobis(point: number[][]): {
diff: number[][];
covarianceInvert: number[][];
value: number;
};
/**
* Malahanobis distance is made against an observation, so the mean and covariance
* are projected into the observation space
* @param {KalmanFilter} kf kalman filter use to project the state in observation's space
* @param {Observation} observation
* @param {Array.<Number>} obsIndexes list of indexes of observation state to use for the mahalanobis distance
* @returns {DetailedMahalanobis}
*/
detailedMahalanobis(args: {
kf: KalmanFilter;
observation: number[][] | number[];
obsIndexes?: number[];
}): {
diff: number[][];
covarianceInvert: number[][];
value: number;
};
/**
* @param {Object} options @see detailedMahalanobis
* @returns {Number}
*/
mahalanobis(options: {
kf: KalmanFilter;
observation: number[][] | number[];
obsIndexes?: number[];
}): number;
/**
* Bhattacharyya distance is made against in the observation space
* to do it in the normal space see state.bhattacharyya
* @param {KalmanFilter} kf kalman filter use to project the state in observation's space
* @param {State} state
* @param {Array.<Number>} obsIndexes list of indexes of observation state to use for the bhattacharyya distance
* @returns {Number}
*/
obsBhattacharyya(options: {
kf: KalmanFilter;
state: State;
obsIndexes: number[];
}): number;
/**
* @param {State} otherState other state to compare with
* @returns {Number}
*/
bhattacharyya(otherState: State): number;
}
/**
* Creates a dynamic model, considering the null in order to make the predictions
* @param {ObservationConfig} observation
* @returns {DynamicConfig}
*/
declare function constantSpeedDynamic(args: {
staticCovariance: number[];
avSpeed: number[];
center: number[];
}, observation: any): {
init: {
mean: number[][];
covariance: number[][];
index: number;
};
dimension: number;
transition: (args: {
getTime: (index: number) => number;
index: number;
previousCorrected: State;
}) => number[][];
covariance: (args: {
index: number;
previousCorrected: State;
getTime: (index: number) => number;
}) => number[][];
};
/**
* This model is based on the constant speed model
* The constant speed model creates problems when dT >> fps (the track is lost)
* then the expected position can be very far from the center of the field
* to solve that, we use a model with 2 more hidden variable that are always center of the field
* When dT << typicalTime the model acts exactly as a constant speed model
* When dT >> typicalTime the model is a constant [x,y] = center model, sigma = defaultVariance
* @param {Object} options
* @param {ObservationConfig} observation
* @param {Number} [options.typicalTime=10]
* @returns {DynamicConfig}
*/
declare function shorttermConstantSpeed(options: any, observation: any): {
dimension: number;
init: {
mean: number[][];
covariance: number[][];
index: number;
};
transition(options: {
getTime: (index: number) => number;
index: number;
previousCorrected: State;
}): number[][];
covariance(options: {
getTime: (index: number) => number;
index: number;
previousCorrected: State;
}, observation: any): number[][];
};
declare function sensor(options: any): ObservationConfig;
/**
* @param {Object} options
* @param {Number} options.sensorDimension
* @param {CovarianceParam} options.sensorCovariance
* @param {Number} options.nSensors
* @returns {ObservationConfig}
*/
declare function nullableSensor(options: any): ObservationConfig;
/**
*Creates an observation model with a observedProjection corresponding to
* @param {DynamicConfig} dynamic
* @param {ObservationConfig} observation
* @returns {DynamicConfig}
*/
declare function sensorProjected({ selectedCovariance, totalDimension, obsIndexes, selectedStateProjection }: {
selectedCovariance: any;
totalDimension: any;
obsIndexes: any;
selectedStateProjection: any;
}): {
dimension: any;
observedProjection: number[][];
covariance(o: any): any;
};
/**
* @param {Object} opts
* @param {Array.<Array.<Number>>} opts.measures a list of measure, size is LxN L the number of sample, N the dimension
* @param {Array.<Array.<Number>>} opts.averages a list of averages, size is LxN L the number of sample, N the dimension
* @returns {Array.<Array.<Number>>} covariance matrix size is NxN
*/
declare function getCovariance({ measures, averages }: {
measures: any;
averages: any;
}): number[][];
declare function checkCovariance(args: {
covariance: number[][];
eigen?: boolean;
}, _title?: string): void;
declare function correlationToCovariance({ correlation, variance }: {
correlation: any;
variance: any;
}): any;
declare function covarianceToCorrelation(covariance: any): {
variance: any;
correlation: any;
};
declare function projectObservation({ observation, obsIndexes, selectedStateProjection, invertSelectedStateProjection }: {
observation: any;
obsIndexes: any;
selectedStateProjection: any;
invertSelectedStateProjection: any;
}): number[];
export { KalmanFilter, State, buildDynamic, buildObservation, checkCovariance, composition, constantAcceleration, constantPosition, constantPositionWithNull, constantSpeed, constantSpeedDynamic, correlationToCovariance, covarianceToCorrelation, getCovariance, projectObservation, registerDynamic, registerObservation, sensor, nullableSensor as sensorLocalVariance, sensorProjected, shorttermConstantSpeed };