Robot Control Library
SISO_Filter

Description

Functions for generating and implementing discrete SISO filters.

<rc/math/filter.h>

This discrete filter implementation allows for easy implementation of arbitrary transfer functions in code. It keeps track of all previous inputs and outputs in ring buffers for efficiency. All computation is done using single-precision doubleing point values.

Several functions are providing for generating filter coefficients for the most common functions such as low/high pass filters, moving average filters, PID, and integrators.

See the rc_test_filter.c example for use case.

Author
James Strawson
Date
2016

Data Structures

struct  rc_filter_t
 Struct containing configuration and state of a SISO filter. More...
 

Macros

#define RC_FILTER_INITIALIZER
 

Typedefs

typedef struct rc_filter_t rc_filter_t
 Struct containing configuration and state of a SISO filter. More...
 

Functions

rc_filter_t rc_filter_empty (void)
 Critical function for initializing rc_filter_t structs. More...
 
int rc_filter_alloc (rc_filter_t *f, rc_vector_t num, rc_vector_t den, double dt)
 Allocate memory for a discrete-time filter & populates it with the transfer function coefficients provided in vectors num and den. More...
 
int rc_filter_alloc_from_arrays (rc_filter_t *f, double dt, double *num, int numlen, double *den, int denlen)
 Like rc_filter_alloc(), but takes arrays for the numerator and denominator coefficients instead of vectors. More...
 
int rc_filter_duplicate (rc_filter_t *f, rc_filter_t old)
 duplicates a filter More...
 
int rc_filter_free (rc_filter_t *f)
 Frees the memory allocated by a filter's buffers and coefficient vectors. Also resets all filter properties back to 0. More...
 
int rc_filter_print (rc_filter_t f)
 Prints the transfer function and other statistic of a filter to the screen. More...
 
double rc_filter_march (rc_filter_t *f, double new_input)
 March a filter forward one step with new input provided as an argument. More...
 
int rc_filter_reset (rc_filter_t *f)
 Resets all previous inputs and outputs to 0. Also resets the step counter & saturation flag. More...
 
int rc_filter_enable_saturation (rc_filter_t *f, double min, double max)
 Enables saturation between bounds min and max. More...
 
int rc_filter_get_saturation_flag (rc_filter_t *f)
 Checks if the filter saturated the last time step. More...
 
int rc_filter_enable_soft_start (rc_filter_t *f, double seconds)
 Enables soft start functionality where the output bound is gradually opened linearly from 0 to the normal saturation range. More...
 
double rc_filter_previous_input (rc_filter_t *f, int steps)
 Returns the input 'steps' back in time. Steps=0 returns most recent input. More...
 
double rc_filter_previous_output (rc_filter_t *f, int steps)
 Returns the output 'steps' back in time. Steps = 0 returns most recent output. More...
 
int rc_filter_prefill_inputs (rc_filter_t *f, double in)
 Fills all previous inputs to the filter as if they had been equal to 'in'. More...
 
int rc_filter_prefill_outputs (rc_filter_t *f, double out)
 Fills all previous outputs of the filter as if they had been equal to 'out'. More...
 
int rc_filter_multiply (rc_filter_t f1, rc_filter_t f2, rc_filter_t *out)
 Creates a new filter 'out' by multiplying f1*f2. More...
 
int rc_filter_multiply_three (rc_filter_t f1, rc_filter_t f2, rc_filter_t f3, rc_filter_t *out)
 Creates a new filter 'out' by multiplying f1*f2*f3. More...
 
int rc_filter_c2d_tustin (rc_filter_t *f, double dt, rc_vector_t num, rc_vector_t den, double w)
 Creates a discrete time filter with similar dynamics to a provided continuous time transfer function using tustin's approximation with prewarping about a frequency of interest 'w' in radians per second. More...
 
int rc_filter_normalize (rc_filter_t *f)
 Normalizes a discrete time filter so that the leading demoninator coefficient is 1. More...
 
int rc_filter_first_order_lowpass (rc_filter_t *f, double dt, double tc)
 Creates a first order low pass filter. More...
 
int rc_filter_first_order_highpass (rc_filter_t *f, double dt, double tc)
 Creates a first order high pass filter. More...
 
