Skip to content
Snippets Groups Projects
bestrobot.py 6.32 KiB
Newer Older
mehtank's avatar
mehtank committed
from scipy import log, sqrt, linspace, logspace, meshgrid, zeros, absolute
mehtank's avatar
mehtank committed
from scipy.optimize import minimize
mehtank's avatar
mehtank committed


def isstable(model):
Cathy's avatar
Cathy committed
    """

    :param model: (F,G,H)
    :return: 2GH + H^2 - 2F, > 0 if stable; < 0 if unstable
    """
    return 2 * model[1] * model[2] + model[2] * model[2] - 2 * model[0]
mehtank's avatar
mehtank committed

mehtank's avatar
mehtank committed
def tf((f,g,h), w):
    """
    Return the complex value of the linearized transfer function T(jw)
mehtank's avatar
mehtank committed
    """
    return (g*1j*w + f)/(-w*w + (g+h)*1j*w + f)

def tflogmag(model, w):
mehtank's avatar
mehtank committed
    """
    Return the log of the magnitude of the linearized transfer function
        log[ |T(jw)| ]
    """
mehtank's avatar
mehtank committed
    return log(absolute(tf(model, w)))
mehtank's avatar
mehtank committed

mehtank's avatar
mehtank committed
def htflogmag(model, w):
mehtank's avatar
mehtank committed
    """
    Return the log of the magnitude of the linearized headway transfer function
        log[ |1-T(jw)| ]
    """
mehtank's avatar
mehtank committed
    return log(absolute(1-tf(model,w)))
mehtank's avatar
mehtank committed

def btf(human, robot, gamma, w):
    """
    Return the complex value of the linearized transfer function T(jw)
    of a bilateral robot controller [ followed by a human ]:
        Y(robot) = gamma Tr Y(forward) + (1-gamma) Tr Y(behind)
        Y(behind) = Th Y(robot)
        Y(robot) = gamma Tr Y(forward) + (1-gamma) Tr Th Y(robot)
        Y(robot) = [ gamma Tr ] / [ 1 - (1-gamma) Tr Th ] Y(forward)
    """
    tr = tf(robot, w)
    th = tf(human, w)
    t = gamma * tr / (1 - (1-gamma)*tr*th)
    return t

def btflogmag(human, robot, gamma, w):
    """
    Return the log of the magnitude of the linearized transfer function
        log[ |T(jw)| ]
    of a bilateral robot controller [ followed by a human ]
    """
    return log(absolute(btf(human, robot, gamma, w)))

def bhtflogmag(human, robot, gamma, w):
    """
    Return the log of the magnitude of the linearized headway transfer function
        log[ |1-T(jw)| ]
    of a bilateral robot controller [ followed by a human ]
    """
    return log(absolute(1-btf(human, robot, gamma, w)))

mehtank's avatar
mehtank committed
def kstable(human, robot, w, eta=2):
    """
    Return the maximum number of human cars 
        that can be string-stabilized by a single robot car
        for an oscillatory perturbation of frequency w
    """
Cathy's avatar
Cathy committed
    return -tflogmag(robot, w) / tflogmag(human, w)

mehtank's avatar
mehtank committed

mehtank's avatar
mehtank committed
def ksafe(human, robot, w, eta):
mehtank's avatar
mehtank committed
    """
Cathy's avatar
Cathy committed
    Return the maximum number of human cars
mehtank's avatar
mehtank committed
        that can be "safely" followed by a single robot car
        for an oscillatory perturbation of frequency w
Cathy's avatar
Cathy committed
        by a safety parameter eta
    :param human:
    :param robot:
    :param w:
    :param eta: safety margin
    :return:
mehtank's avatar
mehtank committed
    """
Cathy's avatar
Cathy committed
    return (log(eta) - htflogmag(robot, w)) / tflogmag(human, w)

mehtank's avatar
mehtank committed

TOL = 1e-6

mehtank's avatar
mehtank committed
def maxkfn(human, robot, eta, kfn):
Cathy's avatar
Cathy committed
    """
    Find the maximum number of humans cars that can be handled by a
Cathy's avatar
Cathy committed
    single robot car for any frequency (all w)
    :param human:
    :param robot:
    :param eta:
    :param kfn: function { ksafe for safety | kstable for string stability }
Cathy's avatar
Cathy committed
    :return:
    """
mehtank's avatar
mehtank committed
    f,g,h = human
mehtank's avatar
mehtank committed
    maxw = sqrt(-isstable(human))
    return minimize(lambda w : kfn(human, robot, w, eta), 1, bounds=[(TOL,maxw-TOL)])#.fun[0]
mehtank's avatar
mehtank committed

