Chapter 19: Integration Lab — Building a Complete Robot Stack

Lesson 1: Hardware Setup and Sensor Bring-Up

This lab-style lesson connects the abstract notions of sensing and embedded computation to a concrete, mathematically grounded procedure for taking a physical robot from “powered-off electronics” to a verified, calibrated sensing subsystem ready to close feedback loops. We model sensors as noisy, sampled, quantized measurement channels, derive basic calibration and resolution formulas, and then instantiate those ideas in Python, C++, Java, and MATLAB/Simulink code skeletons for a simple robot sensor bring-up.

1. Lab Context and Objectives

In the overall robot “sense–think–act” loop introduced earlier, this lesson focuses on the sense part, but from a systems and control perspective. For a robot with state \( x_k \) (e.g., position, velocity) and control input \( u_k \), a discrete-time linear control law assumes access to measurements

\[ y_k = C x_k + v_k, \]

where \( y_k \) are sensor readings at sampling instant \( k \), \( C \) is the measurement matrix, and \( v_k \) is measurement noise. Hardware setup and sensor bring-up is precisely the process that turns messy voltages and digital codes into \( y_k \) that actually behave like this model.

At a high level, a disciplined bring-up procedure for a new robot platform includes:

  • Verifying power rails and reference voltages.
  • Checking physical connections and bus topology (I2C, SPI, UART, CAN).
  • Confirming basic communication (device IDs, self-test registers).
  • Establishing sampling rates, conversions, and units.
  • Performing static and dynamic calibration, estimating offsets and gains.
  • Assessing noise and bandwidth, and validating against control requirements.
flowchart TD
  S["Start: robot powered off"] --> PWR["Verify power rails and ground"]
  PWR --> BUS["Connect and inspect sensor buses"]
  BUS --> COMMS["Ping devices and read basic registers"]
  COMMS --> RAW["Stream raw data to host"]
  RAW --> CAL["Estimate offsets, gains, and units"]
  CAL --> CHECK["Check noise, range, and dynamics"]
  CHECK --> READY["Sensor stack ready for control use"]
        

2. Sensor Signal Chain and Measurement Model

Consider a single scalar physical quantity \( w(t) \) (e.g., acceleration along one axis). A typical sensor channel in a robot can be abstracted as:

  1. Transduction: physical quantity to electrical signal.
  2. Analog conditioning: filtering and amplification.
  3. Sampling and quantization in an ADC.
  4. Digital processing and scaling into engineering units.

A convenient continuous-time model for the analog part is

\[ v_s(t) = k_s\, w(t) + b_s + n_a(t), \]

where \( v_s(t) \) is the sensor output voltage, \( k_s \) is a sensitivity (V per unit of \( w \)), \( b_s \) is an offset, and \( n_a(t) \) is analog noise (often modeled as zero-mean with some power spectral density).

The analog conditioning stage is frequently well approximated by a first-order low-pass filter with time constant \( \tau_f \):

\[ H(s) = \frac{V_{\text{out}}(s)}{V_s(s)} = \frac{1}{1 + s\, \tau_f}. \]

Sampling the filtered signal at period \( T_s \) yields a discrete-time sequence

\[ v_k = v_s(k T_s) \quad \text{for } k = 0,1,2,\dots \]

The ADC maps \( v_k \) to a digital code \( c_k \in \{0,1,\dots,2^N - 1\} \), where \( N \) is the number of bits. A simple measurement model that incorporates calibration and quantization is then

\[ y_k = a\, c_k + b + n_{d,k}, \]

where \( a \) and \( b \) convert raw codes \( c_k \) to engineering units (e.g., m/s\(^2\)) and \( n_{d,k} \) models digital noise (quantization plus any digital filtering artifacts).

From a linear control viewpoint, once bring-up is complete the controller only “sees” the abstract measurement equation

\[ y_k = C x_k + v_k, \]

but the correct \( C \) and the statistics of \( v_k \) depend critically on the detailed signal chain and calibration steps above.

3. Sampling, Nyquist Condition, and ADC Quantization

Let the analog signal entering the ADC have (essentially) bandlimited spectrum with maximum frequency \( B \) (Hz). The classical sampling theorem states that perfect reconstruction is possible (in principle) if the sampling frequency \( f_s = 1/T_s \) satisfies

\[ f_s > 2 B. \]