int rc_filter_butterworth_lowpass (rc_filter_t *f, int order, double dt, double wc)
 Creates a Butterworth low pass filter of specified order and cutoff frequency. More...
 
int rc_filter_butterworth_highpass (rc_filter_t *f, int order, double dt, double wc)
 Creates a Butterworth high pass filter of specified order and cutoff frequency. More...
 
int rc_filter_moving_average (rc_filter_t *f, int samples, double dt)
 Makes a FIR moving average filter that averages over specified number of samples. More...
 
int rc_filter_integrator (rc_filter_t *f, double dt)
 Creates a first order integrator. More...
 
int rc_filter_double_integrator (rc_filter_t *f, double dt)
 Creates a second order double integrator. More...
 
int rc_filter_pid (rc_filter_t *f, double kp, double ki, double kd, double Tf, double dt)
 Creates a discrete-time implementation of a parallel PID controller with high-frequency rolloff using the forward-Euler integration method. More...
 
int rc_filter_third_order_complement (rc_filter_t *lp, rc_filter_t *hp, double freq, double damp, double dt)
 Creates a third order symmetric complementary pair of high/low pass filters. More...
 

Macro Definition Documentation

◆ RC_FILTER_INITIALIZER

#define RC_FILTER_INITIALIZER
Value:
{\
.order = 0,\
.dt = 0.0,\
.gain = 1.0,\
.sat_en = 0,\
.sat_min = 0.0,\
.sat_max = 0.0,\
.sat_flag = 0,\
.ss_en = 0,\
.ss_steps = 0,\
.newest_input = 0.0,\
.newest_output = 0.0,\
.step = 0,\
.initialized = 0}
#define RC_RINGBUF_INITIALIZER
Definition: ring_buffer.h:41
#define RC_VECTOR_INITIALIZER
Definition: vector.h:48
Examples:
rc_altitude.c, rc_balance.c, rc_test_bmp.c, rc_test_complementary_filters.c, and rc_test_filters.c.

Function Documentation

◆ rc_filter_empty()

rc_filter_t rc_filter_empty ( void  )

Critical function for initializing rc_filter_t structs.

This is a very important function. If your rc_filter_t struct is not a global variable, then its initial contents cannot be guaranteed to be anything in particular. Therefore it could contain problematic contents which could interfere with functions in this library. Therefore, you should always initialize your filters with rc_filter_empty before using with any other function in this library such as rc_filter_alloc. This serves the same purpose as rc_matrix_empty, rc_vector_empty, and rc_ringbuf_empty.

Returns
Empty zero-filled rc_filter_t struct

◆ rc_filter_alloc()

int rc_filter_alloc ( rc_filter_t f,
rc_vector_t  num,
rc_vector_t  den,
double  dt 
)

Allocate memory for a discrete-time filter & populates it with the transfer function coefficients provided in vectors num and den.

The memory in num and den is duplicated so those vectors can be reused or freed after allocating a filter without fear of disturbing the function of the filter. Argument dt is the timestep in seconds at which the user expects to operate the filter. The length of demonimator den must be at least as large as numerator num to avoid describing an improper transfer function. If rc_filter_t pointer f points to an existing filter then the old filter's contents are freed safely to avoid memory leaks. We suggest initializing filter f with rc_filter_empty before calling this function if it is not a global variable to ensure it does not accidentally contain invlaid contents such as null pointers. The filter's order is derived from the length of the denominator polynomial.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]numThe numerator vector
[in]denThe denomenator vector
[in]dtTimestep in seconds
Returns
0 on success or -1 on failure.

◆ rc_filter_alloc_from_arrays()

int rc_filter_alloc_from_arrays ( rc_filter_t f,
double  dt,
double *  num,
int  numlen,
double *  den,
int  denlen 
)

Like rc_filter_alloc(), but takes arrays for the numerator and denominator coefficients instead of vectors.

Arrays num and den must have lengths that form a proper or semi-proper transfer function.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtTimestep in seconds
[in]numpointer to numerator array
[in]numlenThe numerator length
[in]denpointer to denominator array
[in]denlenThe denominator length
Returns
0 on success or -1 on failure.
Examples:
rc_balance.c.

