Beispiel #1
0
def test_expm(rng):
    scipy_linalg = pytest.importorskip("scipy.linalg")
    for a in [np.eye(3), rng.randn(10, 10), -10 + rng.randn(10, 10)]:
        assert np.allclose(scipy_linalg.expm(a), expm(a))
Beispiel #2
0
def cont2discrete(sys, dt, method="zoh", alpha=None):  # noqa: C901
    """
    Transform a continuous to a discrete state-space system.

    Parameters
    ----------
    sys : a tuple describing the system.
        The following gives the number of elements in the tuple and
        the interpretation:

           * 2: (num, den)
           * 3: (zeros, poles, gain)
           * 4: (A, B, C, D)

    dt : float
        The discretization time step.
    method : {"gbt", "bilinear", "euler", "backward_diff", "zoh"}
        Which method to use:

           * gbt: generalized bilinear transformation
           * bilinear: Tustin's approximation ("gbt" with alpha=0.5)
           * euler: Euler (or forward differencing) method ("gbt" with alpha=0)
           * backward_diff: Backwards differencing ("gbt" with alpha=1.0)
           * zoh: zero-order hold (default)

    alpha : float within [0, 1]
        The generalized bilinear transformation weighting parameter, which
        should only be specified with method="gbt", and is ignored otherwise

    Returns
    -------
    sysd : tuple containing the discrete system
        Based on the input type, the output will be of the form

        * (num, den, dt)   for transfer function input
        * (zeros, poles, gain, dt)   for zeros-poles-gain input
        * (A, B, C, D, dt) for state-space system input

    Notes
    -----
    By default, the routine uses a Zero-Order Hold (zoh) method to perform
    the transformation.  Alternatively, a generalized bilinear transformation
    may be used, which includes the common Tustin's bilinear approximation,
    an Euler's method technique, or a backwards differencing technique.

    The Zero-Order Hold (zoh) method is based on [1]_, the generalized bilinear
    approximation is based on [2]_ and [3]_.

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Discretization
           #Discretization_of_linear_state_space_models

    .. [2] http://techteach.no/publications/discretetime_signals_systems/discrete.pdf

    .. [3] G. Zhang, X. Chen, and T. Chen, Digital redesign via the generalized
           bilinear transformation, Int. J. Control, vol. 82, no. 4, pp. 741-754, 2009.
    """
    if len(sys) == 2:
        sysd = cont2discrete(tf2ss(sys[0], sys[1]), dt, method=method, alpha=alpha)
        return ss2tf(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,)
    elif len(sys) == 3:
        sysd = cont2discrete(
            zpk2ss(sys[0], sys[1], sys[2]), dt, method=method, alpha=alpha
        )
        return ss2zpk(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,)
    elif len(sys) == 4:
        a, b, c, d = sys
    else:
        raise ValueError(
            "First argument must either be a tuple of 2 (tf), "
            "3 (zpk), or 4 (ss) arrays."
        )

    if method == "gbt":
        if alpha is None:
            raise ValueError(
                "Alpha parameter must be specified for the "
                "generalized bilinear transform (gbt) method"
            )
        elif alpha < 0 or alpha > 1:
            raise ValueError(
                "Alpha parameter must be within the interval "
                "[0,1] for the gbt method"
            )

    if method == "gbt":
        # This parameter is used repeatedly - compute once here
        ima = np.eye(a.shape[0]) - alpha * dt * a
        ad = np.linalg.solve(ima, np.eye(a.shape[0]) + (1.0 - alpha) * dt * a)
        bd = np.linalg.solve(ima, dt * b)

        # Similarly solve for the output equation matrices
        cd = np.linalg.solve(ima.transpose(), c.transpose())
        cd = cd.transpose()
        dd = d + alpha * np.dot(c, bd)

    elif method in ("bilinear", "tustin"):
        return cont2discrete(sys, dt, method="gbt", alpha=0.5)

    elif method in ("euler", "forward_diff"):
        return cont2discrete(sys, dt, method="gbt", alpha=0.0)

    elif method == "backward_diff":
        return cont2discrete(sys, dt, method="gbt", alpha=1.0)

    elif method == "zoh":
        # Build an exponential matrix
        em_upper = np.hstack((a, b))

        # Need to stack zeros under the a and b matrices
        em_lower = np.hstack(
            (np.zeros((b.shape[1], a.shape[0])), np.zeros((b.shape[1], b.shape[1])))
        )

        em = np.vstack((em_upper, em_lower))
        ms = expm(dt * em)

        # Dispose of the lower rows
        ms = ms[: a.shape[0], :]

        ad = ms[:, 0 : a.shape[1]]
        bd = ms[:, a.shape[1] :]

        cd = c
        dd = d

    else:
        raise ValueError("Unknown transformation method '%s'" % method)

    return ad, bd, cd, dd, dt