In practice, robot sensors include anti-aliasing filters designed to attenuate energy above \( f_s/2 \). For bring-up, you must ensure that configured sample rates are consistent with expected motion bandwidth; for example, joint encoders on a fast arm require larger \( f_s \) than a slowly drifting temperature sensor.

Now consider quantization. Suppose the ADC input range is \([V_{\min}, V_{\max}]\) and its resolution is \( N \) bits. The quantization step size \( \Delta \) (in volts) is

\[ \Delta = \frac{V_{\max} - V_{\min}}{2^N}. \]

Assuming an ideal mid-tread quantizer and an input signal whose distribution is approximately uniform across quantization bins, the quantization error \( e_q(k) \) can be modeled as a random variable uniformly distributed in the interval \([-\Delta/2, \Delta/2]\). Its variance is then

\[ \sigma_q^2 = \operatorname{Var}(e_q) = \frac{\Delta^2}{12}. \]

If the sensor sensitivity is \( k_s \) V per unit of the physical quantity (e.g., V per m/s\(^2\)), then the effective resolution in physical units, \( \Delta_w \), is

\[ \Delta_w = \frac{\Delta}{k_s}. \]

During bring-up you should verify that \( \Delta_w \) is significantly smaller than the smallest changes your controller must react to; otherwise, quantization will dominate and linear control assumptions about measurement accuracy will be violated.

4. Static Calibration via Least Squares

A typical first calibration task is to estimate a linear relationship between the raw ADC codes \( c_i \) and known reference values \( w_i^{\text{ref}} \) of the physical quantity, such as accelerations produced by known orientations in gravity or distances measured against a ruler.

We assume a model

\[ w_i^{\text{ref}} = a\, c_i + b + \varepsilon_i,\quad i = 1,\dots,m, \]

where \( a \) is the scale factor, \( b \) is an offset, and \( \varepsilon_i \) are zero-mean errors (noise, imperfect ground truth, etc.). Define \( \boldsymbol{\theta} = \begin{bmatrix} a \\ b \end{bmatrix} \), \( \mathbf{w} \in \mathbb{R}^m \) with entries \( w_i^{\text{ref}} \), and the regression matrix

\[ \mathbf{\Phi} = \begin{bmatrix} c_1 & 1 \\ c_2 & 1 \\ \vdots & \vdots \\ c_m & 1 \end{bmatrix}. \]

Then the model can be written compactly as

\[ \mathbf{w} = \mathbf{\Phi}\, \boldsymbol{\theta} + \boldsymbol{\varepsilon}. \]

The least-squares estimate \( \hat{\boldsymbol{\theta}} \) minimizes the cost

\[ J(a,b) = \frac{1}{2} \sum_{i=1}^m \left( w_i^{\text{ref}} - a\, c_i - b \right)^2. \]

Setting the gradient to zero yields the normal equations, and their solution is

\[ \hat{\boldsymbol{\theta}} = \left( \mathbf{\Phi}^{\top} \mathbf{\Phi} \right)^{-1} \mathbf{\Phi}^{\top} \mathbf{w}. \]

In scalar form, writing \( \bar{c} = \tfrac{1}{m} \sum_i c_i \) and \( \bar{w} = \tfrac{1}{m} \sum_i w_i^{\text{ref}} \), we obtain

\[ \hat{a} = \frac{\sum_{i=1}^m (c_i - \bar{c})(w_i^{\text{ref}} - \bar{w})} {\sum_{i=1}^m (c_i - \bar{c})^2}, \qquad \hat{b} = \bar{w} - \hat{a}\, \bar{c}. \]

During lab bring-up you will often repeat this procedure for each axis of a vector sensor (e.g., 3D accelerometer or magnetometer) and sometimes for cross-axis coupling using multivariate linear regression. The principle is the same: build \( \mathbf{\Phi} \), solve for \( \hat{\boldsymbol{\theta}} \), and then convert raw codes \( c_k \) to calibrated values using \( \hat{a} \) and \( \hat{b} \).

5. Noise, Filtering, and Bring-Up Validation Checks

Once basic communication and calibration are working, you must decide whether a given sensor configuration is “good enough” for the control tasks it will support. This typically involves estimating noise levels, effective bandwidth, and verifying that measurements obey basic expectations (e.g., gravity magnitude).