◆ rc_filter_duplicate()

int rc_filter_duplicate ( rc_filter_t f,
rc_filter_t  old 
)

duplicates a filter

This allocates new memory in filter f so it can be used independently from the old filter but with the same configuration.

Parameters
fnew filter to allocate
[in]oldold filter to copy
Returns
{ description_of_the_return_value }

◆ rc_filter_free()

int rc_filter_free ( rc_filter_t f)

Frees the memory allocated by a filter's buffers and coefficient vectors. Also resets all filter properties back to 0.

Parameters
fPointer to user's rc_filter_t struct
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c, and rc_test_complementary_filters.c.

◆ rc_filter_print()

int rc_filter_print ( rc_filter_t  f)

Prints the transfer function and other statistic of a filter to the screen.

Only works on filters up to order 9.

Parameters
fPointer to user's rc_filter_t struct
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c, rc_test_complementary_filters.c, and rc_test_filters.c.

◆ rc_filter_march()

double rc_filter_march ( rc_filter_t f,
double  new_input 
)

March a filter forward one step with new input provided as an argument.

If saturation or soft-start are enabled then the output will automatically be bound appropriately. The steps counter is incremented by one and internal ring buffers are updated accordingly. Once a filter is created, this is typically the only function required afterwards.

Parameters
fPointer to user's rc_filter_t struct
[in]new_inputThe new input
Returns
Returns the new output which could also be accessed with the newest_output field in the filter struct.
Examples:
rc_altitude.c, rc_balance.c, rc_test_bmp.c, rc_test_complementary_filters.c, and rc_test_filters.c.

◆ rc_filter_reset()

int rc_filter_reset ( rc_filter_t f)

Resets all previous inputs and outputs to 0. Also resets the step counter & saturation flag.

This is sufficient to start the filter again as if it were just created.

Parameters
fPointer to user's rc_filter_t struct
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c.

◆ rc_filter_enable_saturation()

int rc_filter_enable_saturation ( rc_filter_t f,
double  min,
double  max 
)

Enables saturation between bounds min and max.

If saturation is enabled for a specified filter, the filter will automatically bound the output between min and max. You may ignore this function if you wish the filter to run unbounded. Max must be greater than or equal to min. If max==min, the output will be fixed at that value. Any double-precision floating point value is allowed, positive or negative.

Parameters
fPointer to user's rc_filter_t struct
[in]minThe lower bound
[in]maxThe upper bound
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c.

◆ rc_filter_get_saturation_flag()

int rc_filter_get_saturation_flag ( rc_filter_t f)

Checks if the filter saturated the last time step.

This information could also be retrieved by looking at the 'sat_flag' value in the filter struct.

Parameters
fPointer to user's rc_filter_t struct
Returns
Returns 1 if the filter saturated the last time step. Returns 0 otherwise.

◆ rc_filter_enable_soft_start()

int rc_filter_enable_soft_start ( rc_filter_t f,
double  seconds 
)

Enables soft start functionality where the output bound is gradually opened linearly from 0 to the normal saturation range.

This occurs over the time specified from argument 'seconds' from when the filter is first created or reset. Saturation must already be enabled for this to work. This assumes that the user does indeed call rc_filter_march at roughly the same time interval as the 'dt' variable in the filter struct which is set at creation time. The soft-start property is maintained through a call to rc_filter_reset so the filter will soft-start again after each reset. This feature should only really be used for feedback controllers to prevent jerky starts. The saturation flag will not be set during this period as the output is usually expected to be bounded and we don't want to falsely trigger alarms or saturation counters.

Parameters
fPointer to user's rc_filter_t struct
[in]secondsTime in seconds
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c.

◆ rc_filter_previous_input()

double rc_filter_previous_input ( rc_filter_t f,
int  steps 
)

Returns the input 'steps' back in time. Steps=0 returns most recent input.

'steps' must be between 0 and order inclusively as those are the only steps retained in memory for normal filter operation. To record values further back in time we suggest creating your own rc_ringbuf_t ring buffer.

Parameters
fPointer to user's rc_filter_t struct
[in]stepsThe steps back in time, steps=0 returns most recent input.
Returns
Returns the requested previous input. If there is an error, returns -1.0f and prints an error message.

