Skip to content
Snippets Groups Projects
cfm.py 3.98 KiB
Newer Older
from scipy import signal
from scipy import sqrt, linspace, exp, pi, polymul, sinh, cosh, arange
from scipy import real, array, eye, kron, diag, sparse
from numpy.linalg import eig

class CFM:
    # General car following model class

    def go(self, re):
        # Compute linearization about equilibrium point re = ve / v0 :
        #   v_e, h_e : equilibrium velocity and headway
        #   F, G, H  : linearized gains
        # then generate state-space matrices and transfer function

        self.re = re

        self._linearize()
        self._mn()
        self._sys()

    def _linearize(self):
        # generate F, G, H linearized gains
        raise NotImplementedError

    def _mn(self):
        # use F, G, H linearization to create M, N state-space matrices
        self.m = array([[0,1],[-self.f, -self.g-self.h]])
        self.n = array([[0,0],[self.f, self.g]])

    def _sys(self):
        # use F, G, H linearization to create transfer function
        self.sys = signal.TransferFunction([self.g, self.f], [1, self.g+self.h, self.f])

    def sysn(self, cnt):
        # use F, G, H linearization to create head-to-tail transfer function 
        #   for cnt cars in a row
        if cnt == 0:
            return signal.TransferFunction([1],[1])

        f,g,h = self.f, self.g, self.h
        num = [g, f]
        den = [1, h+g, f]
        for i in range(cnt-1):
            num = polymul(num, [g, f])
            den = polymul(den, [1, h+g, f])
        sys = signal.TransferFunction(num, den)
        return sys

    def loop(self, cnt):
        m = self.m
        n = self.n
        a = kron(eye(cnt), m) + \
            kron(diag([1]*(cnt-1), -1) + \
                 diag([1], cnt-1), n)
        '''
        a = sparse.kron(sparse.identity(cnt), m) + \
            sparse.kron(sparse.diags(([1]*(cnt-1), [1]), (-1, cnt-1)), n)
        '''
        return a

    def loopWithRobot(self, cnt):
        m = self.m
        n = self.n
        cnt -= 2
        a = kron(eye(cnt+2), m) + \
                kron(diag([1]*(cnt-1) + [1/2., 1], -1) + \
                     diag([0]* cnt    + [1/2.]   ,  1) + \
                     diag([1], cnt+1), n)
        return a

    def loopStablity(self, cnt):
        a = self.loop(cnt)
        w, _ = eig(a)
        # w,v = sparse.linalg.eigs(a)
        return max(real(w))

    def tfStability(self):
        _, mag, _ = self.sys.bode()
        return max(mag)

class Wang(CFM):
    def __init__(self, kd, kv):
        self.kd = kd
        self.kv = kv

    def _linearize(self):
        self.ve = 0
        self.he = 0

        self.f = self.kd
        self.g = self.kv
        self.h = 0

class IDM(CFM):
    D = 4

    def __init__(self, t, a, b, s0, v0):
        self.t = float(t)
        self.a = float(a)
        self.b = float(b)
        self.s0 = float(s0)
        self.v0 = float(v0)

    def _linearize(self):
        t = self.t
        a = self.a
        b = self.b
        d = self.D
        s0 = self.s0
        v0 = self.v0

        re = self.re

        r1 = (1-re**d)
        r32 = r1**(1.5)
        ge = s0 + t * v0 * re

        self.ve = re * v0
        self.he = ge * r1**(-0.5)

        self.f = 1*a*r32/ge
        self.g = v0 * sqrt(a/b) * re * r1/ge
        self.h = a * d * re**(d-1)/v0 + 2*a*t*r1/ge


def plotSweep(fn, rngs, axis=None, separate=True, logx=False):
    if logx:
        plot = plt.semilogx
    else:
        plot = plt.plot

    defaults = [x["default"] for x in rngs]

    if not separate:
        plot(*fn(*defaults, fmt='k'))

    for i, r in enumerate(rngs):
        if "values" in r and r["values"] is not None:
            if separate:
                plot(*fn(*defaults, fmt='k'))

            print r["name"], defaults[i], r["values"]
            args = list(defaults)
            for dot, arg in zip(r["dots"], r["values"]):
                args[i] = arg
                plot(*fn(*args, fmt=r["color"]+dot))

            if axis is not None:
                plt.axis(axis)
            if separate:
                plt.show()
    if not separate:
        plt.show()