Suppose we have \( N \) samples \( y_1,\dots,y_N \) under a static condition (e.g., accelerometer lying still on a table). The sample mean and variance are

\[ \bar{y} = \frac{1}{N} \sum_{k=1}^N y_k, \qquad s^2 = \frac{1}{N-1} \sum_{k=1}^N (y_k - \bar{y})^2. \]

The mean \( \bar{y} \) should be close to the expected value \( y^{\star} \) (e.g., one Earth gravity component), while \( s^2 \) characterizes sensor noise. A simple acceptance criterion is

\[ \left| \bar{y} - y^{\star} \right| \leq \delta_{\text{offset}}, \qquad s \leq \sigma_{\text{max}}, \]

where \( \delta_{\text{offset}} \) and \( \sigma_{\text{max}} \) are design tolerances derived from control requirements (for example, from allowable steady-state error or noise amplification in a feedback loop).

For dynamic behavior, you may command a known sinusoidal motion \( w(t) = A \sin(\omega t) \) and fit a simple discrete-time first-order model to the measured output \( y_k \). A widely used digital low-pass filter for conditioning sensor data is

\[ y_k = \alpha y_{k-1} + (1 - \alpha) x_k, \]

where \( x_k \) is the raw measurement and \( \alpha \in (0,1) \) controls the effective time constant. For small \( 1 - \alpha \), the equivalent time constant is roughly

\[ \tau_{\text{eq}} \approx \frac{T_s}{1 - \alpha}. \]

Choosing \( \alpha \) thus trades noise reduction against latency. This trade-off should be checked against the closed-loop bandwidth of your control design, which you already know from linear control theory.

flowchart TD
  W["Physical quantity w(t)"] --> SENS["Sensor transducer: k_s, b_s, analog noise"]
  SENS --> AF["Analog filter and amplifier"]
  AF --> ADC["ADC: sampling and quantization"]
  ADC --> DSP["Digital filter and calibration"]
  DSP --> CTRL["Controller using y_k in feedback"]
        

6. Python Example — Serial Sensor Bring-Up

Many educational robots stream sensor data over a UART–USB bridge. Below is a minimal Python script using pyserial and numpy to:

  • Open a serial port.
  • Collect a batch of samples from an IMU acceleration channel.
  • Estimate mean and variance.
  • Apply a precomputed linear calibration \( \hat{a}, \hat{b} \).

import serial
import numpy as np

# Configuration (adapt to your board)
PORT = "/dev/ttyUSB0"
BAUD = 115200
N_SAMPLES = 1000

# Calibration parameters for this axis (from offline least squares)
a_hat = 0.0123  # units per ADC code
b_hat = -0.15   # units

def open_serial(port, baud):
    return serial.Serial(port=port, baudrate=baud, timeout=1.0)

def parse_line(line):
    """
    Assume lines of the form: "ax:1234, ay:1230, az:1010"
    Return raw integer for ax channel.
    """
    try:
        text = line.decode("ascii").strip()
        parts = text.split(",")
        first = parts[0].split(":")
        return int(first[1])
    except Exception:
        return None

def acquire_raw_samples(ser, n_samples):
    raw = []
    while len(raw) != n_samples:
        line = ser.readline()
        if not line:
            continue
        code = parse_line(line)
        if code is None:
            continue
        raw.append(code)
    return np.array(raw, dtype=float)

if __name__ == "__main__":
    ser = open_serial(PORT, BAUD)
    try:
        print("Collecting {} samples...".format(N_SAMPLES))
        raw = acquire_raw_samples(ser, N_SAMPLES)
    finally:
        ser.close()

    # Convert to engineering units
    w = a_hat * raw + b_hat

    mean_w = np.mean(w)
    std_w = np.std(w, ddof=1)

    print("Mean (units):", mean_w)
    print("Std dev (units):", std_w)

    # Check against expected static reference, e.g., 1 g on this axis
    w_star = 9.81  # example
    offset = mean_w - w_star
    print("Offset from expected (units):", offset)
      

In a more advanced integration lab, you would extend this script to estimate \( \hat{a}, \hat{b} \) directly from reference measurements, and to log data streams to files for later analysis and system identification.

7. C++ Example — I2C Sensor Register Read