◆ rc_filter_previous_output()

double rc_filter_previous_output ( rc_filter_t f,
int  steps 
)

Returns the output 'steps' back in time. Steps = 0 returns most recent output.

'steps' must be between 0 and order inclusively as those are the only steps retained in memory for normal filter operation. To record values further back in time we suggest creating your own rc_ringbuf_t ring buffer.

Parameters
fPointer to user's rc_filter_t struct
[in]stepsThe steps back in time, steps=0 returns most recent output.
Returns
Returns the requested previous output. If there is an error, returns -1.0f and prints an error message.

◆ rc_filter_prefill_inputs()

int rc_filter_prefill_inputs ( rc_filter_t f,
double  in 
)

Fills all previous inputs to the filter as if they had been equal to 'in'.

Most useful when starting high-pass filters to prevent unwanted jumps in the output when starting with non-zero input.

Parameters
fPointer to user's rc_filter_t struct
[in]inInput value to fill
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_altitude.c, and rc_test_bmp.c.

◆ rc_filter_prefill_outputs()

int rc_filter_prefill_outputs ( rc_filter_t f,
double  out 
)

Fills all previous outputs of the filter as if they had been equal to 'out'.

Most useful when starting low-pass filters to prevent unwanted settling time when starting with non-zero input.

Parameters
fPointer to user's rc_filter_t struct
[in]outoutput value to fill
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_altitude.c, and rc_test_bmp.c.

◆ rc_filter_multiply()

int rc_filter_multiply ( rc_filter_t  f1,
rc_filter_t  f2,
rc_filter_t out 
)

Creates a new filter 'out' by multiplying f1*f2.

The contents of f3 are freed safely if necessary and new memory is allocated to avoid memory leaks.

Parameters
[in]f1Pointer to user's rc_filter_t struct to be multiplied
[in]f2Pointer to user's rc_filter_t struct to be multiplied
[out]outPointer to newly created filter struct
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_multiply_three()

int rc_filter_multiply_three ( rc_filter_t  f1,
rc_filter_t  f2,
rc_filter_t  f3,
rc_filter_t out 
)

Creates a new filter 'out' by multiplying f1*f2*f3.

The contents of f3 are freed safely if necessary and new memory is allocated to avoid memory leaks.

Parameters
[in]f1Pointer to user's rc_filter_t struct to be multiplied
[in]f2Pointer to user's rc_filter_t struct to be multiplied
[in]f3Pointer to user's rc_filter_t struct to be multiplied
[out]outPointer to newly created filter struct
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_c2d_tustin()

int rc_filter_c2d_tustin ( rc_filter_t f,
double  dt,
rc_vector_t  num,
rc_vector_t  den,
double  w 
)

Creates a discrete time filter with similar dynamics to a provided continuous time transfer function using tustin's approximation with prewarping about a frequency of interest 'w' in radians per second.

Any existing memory allocated for f is freed is necessary to prevent memory leaks. Returns 0 on success or -1 on failure.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtdesired timestep of discrete filter in seconds
[in]numcontinuous time numerator coefficients
[in]dencontinuous time denominator coefficients
[in]wprewarping frequency in rad/s
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_normalize()

int rc_filter_normalize ( rc_filter_t f)

Normalizes a discrete time filter so that the leading demoninator coefficient is 1.

Parameters
fPointer to user's rc_filter_t struct
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_first_order_lowpass()

int rc_filter_first_order_lowpass ( rc_filter_t f,
double  dt,
double  tc 
)

Creates a first order low pass filter.

Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter. dt is in units of seconds and time_constant is the number of seconds it takes to rise to 63.4% of a steady-state input. This can be used alongside rc_first_order_highpass to make a complementary filter pair.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtdesired timestep of discrete filter in seconds
[in]tctime constant: Seconds it takes to rise to 63.4% of a steady-state input
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_altitude.c, rc_test_complementary_filters.c, and rc_test_filters.c.

◆ rc_filter_first_order_highpass()

int rc_filter_first_order_highpass ( rc_filter_t f,
double  dt,
double  tc 
)