def maxk(human, robot, eta):
Cathy's avatar
Cathy committed
    """
    Min of maxksafe and maxkstable
    :param human:
    :param robot:
    :param eta:
    :return:
    """
mehtank's avatar
mehtank committed
    return min(maxkfn(human, robot, eta, kstable), maxkfn(human, robot, eta, ksafe))
mehtank's avatar
mehtank committed

def best_robot_at_w(human, w, optfun, eta=2, fbound=(0, 0.2), gbound=(0, 0.2),
                    hbound=(0, 0.2)):
Cathy's avatar
Cathy committed
    cons = ({'type': 'ineq', 'fun': lambda x: isstable(x)},)
    return minimize(lambda robot: -optfun(human, robot, w, eta),
                    [0.001, 0.001, 0.001], constraints=cons,
Cathy's avatar
Cathy committed
                    bounds=[fbound, gbound, hbound])
mehtank's avatar
mehtank committed

def bestrobot(human, eta=2, fbound=(0, 0.2), gbound=(0, 0.2), hbound=(0, 0.2)):
    cons = ({'type': 'ineq', 'fun': lambda x: isstable(x)},)
    mink = minimize(lambda robot: -maxk(human, robot, eta).fun[0],
                    [0.001, 0.001, 0.001], constraints=cons,
                    bounds=[fbound, gbound, hbound])
    # robot = mink.x
    # print maxksafe(human, robot, eta=eta)
    # print maxkstable(human, robot, eta=eta)
    return mink


def plot_kmax_vs_w(human, eta=2):
    """
    I think this plot is not meaningful (after the fact)
    :param human:
    :param eta:
    :return:
    """
    # ws = linspace(0,1,10)

    maxw = sqrt(-isstable(human))

    ws = linspace(TOL, maxw - TOL, 101)
    kstablerobots = [-best_robot_at_w(human, w, kstable).fun for w in ws]
    ksaferobots = [-best_robot_at_w(human, w, ksafe, eta).fun for w in ws]
    # kst = kstable(human, robot, ws)
    # ksf = ksafe(human, robot, ws, eta)
    # print "kstable = ", kstable(human, robot, ws)
    print ksaferobots, kstablerobots
    plt.plot(ws, kstablerobots, 'k.')
    plt.plot(ws, ksaferobots, 'r.')
    plt.axis([0, maxw, 0, 200])
    plt.show()


def plot_kmaxs_vs_FGH(human, robotopt, eta=2, res=100, fbound=(0, 0.2),
                      gbound=(0, 0.2), hbound=(0, 0.3)):
    """
    How do the two optimality conditions (stability and safety) vary as the
    robot parameters are deviated from the optimal robot controller?
    :param human:
    :param robotopt: (F,G,H) for the optimal robot controller given human
    :param eta:
    :param res: resolution for plotting, how fine to plot along the x-axis
    :param fbound:
    :param gbound:
    :param hbound:
    :return:
    """
    robotFs = linspace(fbound[0], fbound[1], res)
    robotGs = linspace(gbound[0], gbound[1], res)
    robotHs = linspace(hbound[0], hbound[1], res)

    for i, robotXs in enumerate([robotFs, robotGs, robotHs]):
        robots = np.tile(robotopt, (res, 1))
        robots[:, i] = robotXs
        kstablerobots = np.array(
            [maxkfn(human, robot, eta, kstable).fun[0] for robot in robots])
        ksaferobots = [maxkfn(human, robot, eta, ksafe).fun[0] for robot in robots]
        kstablerobots[kstablerobots < 0] = 0
        plt.plot(robotXs, kstablerobots, 'k.')
        plt.plot(robotXs, ksaferobots, 'r.')
        plt.vlines(robotopt[i], 0, max(max(kstablerobots), max(ksaferobots)))
        plt.axis([0, max(robotXs), 0, 100])
        plt.show()

mehtank's avatar
mehtank committed

if __name__ == "__main__":
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import pyplot as plt
    from cfm import IDM, CFM

Cathy's avatar
Cathy committed
    hmodel = IDM(a=0.3, b=3, t=1.5, s0=2, v0=30)
mehtank's avatar
mehtank committed
    hmodel.go(0.5)
    human = (hmodel.f, hmodel.g, hmodel.h)

mehtank's avatar
mehtank committed
    maxw = sqrt(-isstable(human))
mehtank's avatar
mehtank committed

Cathy's avatar
Cathy committed
    eta = 2
mehtank's avatar
mehtank committed

    robotopt = bestrobot(human, eta)
    plot_kmaxs_vs_FGH(human, robotopt.x)
mehtank's avatar
mehtank committed