On microcontroller-based robots, C or C++ firmware often performs low-level I2C register reads and writes. The following pseudo-code (using a generic hardware abstraction layer HAL_I2C_*) reads a 16-bit sensor register, converts it to engineering units, and updates a simple exponential moving average filter \( y_k = \alpha y_{k-1} + (1 - \alpha) x_k \).


#include <cstdint>

static constexpr uint8_t  SENSOR_ADDR = 0x1E;     // example I2C address
static constexpr uint8_t  REG_ACCEL_X = 0x28;     // example register
static constexpr float    SCALE       = 0.000598f; // units per LSB
static constexpr float    OFFSET      = 0.05f;     // units
static constexpr float    ALPHA       = 0.9f;      // filter coefficient

float y_accel_x = 0.0f; // filtered value

bool read_register_16(uint8_t dev_addr, uint8_t reg_addr, int16_t &value)
{
    uint8_t buf[2] = {0, 0};
    if (!HAL_I2C_Mem_Read(dev_addr, reg_addr, buf, 2)) {
        return false;
    }
    // Assume sensor sends low byte then high byte
    value = static_cast<int16_t>(
        static_cast<uint16_t>(buf[0]) |
        (static_cast<uint16_t>(buf[1]) << 8)
    );
    return true;
}

void update_accel_x()
{
    int16_t raw = 0;
    if (!read_register_16(SENSOR_ADDR, REG_ACCEL_X, raw)) {
        // handle error (timeout, bus fault, etc.)
        return;
    }

    // Convert to engineering units
    float x_k = SCALE * static_cast<float>(raw) + OFFSET;

    // Exponential smoothing filter
    y_accel_x = ALPHA * y_accel_x + (1.0f - ALPHA) * x_k;
}
      

In a real firmware project, you would implement robust error handling (timeouts, reinitialization on bus errors), and expose y_accel_x to the higher-level control loop running at a fixed sampling period.

8. Java Example — USB Serial Bring-Up on a Host PC

On laptops or desktop machines used as robot “brains”, Java can be convenient for GUI tools. Libraries like jSerialComm simplify USB serial access. The snippet below opens a port, reads lines, and parses a single scalar sensor value.


import com.fazecast.jSerialComm.SerialPort;

public class SensorConsole {

    public static void main(String[] args) {
        SerialPort port = SerialPort.getCommPort("/dev/ttyUSB0");
        port.setBaudRate(115200);
        port.setNumDataBits(8);
        port.setNumStopBits(SerialPort.ONE_STOP_BIT);
        port.setParity(SerialPort.NO_PARITY);

        if (!port.openPort()) {
            System.err.println("Failed to open serial port.");
            return;
        }

        try {
            byte[] buffer = new byte[256];
            StringBuilder lineBuilder = new StringBuilder();

            while (true) {
                int n = port.readBytes(buffer, buffer.length);
                if (n <= 0) {
                    continue;
                }
                for (int i = 0; i != n; ++i) {
                    char c = (char) buffer[i];
                    if (c == '\n') {
                        String line = lineBuilder.toString().trim();
                        lineBuilder.setLength(0);
                        handleLine(line);
                    } else {
                        lineBuilder.append(c);
                    }
                }
            }
        } finally {
            port.closePort();
        }
    }

    private static void handleLine(String line) {
        // Suppose firmware sends lines like "temp:23.5"
        if (!line.startsWith("temp:")) {
            return;
        }
        try {
            double value = Double.parseDouble(line.substring(5));
            System.out.println("Temperature [degC]: " + value);
        } catch (NumberFormatException ex) {
            // ignore malformed line
        }
    }
}
      

Such host-side tools are invaluable during bring-up to visualize multiple channels, compute statistics, and compare measured behavior with theoretical expectations from your control design.

9. MATLAB / Simulink Example — Streaming and Filtering

In MATLAB, the Instrument Control Toolbox provides convenient high-level access to serial devices. The code below opens a port, reads numeric samples, filters them, and computes summary statistics.


% Parameters
portName   = "/dev/ttyUSB0";
baudRate   = 115200;
N          = 2000;
alpha      = 0.9;  % exponential filter coefficient

% Open serial port
s = serialport(portName, baudRate);
configureTerminator(s, "LF");

raw = zeros(N,1);
y   = zeros(N,1);