Creates a first order high pass filter.

Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter. dt is in units of seconds and time_constant is the number of seconds it takes to decay by 63.4% of a steady-state input. This can be used alongside rc_first_order_highpass to make a complementary filter pair.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtdesired timestep of discrete filter in seconds
[in]tctime constant: Seconds it takes to decay by 63.4% of a steady-state input
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_test_complementary_filters.c, and rc_test_filters.c.

◆ rc_filter_butterworth_lowpass()

int rc_filter_butterworth_lowpass ( rc_filter_t f,
int  order,
double  dt,
double  wc 
)

Creates a Butterworth low pass filter of specified order and cutoff frequency.

Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]orderThe order (>=1)
[in]dtdesired timestep of discrete filter in seconds
[in]wcCuttoff freqauency in rad/s
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_test_bmp.c, and rc_test_filters.c.

◆ rc_filter_butterworth_highpass()

int rc_filter_butterworth_highpass ( rc_filter_t f,
int  order,
double  dt,
double  wc 
)

Creates a Butterworth high pass filter of specified order and cutoff frequency.

Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]orderThe order (>=1)
[in]dtdesired timestep of discrete filter in seconds
[in]wcCuttoff freqauency in rad/s
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_test_filters.c.

◆ rc_filter_moving_average()

int rc_filter_moving_average ( rc_filter_t f,
int  samples,
double  dt 
)

Makes a FIR moving average filter that averages over specified number of samples.

Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter.

Note that the timestep dt does not effect the dynamics of the produced filter. It is simply copied into the 'dt' field of the rc_filter_t struct. However, it is necessary for creation of this filter for compatability with the soft-start feature and any other user codepaths that may be dependent on a filter's timestep.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]samplesThe samples to average over (>=2)
[in]dtdesired timestep of discrete filter in seconds
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_integrator()

int rc_filter_integrator ( rc_filter_t f,
double  dt 
)

Creates a first order integrator.

Like most functions here, the dynamics are only accurate if the filter is called with a timestep corresponding to dt. Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtdesired timestep of discrete filter in seconds
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_test_filters.c.

◆ rc_filter_double_integrator()

int rc_filter_double_integrator ( rc_filter_t f,
double  dt 
)

Creates a second order double integrator.

Like most functions here, the dynamics are only accurate if the filter is called with a timestep corresponding to dt. Any existing memory allocated for f is freed safely to avoid memory leaks and new memory is allocated for the new filter.

Parameters
[out]fPointer to user's rc_filter_t struct
[in]dtdesired timestep of discrete filter in seconds
Returns
Returns 0 on success or -1 on failure.

◆ rc_filter_pid()

int rc_filter_pid ( rc_filter_t f,
double  kp,
double  ki,
double  kd,
double  Tf,
double  dt 
)

Creates a discrete-time implementation of a parallel PID controller with high-frequency rolloff using the forward-Euler integration method.

This is equivalent to the Matlab function: C = pid(Kp,Ki,Kd,Tf,Ts)

It is not possible to implement a pure differentiator with a discrete transfer function so this filter has high frequency rolloff with time constant Tf. Smaller Tf results in less rolloff, but Tf must be greater than dt/2 for stability. Returns 0 on success or -1 on failure.

Parameters
fPointer to user's rc_filter_t struct
[in]kpProportional constant
[in]kiIntegration constant
[in]kdDerivative constant
[in]TfHigh Frequency rolloff time constant (seconds)
[in]dtdesired timestep of discrete filter in seconds
Returns
Returns 0 on success or -1 on failure.
Examples:
rc_balance.c.

◆ rc_filter_third_order_complement()

int rc_filter_third_order_complement ( rc_filter_t lp,
rc_filter_t hp,
double  freq,
double  damp,
double  dt 
)

Creates a third order symmetric complementary pair of high/low pass filters.

Parameters
lplowpass filter to be populated
hphighpass filter to be populated
[in]freqcrossover frequency in rad/s
[in]dampDamping ratio >0, 1 is critically damped, lower than that gets wobbly
[in]dtdesired timestep of discrete filter in seconds
Returns
0 on success or -1 on failure.
Examples:
rc_test_complementary_filters.c.