for k = 1:N
    line = readline(s);
    value = str2double(line);   % assume line is just a number
    if isnan(value)
        value = raw(max(k-1,1)); % simple hold on NaN
    end
    raw(k) = value;
    if k == 1
        y(k) = value;
    else
        y(k) = alpha * y(k-1) + (1-alpha) * value;
    end
end

clear s;  % close port

mean_raw = mean(raw);
std_raw  = std(raw, 1);
mean_y   = mean(y);
std_y    = std(y, 1);

fprintf("Raw mean = %.4f, raw std = %.4f\n", mean_raw, std_raw);
fprintf("Filtered mean = %.4f, filtered std = %.4f\n", mean_y, std_y);
      

In Simulink, a common workflow is to receive sensor data via blocks such as UDP Receive or custom S-function drivers, pass them through a discrete transfer function implementing a desired filter, and log the outputs. The underlying discrete transfer function is derived using methods from linear control and digital signal processing.

10. Problems and Solutions

Problem 1 (ADC Resolution and Quantization Noise): A distance sensor outputs voltage in the range \([0, 5]\) V for distances in \([0, 2]\) m. It is sampled by a 12-bit ADC with input range \([0, 5]\) V. Compute: (a) the quantization step size \( \Delta \) in volts; (b) the corresponding step size in meters \( \Delta_d \); (c) the quantization noise variance in meters squared (assuming uniform error).

Solution:

The ADC step size is

\[ \Delta = \frac{5 - 0}{2^{12}} = \frac{5}{4096} \approx 0.00122 \text{ V}. \]

The sensor sensitivity is \( k_s = \frac{5 \text{ V}}{2 \text{ m}} = 2.5 \text{ V/m} \), so the distance step size is

\[ \Delta_d = \frac{\Delta}{k_s} = \frac{5/4096}{2.5} = \frac{1}{4096} \text{ m} \approx 2.44 \times 10^{-4} \text{ m}. \]

The quantization error is modeled as uniform on \([-\Delta_d/2, \Delta_d/2]\), so its variance is

\[ \sigma_q^2 = \frac{\Delta_d^2}{12} = \frac{1}{12} \left( \frac{1}{4096} \right)^2 \text{ m}^2. \]

Problem 2 (Checking Sampling Rate vs. Motion Bandwidth): A robot joint is modeled as a second-order system whose dominant closed-loop bandwidth (in magnitude) is approximately \( B = 5 \) Hz. You plan to sample an attached encoder at \( f_s = 20 \) Hz. Comment on whether this is adequate from a sampling perspective, and suggest an improved sampling frequency if not.

Solution:

The Nyquist condition requires \( f_s > 2 B \), so the bare minimum is \( 10 \) Hz. However, to allow for nonideal filters and to capture higher harmonics in the closed-loop response, it is common to choose \( f_s \) at least 5–10 times the bandwidth: \( f_s \geq 5 B \). Here, \( f_s = 20 \) Hz yields a ratio of \( 4 \), which is marginal. A more conservative choice would be \( f_s = 50 \) Hz or higher.

Problem 3 (Least Squares Calibration Interpretation): Show that the least-squares estimators \( \hat{a} \) and \( \hat{b} \) in the linear calibration model \( w_i^{\text{ref}} = a c_i + b + \varepsilon_i \) are unbiased if \( \varepsilon_i \) are independent, zero-mean, and have equal variance, and the calibration points \( c_i \) are deterministic.

Solution:

We have \( \mathbf{w} = \mathbf{\Phi}\boldsymbol{\theta} + \boldsymbol{\varepsilon} \) with fixed \( \mathbf{\Phi} \). The least-squares estimator is \( \hat{\boldsymbol{\theta}} = (\mathbf{\Phi}^{\top}\mathbf{\Phi})^{-1} \mathbf{\Phi}^{\top} \mathbf{w} \). Taking expectation,

\[ \mathbb{E}[\hat{\boldsymbol{\theta}}] = (\mathbf{\Phi}^{\top}\mathbf{\Phi})^{-1} \mathbf{\Phi}^{\top} \mathbb{E}[\mathbf{w}] = (\mathbf{\Phi}^{\top}\mathbf{\Phi})^{-1} \mathbf{\Phi}^{\top} \left( \mathbf{\Phi}\boldsymbol{\theta} + \mathbb{E}[\boldsymbol{\varepsilon}] \right) = \boldsymbol{\theta}, \]

because \( \mathbb{E}[\boldsymbol{\varepsilon}] = \mathbf{0} \) by assumption. Thus both \( \hat{a} \) and \( \hat{b} \) are unbiased.

Problem 4 (Discrete-Time Low-Pass Filter Pole): Consider the digital low-pass filter \( y_k = \alpha y_{k-1} + (1 - \alpha) x_k \) with \( \alpha \in (0,1) \). Determine the location of its pole in the complex plane and relate \( \alpha \) to an equivalent continuous-time time constant \( \tau_{\text{eq}} \) for sample period \( T_s \).

Solution:

Taking the Z-transform and assuming zero initial conditions, \( Y(z) = \alpha z^{-1} Y(z) + (1 - \alpha) X(z) \), we find the transfer function

\[ H(z) = \frac{Y(z)}{X(z)} = \frac{1 - \alpha}{1 - \alpha z^{-1}}. \]

The pole is at \( z = \alpha \). For a first-order continuous-time system \( \dot{y} = -\frac{1}{\tau} y + \frac{1}{\tau} x \), the discrete-time pole under zero-order hold with sampling period \( T_s \) is \( z = e^{-T_s/\tau} \). Equating these gives

\[ \alpha = e^{-T_s / \tau_{\text{eq}}} \quad \Rightarrow \quad \tau_{\text{eq}} = -\frac{T_s}{\ln \alpha}. \]

For \( \alpha \) close to 1, \( \ln \alpha \approx -(1 - \alpha) \), yielding the approximation \( \tau_{\text{eq}} \approx T_s / (1 - \alpha) \) used earlier.

Problem 5 (Measurement Offset in State-Space Models): A robot’s measurement model is \( y_k = C x_k + d + v_k \), where \( d \) is an unknown constant sensor bias. Show how to augment the state so that the model fits the standard form \( \tilde{y}_k = \tilde{C} \tilde{x}_k + \tilde{v}_k \) with zero bias, and write the augmented measurement matrix.

Solution:

Define the augmented state \( \tilde{x}_k = \begin{bmatrix} x_k \\ d \end{bmatrix} \), with dynamics \( d_{k+1} = d_k \) (bias modeled as constant). The measurement becomes

\[ y_k = C x_k + d + v_k = \begin{bmatrix} C & I \end{bmatrix} \begin{bmatrix} x_k \\ d \end{bmatrix} + v_k = \tilde{C} \tilde{x}_k + \tilde{v}_k, \]

where \( \tilde{C} = \begin{bmatrix} C & I \end{bmatrix} \) and \( I \) is the identity matrix with dimension matching \( d \). Estimating \( \tilde{x}_k \) with a Kalman filter or other observer now includes estimating the sensor bias as an additional state component, which is a common design in high-quality robot sensing systems.

11. Summary

This lesson treated hardware setup and sensor bring-up as the rigorous process of converting physical quantities into mathematically well-understood measurements suitable for feedback control. We modeled sensors as linear channels with noise, bandwidth limits, and quantization; derived key relationships for sampling, resolution, and calibration; and linked these to practical Python, C++, Java, and MATLAB/Simulink code skeletons that implement streaming, filtering, and verification. Subsequent lessons in this integration lab will build on this foundation to stream and visualize data, connect simulation with hardware, and demonstrate simple autonomous behaviors.

12. References

  1. Nyquist, H. (1928). Certain topics in telegraph transmission theory. Transactions of the American Institute of Electrical Engineers, 47(2), 617–644.
  2. Shannon, C. E. (1948). A mathematical theory of communication. Bell System Technical Journal, 27(3–4), 379–423, 623–656.
  3. Bennett, W. R. (1948). Spectra of quantized signals. Bell System Technical Journal, 27(3), 446–472.
  4. Kalman, R. E. (1960). A new approach to linear filtering and prediction problems. Journal of Basic Engineering, 82(1), 35–45.
  5. Goodwin, G. C., & Payne, R. L. (1977). Dynamic system identification: Experiment design and data analysis. International Journal of Control, 26(1), 1–30.
  6. Ljung, L. (1978). Convergence analysis of parametric identification methods. IEEE Transactions on Automatic Control, 23(5), 770–783.
  7. Soderstrom, T., & Stoica, P. (1981). Instrumental variable methods for system identification. Circuits, Systems and Signal Processing, 1(1), 3–36.