Example #1
0
def main():
    x, y = np.mgrid[-1.0:1.0:30j, 0.0:2.0:30j]

    # Need an (N, 2) array of (x, y) pairs.
    xy = np.column_stack([x.flat, y.flat])

    mu = np.array([0.0, 1.0])
    sigma = np.array([0.5, 0.5])
    covariance = np.diag(sigma**2)
    z = multivariate_normal.pdf(xy, mean=mu, cov=covariance).reshape(x.shape)

    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    ax.plot_surface(x, y, z)
    ax.set_xlabel("$x$")
    ax.set_ylabel("$y$")
    ax.set_zlabel("$p(x, y)$")

    if "--noninteractive" in sys.argv:
        latex.savefig("joint_pdf")
    else:
        plt.show()
Example #2
0
def main():
    xlim = [0, 3]
    x = np.arange(xlim[0], xlim[1], 0.001)
    plt.xlim(xlim)

    y1 = np.sin(2 * math.pi * x)
    plt.plot(x, y1, label="Signal at $1$ Hz")

    y2 = np.sin(2 * math.pi * x / 2 + math.pi / 4)
    plt.plot(x, y2, color="k", linestyle="--", label="Signal at $2$ Hz")

    # Draw intersection points
    idx = np.argwhere(np.diff(np.sign(y1 - y2)) != 0)
    plt.plot(x[idx], y1[idx], "ko", label="Samples at $3$ Hz")

    plt.xlabel("$t$")
    plt.ylabel("$f(t)$")
    plt.legend()

    if "--noninteractive" in sys.argv:
        latex.savefig("nyquist")
    else:
        plt.show()
def main():
    elevator = Elevator(0.1)

    plt.figure(1)
    plt.xlabel("Time (s)")
    plt.ylabel("Position (m)")
    simulate(elevator, 0.1, "zoh")
    simulate(elevator, 0.1, "euler")
    simulate(elevator, 0.1, "backward_diff")
    simulate(elevator, 0.1, "bilinear")
    plt.ylim([-2, 3])
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("sampling_simulation_010")

    plt.figure(2)
    plt.xlabel("Time (s)")
    plt.ylabel("Position (m)")
    simulate(elevator, 0.05, "zoh")
    simulate(elevator, 0.05, "euler")
    simulate(elevator, 0.05, "backward_diff")
    simulate(elevator, 0.05, "bilinear")
    plt.ylim([-2, 3])
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("sampling_simulation_005")

    plt.figure(3)
    plt.xlabel("Time (s)")
    plt.ylabel("Position (m)")
    simulate(elevator, 0.01, "zoh")
    simulate(elevator, 0.01, "euler")
    simulate(elevator, 0.01, "backward_diff")
    simulate(elevator, 0.01, "bilinear")
    plt.ylim([-0.25, 2])
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("sampling_simulation_004")
    else:
        plt.show()
def main():
    dt = 0.00505
    drivetrain = Drivetrain(dt)

    t, xprof, vprof, aprof = fct.generate_s_curve_profile(
        max_v=4.0, max_a=3.5, time_to_max_a=1.0, dt=dt, goal=10.0
    )

    # Generate references for LQR
    refs = []
    for i in range(len(t)):
        r = np.array([[vprof[i]], [vprof[i]]])
        refs.append(r)

    # Run LQR
    state_rec, ref_rec, u_rec, y_rec = drivetrain.generate_time_responses(t, refs)
    plt.figure(1)
    drivetrain.plot_time_responses(t, state_rec, ref_rec, u_rec)
    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_decoupled_vel_lqr_profile")

    # Initial robot pose
    pose = Pose2d(2, 0, np.pi / 2.0)
    desired_pose = Pose2d()

    # Ramsete tuning constants
    b = 2
    zeta = 0.7

    vl = float("inf")
    vr = float("inf")

    x_rec = []
    y_rec = []
    vref_rec = []
    omegaref_rec = []
    v_rec = []
    omega_rec = []
    ul_rec = []
    ur_rec = []

    # Log initial data for plots
    vref_rec.append(0)
    omegaref_rec.append(0)
    x_rec.append(pose.x)
    y_rec.append(pose.y)
    ul_rec.append(drivetrain.u[0, 0])
    ur_rec.append(drivetrain.u[1, 0])
    v_rec.append(0)
    omega_rec.append(0)

    # Run Ramsete
    i = 0
    while i < len(t) - 1:
        desired_pose.x = 0
        desired_pose.y = xprof[i]
        desired_pose.theta = np.pi / 2.0

        # pose_desired, v_desired, omega_desired, pose, b, zeta
        vref, omegaref = ramsete(desired_pose, vprof[i], 0, pose, b, zeta)
        vl, vr = get_diff_vels(vref, omegaref, drivetrain.rb * 2.0)
        next_r = np.array([[vl], [vr]])
        drivetrain.update(next_r)
        vc = (drivetrain.x[0, 0] + drivetrain.x[1, 0]) / 2.0
        omega = (drivetrain.x[1, 0] - drivetrain.x[0, 0]) / (2.0 * drivetrain.rb)

        # Log data for plots
        vref_rec.append(vref)
        omegaref_rec.append(omegaref)
        x_rec.append(pose.x)
        y_rec.append(pose.y)
        ul_rec.append(drivetrain.u[0, 0])
        ur_rec.append(drivetrain.u[1, 0])
        v_rec.append(vc)
        omega_rec.append(omega)

        # Update nonlinear observer
        pose.x += vc * math.cos(pose.theta) * dt
        pose.y += vc * math.sin(pose.theta) * dt
        pose.theta += omega * dt

        if i < len(t) - 1:
            i += 1

    plt.figure(2)
    plt.plot([0] * len(t), xprof, label="Reference trajectory")
    plt.plot(x_rec, y_rec, label="Ramsete controller")
    plt.xlabel("x (m)")
    plt.ylabel("y (m)")
    plt.legend()

    # Equalize aspect ratio
    xlim = plt.xlim()
    width = abs(xlim[0]) + abs(xlim[1])
    ylim = plt.ylim()
    height = abs(ylim[0]) + abs(ylim[1])
    if width > height:
        plt.ylim([-width / 2, width / 2])
    else:
        plt.xlim([-height / 2, height / 2])

    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_decoupled_response")

    plt.figure(3)
    num_plots = 4
    plt.subplot(num_plots, 1, 1)
    plt.title("Time domain responses")
    plt.ylabel(
        "Velocity (m/s)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, vref_rec, label="Reference")
    plt.plot(t, v_rec, label="Estimated state")
    plt.legend()
    plt.subplot(num_plots, 1, 2)
    plt.ylabel(
        "Angular rate (rad/s)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, omegaref_rec, label="Reference")
    plt.plot(t, omega_rec, label="Estimated state")
    plt.legend()
    plt.subplot(num_plots, 1, 3)
    plt.ylabel(
        "Left voltage (V)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, ul_rec, label="Control effort")
    plt.legend()
    plt.subplot(num_plots, 1, 4)
    plt.ylabel(
        "Right voltage (V)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, ur_rec, label="Control effort")
    plt.legend()
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_decoupled_vel_lqr_response")
    else:
        plt.show()
def main():
    J = 7.7500e-05
    b = 8.9100e-05
    Kt = 0.0184
    Ke = 0.0211
    R = 0.0916
    L = 5.9000e-05

    # fmt: off
    A = np.array([[-b / J, Kt / J],
                  [-Ke / L, -R / L]])
    B = np.array([[0],
                  [1 / L]])
    C = np.array([[1, 0]])
    D = np.array([[0]])
    # fmt: on

    sysc = ct.StateSpace(A, B, C, D)

    dt = 0.0001
    tmax = 0.025

    sysd = sysc.sample(dt)

    # fmt: off
    Q = np.array([[1 / 20**2, 0],
                  [0, 1 / 40**2]])
    R = np.array([[1 / 12**2]])
    # fmt: on
    K = fct.lqr(sysd, Q, R)

    # Steady-state feedforward
    tmp1 = concatenate(
        (
            concatenate((sysd.A - np.eye(sysd.A.shape[0]), sysd.B), axis=1),
            concatenate((sysd.C, sysd.D), axis=1),
        ),
        axis=0,
    )
    tmp2 = concatenate(
        (np.zeros((sysd.A.shape[0], 1)), np.ones((sysd.C.shape[0], 1))), axis=0
    )
    NxNu = np.linalg.pinv(tmp1) * tmp2
    Nx = NxNu[0 : sysd.A.shape[0], 0]
    Nu = NxNu[sysd.C.shape[0] + 1 :, 0]
    Kff_ss = Nu * np.linalg.pinv(Nx)

    # Plant inversions
    Kff_ts1 = np.linalg.inv(sysd.B.T * Q * sysd.B + R) * (sysd.B.T * Q)
    Kff_ts2 = np.linalg.pinv(sysd.B)

    t = np.arange(0, tmax, dt)
    r = np.array([[2000 * 0.1047], [0]])
    r_rec = np.zeros((2, 1, len(t)))

    # No feedforward
    x = np.array([[0], [0]])
    x_rec = np.zeros((2, 1, len(t)))
    u_rec = np.zeros((1, 1, len(t)))

    # Steady-state feedforward
    x_ss = np.array([[0], [0]])
    x_ss_rec = np.zeros((2, 1, len(t)))
    u_ss_rec = np.zeros((1, 1, len(t)))

    # Plant inversion (Q and R cost)
    x_ts1 = np.array([[0], [0]])
    x_ts1_rec = np.zeros((2, 1, len(t)))
    u_ts1_rec = np.zeros((1, 1, len(t)))

    # Plant inversion (Q cost only)
    x_ts2 = np.array([[0], [0]])
    x_ts2_rec = np.zeros((2, 1, len(t)))
    u_ts2_rec = np.zeros((1, 1, len(t)))

    u_min = np.asarray(-12)
    u_max = np.asarray(12)

    for k in range(len(t)):
        r_rec[:, :, k] = r

        # Without feedforward
        u = K @ (r - x)
        u = np.clip(u, u_min, u_max)
        x = sysd.A @ x + sysd.B @ u
        x_rec[:, :, k] = x
        u_rec[:, :, k] = u

        # With steady-state feedforward
        u_ss = K @ (r - x_ss) + Kff_ss @ r
        u_ss = np.clip(u_ss, u_min, u_max)
        x_ss = sysd.A @ x_ss + sysd.B @ u_ss
        x_ss_rec[:, :, k] = x_ss
        u_ss_rec[:, :, k] = u_ss

        # Plant inversion (Q and R cost)
        u_ts1 = K @ (r - x_ts1) + Kff_ts1 @ (r - sysd.A @ r)
        u_ts1 = np.clip(u_ts1, u_min, u_max)
        x_ts1 = sysd.A @ x_ts1 + sysd.B @ u_ts1
        x_ts1_rec[:, :, k] = x_ts1
        u_ts1_rec[:, :, k] = u_ts1

        # Plant inversion
        u_ts2 = K @ (r - x_ts2) + Kff_ts2 @ (r - sysd.A @ r)
        u_ts2 = np.clip(u_ts2, u_min, u_max)
        x_ts2 = sysd.A @ x_ts2 + sysd.B @ u_ts2
        x_ts2_rec[:, :, k] = x_ts2
        u_ts2_rec[:, :, k] = u_ts2

    plt.figure(1)

    plt.subplot(3, 1, 1)
    plt.plot(t, r_rec[0, 0, :], label="Reference")
    plt.ylabel("$\omega$ (rad/s)")
    plt.plot(t, x_rec[0, 0, :], label="No FF")
    plt.plot(t, x_ss_rec[0, 0, :], label="Steady-state FF")
    plt.plot(t, x_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()

    plt.subplot(3, 1, 2)
    plt.plot(t, r_rec[1, 0, :], label="Reference")
    plt.ylabel("Current (A)")
    plt.plot(t, x_rec[1, 0, :], label="No FF")
    plt.plot(t, x_ss_rec[1, 0, :], label="Steady-state FF")
    plt.plot(t, x_ts1_rec[1, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()

    plt.subplot(3, 1, 3)
    plt.plot(t, u_rec[0, 0, :], label="No FF")
    plt.plot(t, u_ss_rec[0, 0, :], label="Steady-state FF")
    plt.plot(t, u_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()
    plt.ylabel("Control effort (V)")
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("case_study_ss_ff1")

    plt.figure(2)

    plt.subplot(3, 1, 1)
    plt.plot(t, r_rec[0, 0, :], label="Reference")
    plt.ylabel("$\omega$ (rad/s)")
    plt.plot(t, x_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.plot(t, x_ts2_rec[0, 0, :], label="Plant inversion (Q cost only)")
    plt.legend()

    plt.subplot(3, 1, 2)
    plt.plot(t, r_rec[1, 0, :], label="Reference")
    plt.ylabel("Current (A)")
    plt.plot(t, x_ts1_rec[1, 0, :], label="Plant inversion (Q and R cost)")
    plt.plot(t, x_ts2_rec[1, 0, :], label="Plant inversion (Q cost only)")
    plt.legend()

    plt.subplot(3, 1, 3)
    plt.plot(t, u_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.plot(t, u_ts2_rec[0, 0, :], label="Plant inversion (Q cost only)")
    plt.legend()
    plt.ylabel("Control effort (V)")
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("case_study_ss_ff2")
    else:
        plt.show()
def main():
    dt = 0.00505
    flywheel = Flywheel(dt)

    # Set up graphing
    l0 = 0.1
    l1 = l0 + 5.0
    l2 = l1 + 0.1
    t = np.arange(0, l2 + 5.0, dt)

    refs = []

    # Generate references for simulation
    for i in range(len(t)):
        if t[i] < l0:
            r = np.array([[0]])
        elif t[i] < l1:
            r = np.array([[9000 / 60 * 2 * math.pi]])
        else:
            r = np.array([[0]])
        refs.append(r)

    plt.figure(1)
    x_rec, ref_rec, u_rec, y_rec = flywheel.generate_time_responses(t, refs)

    plt.ylabel(flywheel.state_labels[0])
    plt.plot(t, flywheel.extract_row(x_rec, 0), label="Output")
    plt.plot(t, flywheel.extract_row(ref_rec, 0), label="Setpoint")

    fill_end = int(3.0 / dt)
    plt.fill_between(
        t[:fill_end],
        np.ravel(x_rec)[:fill_end],
        np.ravel(ref_rec)[:fill_end],
        color=(0.5, 0.5, 0.5, 0.5),
        label="Error area for integral term",
    )
    plt.legend()
    plt.xlabel("Time (s)")

    annotate_t = 3.25
    time = int(annotate_t / dt)
    bottom = np.ravel(x_rec[0, time])
    top = np.ravel(ref_rec[0, time])
    plt.annotate(
        "",
        xy=(annotate_t, bottom - 5),  # Start coord of arrow
        xycoords="data",
        xytext=(annotate_t, top + 5),  # End coord of arrow
        textcoords="data",
        arrowprops=dict(arrowstyle="<->", connectionstyle="arc3,rad=0"),
        ha="center",
        va="center",
    )
    plt.annotate(
        "steady-state error",
        xy=(annotate_t + 0.125, (top + bottom) / 2.0),  # Start coord of arrow
        xycoords="data",
        ha="left",
        va="center",
    )

    if "--noninteractive" in sys.argv:
        latex.savefig("p_controller_ss_error")
    else:
        plt.show()
def main():
    i_stall = 134
    i_free = 0.7
    rpm_free = 18730
    p_max = 347
    t_stall = 0.71

    # 775pro data
    fig, ax_left = plt.subplots()
    ax_left.set_xlabel("Speed (RPM)")

    rpm = np.arange(0, rpm_free, 50)
    line1 = ax_left.plot(rpm, [i_stall - i_stall / rpm_free * x for x in rpm],
                         "b",
                         label="Current (A)")
    ax_left.annotate(
        "Stall current: " + str(i_stall) + " A",
        xy=(0, i_stall),
        xytext=(0, 160),
        arrowprops=dict(arrowstyle="->"),
    )
    line2 = ax_left.plot(
        rpm,
        [
            -p_max / (rpm_free / 2.0)**2 * (x - rpm_free / 2.0)**2 + p_max
            for x in rpm
        ],
        "g",
        label="Output power (W)",
    )
    ax_left.annotate(
        "Peak power: " + str(p_max) + " W" + os.linesep + "(at " +
        str(rpm_free / 2.0) + " RPM)",
        xy=(rpm_free / 2.0, p_max),
        xytext=(7000, 365),
        arrowprops=dict(arrowstyle="->"),
    )
    ax_left.set_ylabel("Current (A), Power (W)")
    ax_left.set_ylim([0, 400])
    plt.legend(loc=3)

    ax_right = ax_left.twinx()
    line3 = ax_right.plot(rpm, [t_stall - t_stall / rpm_free * x for x in rpm],
                          "y",
                          label="Torque (N-m)")
    ax_right.annotate(
        "Stall torque: " + str(t_stall) + " N-m",
        xy=(0, t_stall),
        xytext=(0, 0.75),
        arrowprops=dict(arrowstyle="->"),
    )
    ax_right.annotate(
        "Free speed: " + str(rpm_free) + " RPM" + os.linesep +
        "Free current: " + str(i_free) + " A",
        xy=(rpm_free, 0),
        xytext=(11000, 0.3),
        arrowprops=dict(arrowstyle="->"),
    )
    ax_right.set_ylabel("Torque (N-m)")
    ax_right.set_ylim([0, 0.8])
    plt.legend(loc=1)

    if "--noninteractive" in sys.argv:
        latex.savefig("motor_data")
    else:
        plt.show()
Example #8
0
def main():
    f_f = 349.23
    f_a = 440
    f_c = 261.63

    T = 0.000001
    xlim = [0, 0.05]
    ylim = [-3, 3]
    x = np.arange(xlim[0], xlim[1], T)
    plt.xlim(xlim)
    plt.ylim(ylim)

    yf = np.sin(f_f * 2 * math.pi * x)
    ya = np.sin(f_a * 2 * math.pi * x)
    yc = np.sin(f_c * 2 * math.pi * x)
    ysum = yf + ya + yc
    ysum_attenuating = ysum * np.exp(-25 * x)

    num_plots = 2

    plt.subplot(num_plots, 1, 1)
    plt.ylim(ylim)
    plt.ylabel("Fmaj4 ($\sigma = 0$)")
    plt.plot(x, ysum)
    plt.gca().axes.get_xaxis().set_ticks([])

    plt.subplot(num_plots, 1, 2)
    plt.ylim(ylim)
    plt.ylabel("Attenuating Fmaj4 ($\sigma = -25$)")
    plt.plot(x, ysum_attenuating)
    plt.gca().axes.get_xaxis().set_ticks([])

    plt.xlabel("$t$")

    if "--noninteractive" in sys.argv:
        latex.savefig("laplace_chord_attenuating")

    x, y = np.mgrid[-150.0:150.0:500j, 200.0:500.0:500j]

    # Need an (N, 2) array of (x, y) pairs.
    xy = np.column_stack([x.flat, y.flat])

    z = np.zeros(xy.shape[0])
    for i, pair in enumerate(xy):
        s = pair[0] + pair[1] * 1j
        h = sin_tf(f_f, s) * sin_tf(f_a, s) * sin_tf(f_c, s)
        z[i] = clamp(math.sqrt(h.real**2 + h.imag**2), -30, 30)
    z = z.reshape(x.shape)

    fig = plt.figure(2)
    ax = fig.add_subplot(111, projection="3d")
    ax.plot_surface(x, y, z, cmap=cm.coolwarm)
    ax.set_xlabel("$Re(\sigma)$")
    ax.set_ylabel("$Im(j\omega)$")
    ax.set_zlabel("$H(s)$")
    ax.set_zticks([])

    if "--noninteractive" in sys.argv:
        latex.savefig("laplace_chord_3d")
    else:
        plt.show()
Example #9
0
def main():
    J = 7.7500e-05
    b = 8.9100e-05
    Kt = 0.0184
    Ke = 0.0211
    R = 0.0916
    L = 5.9000e-05

    # fmt: off
    A = np.array([[-b / J, Kt / J], [-Ke / L, -R / L]])
    B = np.array([[0], [1 / L]])
    C = np.array([[1, 0]])
    D = np.array([[0]])
    # fmt: on

    sysc = ct.StateSpace(A, B, C, D)

    dt = 0.0001
    tmax = 0.025

    sysd = sysc.sample(dt)

    # fmt: off
    Q = np.array([[1 / 20**2, 0], [0, 1 / 40**2]])
    R = np.array([[1 / 12**2]])
    # fmt: on
    K = fct.lqr(sysd, Q, R)

    # Plant inversions
    Kff_ts1 = np.linalg.pinv(sysd.B)
    Kff_ts2 = np.linalg.inv(sysd.B.T * Q * sysd.B + R) * (sysd.B.T * Q)

    t = np.arange(0, tmax, dt)
    r = np.array([[2000 * 0.1047], [0]])
    r_rec = np.zeros((2, 1, len(t)))

    # No feedforward
    x = np.array([[0], [0]])
    x_rec = np.zeros((2, 1, len(t)))
    u_rec = np.zeros((1, 1, len(t)))

    # Plant inversion (Q cost only)
    x_ts1 = np.array([[0], [0]])
    x_ts1_rec = np.zeros((2, 1, len(t)))
    u_ts1_rec = np.zeros((1, 1, len(t)))

    # Plant inversion (Q and R cost)
    x_ts2 = np.array([[0], [0]])
    x_ts2_rec = np.zeros((2, 1, len(t)))
    u_ts2_rec = np.zeros((1, 1, len(t)))

    u_min = np.asarray(-12)
    u_max = np.asarray(12)

    for k in range(len(t)):
        r_rec[:, :, k] = r

        # Without feedforward
        u = K @ (r - x)
        u = np.clip(u, u_min, u_max)
        x = sysd.A @ x + sysd.B @ u
        x_rec[:, :, k] = x
        u_rec[:, :, k] = u

        # Plant inversion (Q and R cost)
        u_ts1 = K @ (r - x_ts1) + Kff_ts1 @ (r - sysd.A @ r)
        u_ts1 = np.clip(u_ts1, u_min, u_max)
        x_ts1 = sysd.A @ x_ts1 + sysd.B @ u_ts1
        x_ts1_rec[:, :, k] = x_ts1
        u_ts1_rec[:, :, k] = u_ts1

        # Plant inversion
        u_ts2 = K @ (r - x_ts2) + Kff_ts2 @ (r - sysd.A @ r)
        u_ts2 = np.clip(u_ts2, u_min, u_max)
        x_ts2 = sysd.A @ x_ts2 + sysd.B @ u_ts2
        x_ts2_rec[:, :, k] = x_ts2
        u_ts2_rec[:, :, k] = u_ts2

    plt.figure(1)

    plt.subplot(3, 1, 1)
    plt.plot(t, r_rec[0, 0, :], label="Reference")
    plt.ylabel("$\omega$ (rad/s)")
    plt.plot(t, x_rec[0, 0, :], label="No feedforward")
    plt.plot(t, x_ts2_rec[0, 0, :], label="Plant inversion")
    plt.plot(t, x_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()

    plt.subplot(3, 1, 2)
    plt.plot(t, r_rec[1, 0, :], label="Reference")
    plt.ylabel("Current (A)")
    plt.plot(t, x_rec[1, 0, :], label="No feedforward")
    plt.plot(t, x_ts2_rec[1, 0, :], label="Plant inversion")
    plt.plot(t, x_ts1_rec[1, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()

    plt.subplot(3, 1, 3)
    plt.plot(t, u_rec[0, 0, :], label="No feedforward")
    plt.plot(t, u_ts2_rec[0, 0, :], label="Plant inversion")
    plt.plot(t, u_ts1_rec[0, 0, :], label="Plant inversion (Q and R cost)")
    plt.legend()
    plt.ylabel("Control effort (V)")
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("case_study_ff")
    else:
        plt.show()
def main():
    # x_1: robot position measured from corner
    # x_2: robot velocity with positive direction toward wall
    # x_3: wall position measured from corner
    xhat = np.array([[0.0], [0.0], [0.0]])

    # y_1: measurement of distance from robot to corner
    # y_2: measurement of distance from robot to wall
    y = np.genfromtxt("kalman_robot.csv", delimiter=",")

    # fmt: off
    phi = np.array([[1, 1, 0], [0, 1, 0], [0, 0, 1]])
    gamma = np.array([[0], [0.1], [0]])

    Q = np.array([[1]])
    R = np.array([[10, 0], [0, 10]])

    P = np.zeros((3, 3))
    K = np.zeros((3, 2))
    H = np.array([[1, 0, 0], [-1, 0, 1]])
    # fmt: on

    # Initialize matrix storage
    xhat_pre_rec = np.zeros((3, 1, y.shape[1]))
    xhat_post_rec = np.zeros((3, 1, y.shape[1]))
    P_pre_rec = np.zeros((3, 3, y.shape[1]))
    P_post_rec = np.zeros((3, 3, y.shape[1]))
    xhat_smooth_rec = np.zeros((3, 1, y.shape[1]))
    P_smooth_rec = np.zeros((3, 3, y.shape[1]))
    t = [0] * (y.shape[1])

    # Forward Kalman filter

    # Set up initial conditions for first measurement
    xhat[0] = y[0, 0]
    xhat[1] = 0.2
    xhat[2] = y[0, 0] + y[1, 0]
    # fmt: off
    P = np.array([[10, 0, 10], [0, 1, 0], [10, 0, 20]])
    # fmt: on

    xhat_pre_rec[:, :, 1] = xhat
    P_pre_rec[:, :, 1] = P
    xhat_post_rec[:, :, 1] = xhat
    P_post_rec[:, :, 1] = P
    t[1] = 1

    for k in range(2, 100):
        # Predict
        xhat = phi @ xhat
        P = phi @ P @ phi.T + gamma @ Q @ gamma.T

        xhat_pre_rec[:, :, k] = xhat
        P_pre_rec[:, :, k] = P

        # Update
        K = P @ H.T @ np.linalg.inv(H @ P @ H.T + R)
        xhat += K @ (y[:, k:k + 1] - H @ xhat)
        P = (np.eye(3, 3) - K @ H) @ P

        xhat_post_rec[:, :, k] = xhat
        P_post_rec[:, :, k] = P
        t[k] = k

    # Kalman smoother

    # Last estimate is already optional, so add it to the record
    xhat_smooth_rec[:, :, -1] = xhat_post_rec[:, :, -1]
    P_smooth_rec[:, :, -1] = P_post_rec[:, :, -1]

    for k in range(y.shape[1] - 2, 0, -1):
        A = P_post_rec[:, :, k] @ phi.T @ np.linalg.inv(P_pre_rec[:, :, k + 1])
        xhat = xhat_post_rec[:, :, k] + A @ (xhat_smooth_rec[:, :, k + 1] -
                                             xhat_pre_rec[:, :, k + 1])
        P = P_post_rec[:, :, k] + A @ (P - P_pre_rec[:, :, k + 1]) @ A.T

        xhat_smooth_rec[:, :, k] = xhat
        P_smooth_rec[:, :, k] = P

    # Robot position
    plt.figure(1)
    plt.xlabel("Time (s)")
    plt.ylabel("Position (cm)")
    plt.plot(t[1:], xhat_post_rec[0, 0, 1:], label="Kalman filter")
    plt.plot(t[1:], xhat_smooth_rec[0, 0, 1:], label="Kalman smoother")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_smoother_robot_pos")

    # Robot velocity
    plt.figure(2)
    plt.xlabel("Time (s)")
    plt.ylabel("Velocity (cm)")
    plt.plot(t[1:], xhat_post_rec[1, 0, 1:], label="Kalman filter")
    plt.plot(t[1:], xhat_smooth_rec[1, 0, 1:], label="Kalman smoother")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_smoother_robot_vel")

    # Wall position
    plt.figure(3)
    plt.xlabel("Time (s)")
    plt.ylabel("Wall position (cm)")
    plt.plot(t[1:], xhat_post_rec[2, 0, 1:], label="Kalman filter")
    plt.plot(t[1:], xhat_smooth_rec[2, 0, 1:], label="Kalman smoother")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_smoother_wall_pos")

    # Robot position variance
    plt.figure(4)
    plt.xlabel("Time (s)")
    plt.ylabel("Robot position variance ($cm^2$)")
    plt.plot(t[1:], P_post_rec[1, 1, 1:], label="Kalman filter")
    plt.plot(t[1:], P_smooth_rec[1, 1, 1:], label="Kalman smoother")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_smoother_robot_pos_variance")
    else:
        plt.show()
def main():
    dt = 0.05
    drivetrain = Drivetrain(dt)

    t, xprof, vprof, aprof = fct.generate_s_curve_profile(
        max_v=4.0, max_a=3.5, time_to_max_a=1.0, dt=dt, goal=10.0
    )

    # Initial robot pose
    pose = Pose2d(2, 0, np.pi / 2.0)
    desired_pose = Pose2d()
    twist_pose = Pose2d(2, 0, np.pi / 2.0)

    # Ramsete tuning constants
    b = 2
    zeta = 0.7

    vl = float("inf")
    vr = float("inf")

    x_rec = []
    y_rec = []
    twist_x_rec = []
    twist_y_rec = []
    vref_rec = []
    omegaref_rec = []
    v_rec = []
    omega_rec = []
    ul_rec = []
    ur_rec = []

    # Log initial data for plots
    vref_rec.append(0)
    omegaref_rec.append(0)
    x_rec.append(pose.x)
    y_rec.append(pose.y)
    twist_x_rec.append(twist_pose.x)
    twist_y_rec.append(twist_pose.y)
    ul_rec.append(drivetrain.u[0, 0])
    ur_rec.append(drivetrain.u[1, 0])
    v_rec.append(0)
    omega_rec.append(0)

    # Run Ramsete
    i = 0
    while i < len(t) - 1:
        desired_pose.x = 0
        desired_pose.y = xprof[i]
        desired_pose.theta = np.pi / 2.0

        # pose_desired, v_desired, omega_desired, pose, b, zeta
        vref, omegaref = ramsete(desired_pose, vprof[i], 0, pose, b, zeta)
        vl, vr = get_diff_vels(vref, omegaref, drivetrain.rb * 2.0)
        next_r = np.array([[vl], [vr]])
        drivetrain.update(next_r)
        vc = (drivetrain.x[0, 0] + drivetrain.x[1, 0]) / 2.0
        omega = (drivetrain.x[1, 0] - drivetrain.x[0, 0]) / (2.0 * drivetrain.rb)

        # Log data for plots
        vref_rec.append(vref)
        omegaref_rec.append(omegaref)
        x_rec.append(pose.x)
        y_rec.append(pose.y)
        twist_x_rec.append(twist_pose.x)
        twist_y_rec.append(twist_pose.y)
        ul_rec.append(drivetrain.u[0, 0])
        ur_rec.append(drivetrain.u[1, 0])
        v_rec.append(vc)
        omega_rec.append(omega)

        # Update nonlinear observer
        pose.x += vc * math.cos(pose.theta) * dt
        pose.y += vc * math.sin(pose.theta) * dt
        pose.theta += omega * dt
        twist_pose.exp(Twist2d(vc, 0, omega), dt)

        if i < len(t) - 1:
            i += 1

    plt.figure(1)
    plt.ylabel("Odometry error (m)")
    plt.plot(t, np.subtract(twist_x_rec, x_rec), label="Error in x")
    plt.plot(t, np.subtract(twist_y_rec, y_rec), label="Error in y")
    plt.legend()
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_twist_odometry_error")
    else:
        plt.show()
def main():
    # x_1: robot position measured from corner
    # x_2: robot velocity with positive direction toward wall
    # x_3: wall position measured from corner
    xhat = np.array([[0], [0], [0]])

    # y_1: measurement of distance from robot to corner
    # y_2: measurement of distance from robot to wall
    y = np.genfromtxt("kalman_robot.csv", delimiter=",")

    # fmt: off
    phi = np.array([[1, 1, 0],
                    [0, 0, 0],
                    [0, 0, 1]])
    gamma = np.array([[0],
                      [0.1],
                      [0]])

    Q = np.array([[1]])
    R = np.array([[10, 0],
                   [0, 10]])

    P = np.zeros((3, 3))
    K = np.zeros((3, 2))
    H = np.array([[1, 0, 0],
                  [-1, 0, 1]])
    # fmt: on

    num_points = y.shape[1]
    xhat_rec = np.zeros((3, 1, num_points))
    P_rec = np.zeros((3, 3, num_points))
    t = np.linspace(0, num_points, num_points)

    dt = 1

    for k in range(100):
        if k == 1:
            xhat[0] = y[0, k]
            xhat[1] = (y[0, 1] - y[0, 0]) / dt
            xhat[2] = y[0, k] + y[1, k]
            # fmt: off
            P = np.array([[10, 10 / dt, 10],
                          [10 / dt, 20 / dt**2, 10 / dt],
                          [10, 10 / dt, 20]])
            # fmt: on

            xhat_rec[:, :, k] = xhat
            P_rec[:, :, k] = np.array([P[0, 0], P[1, 1], P[2, 2]]).T
        elif k > 1:
            # Predict
            xhat = phi @ xhat + np.array([[0, 0.8, 0]]).T
            P = phi @ P @ phi.T + gamma @ Q @ gamma.T

            # Update
            K = P @ H.T @ np.linalg.inv(H @ P @ H.T + R)
            xhat += K @ (y[:, k : k + 1] - H @ xhat)
            P = (np.eye(3, 3) - K @ H) @ P

            xhat_rec[:, :, k] = xhat
            P_rec[:, :, k] = np.array([P[0, 0], P[1, 1], P[2, 2]]).T

    # State estimates and measurements
    plt.figure(1)
    plt.xlabel("Time (s)")
    plt.plot(t[1:], xhat_rec[0, 0, 1:], label="Robot position estimate (cm)")
    plt.plot(t[1:], xhat_rec[1, 0, 1:], label="Robot velocity estimate (cm/s)")
    plt.plot(t[1:], xhat_rec[2, 0, 1:], label="Wall position estimate (cm)")
    plt.plot(t[1:], y[0, 1:].T, label="Robot to corner measurement (cm)")
    plt.plot(t[1:], y[1, 1:].T, label="Robot to wall measurement (cm)")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_filter_all")

    # Robot position estimate and variance
    plt.figure(2)
    plt.xlabel("Time (s)")
    plt.plot(t[1:], xhat_rec[0, 0, 1:], label="Robot position estimate (cm)")
    plt.plot(t[1:], P_rec[0, 0, 1:], label="Robot position variance ($cm^2$)")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_filter_robot_pos")

    # Wall position estimate and variance
    plt.figure(3)
    plt.xlabel("Time (s)")
    plt.plot(t[1:], xhat_rec[2, 0, 1:], label="Wall position estimate (cm)")
    plt.plot(t[1:], P_rec[2, 0, 1:], label="Wall position variance ($cm^2$)")
    plt.legend()
    if "--noninteractive" in sys.argv:
        latex.savefig("kalman_filter_wall_pos")
    else:
        plt.show()
Example #13
0
def main():
    J = 7.7500e-05
    b = 8.9100e-05
    Kt = 0.0184
    Ke = 0.0211
    R = 0.0916
    L = 5.9000e-05

    # fmt: off
    A = np.array([[-b / J, Kt / J], [-Ke / L, -R / L]])
    B = np.array([[0], [1 / L]])
    C = np.array([[1, 0]])
    D = np.array([[0]])
    # fmt: on

    sysc = ct.StateSpace(A, B, C, D)

    dt = 0.0001
    tmax = 0.025

    sysd = sysc.sample(dt)

    # fmt: off
    Q = np.array([[1 / 20**2, 0], [0, 1 / 40**2]])
    R = np.array([[1 / 12**2]])
    # fmt: on
    K_pp1 = ct.place(sysd.A, sysd.B, [0.1, 0.9])
    K_pp2 = ct.place(sysd.A, sysd.B, [0.1, 0.8])
    K_lqr = fct.lqr(sysd, Q, R)

    t = np.arange(0, tmax, dt)
    r = np.array([[2000 * 0.1047], [0]])
    r_rec = np.zeros((2, 1, len(t)))

    # Pole placement 1
    x_pp1 = np.array([[0], [0]])
    x_pp1_rec = np.zeros((2, 1, len(t)))
    u_pp1_rec = np.zeros((1, 1, len(t)))

    # Pole placement 2
    x_pp2 = np.array([[0], [0]])
    x_pp2_rec = np.zeros((2, 1, len(t)))
    u_pp2_rec = np.zeros((1, 1, len(t)))

    # LQR
    x_lqr = np.array([[0], [0]])
    x_lqr_rec = np.zeros((2, 1, len(t)))
    u_lqr_rec = np.zeros((1, 1, len(t)))

    u_min = np.asarray(-12)
    u_max = np.asarray(12)

    for k in range(len(t)):
        # Pole placement 1
        u_pp1 = K_pp1 @ (r - x_pp1)

        # Pole placement 2
        u_pp2 = K_pp2 @ (r - x_pp2)

        # LQR
        u_lqr = K_lqr @ (r - x_lqr)

        u_pp1 = np.clip(u_pp1, u_min, u_max)
        x_pp1 = sysd.A @ x_pp1 + sysd.B @ u_pp1
        u_pp2 = np.clip(u_pp2, u_min, u_max)
        x_pp2 = sysd.A @ x_pp2 + sysd.B @ u_pp2
        u_lqr = np.clip(u_lqr, u_min, u_max)
        x_lqr = sysd.A @ x_lqr + sysd.B @ u_lqr

        r_rec[:, :, k] = r
        x_pp1_rec[:, :, k] = x_pp1
        u_pp1_rec[:, :, k] = u_pp1
        x_pp2_rec[:, :, k] = x_pp2
        u_pp2_rec[:, :, k] = u_pp2
        x_lqr_rec[:, :, k] = x_lqr
        u_lqr_rec[:, :, k] = u_lqr

    plt.figure(1)

    plt.subplot(3, 1, 1)
    plt.plot(t, r_rec[0, 0, :], label="Reference")
    plt.ylabel("$\omega$ (rad/s)")
    plt.plot(t,
             x_pp1_rec[0, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.9, 0)$")
    plt.plot(t,
             x_pp2_rec[0, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.8, 0)$")
    plt.plot(t, x_lqr_rec[0, 0, :], label="LQR")
    plt.legend()

    plt.subplot(3, 1, 2)
    plt.plot(t, r_rec[1, 0, :], label="Reference")
    plt.ylabel("Current (A)")
    plt.plot(t,
             x_pp1_rec[1, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.9, 0)$")
    plt.plot(t,
             x_pp2_rec[1, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.8, 0)$")
    plt.plot(t, x_lqr_rec[1, 0, :], label="LQR")
    plt.legend()

    plt.subplot(3, 1, 3)
    plt.plot(t,
             u_pp1_rec[0, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.9, 0)$")
    plt.plot(t,
             u_pp2_rec[0, 0, :],
             label="Pole placement at $(0.1, 0)$ and $(0.8, 0)$")
    plt.plot(t, u_lqr_rec[0, 0, :], label="LQR")
    plt.legend()
    plt.ylabel("Control effort (V)")
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("case_study_pp_lqr")
    else:
        plt.show()
def main():
    dt = 0.02
    drivetrain = Drivetrain(dt)

    data = np.genfromtxt("ramsete_traj.csv", delimiter=",")
    t = data[1:, 0].T
    xprof = data[1:, 1].T
    yprof = data[1:, 2].T
    thetaprof = data[1:, 3].T
    vprof = data[1:, 4].T
    omegaprof = data[1:, 5].T

    # Initial robot pose
    pose = Pose2d(xprof[0] + 0.5, yprof[0] + 0.5, np.pi)
    desired_pose = Pose2d()

    # Ramsete tuning constants
    b = 2
    zeta = 0.7

    vl = float("inf")
    vr = float("inf")

    x_rec = []
    y_rec = []
    theta_rec = []
    vl_rec = []
    vlref_rec = []
    vr_rec = []
    vrref_rec = []
    ul_rec = []
    ur_rec = []

    # Run Ramsete
    i = 0
    while i < len(t) - 1:
        desired_pose.x = xprof[i]
        desired_pose.y = yprof[i]
        desired_pose.theta = thetaprof[i]

        # pose_desired, v_desired, omega_desired, pose, b, zeta
        vref, omegaref = ramsete(desired_pose, vprof[i], omegaprof[i], pose, b,
                                 zeta)
        vlref, vrref = get_diff_vels(vref, omegaref, drivetrain.rb * 2.0)
        next_r = np.array([[vlref], [vrref]])
        drivetrain.update(next_r)
        vc = (drivetrain.x[0, 0] + drivetrain.x[1, 0]) / 2.0
        omega = (drivetrain.x[1, 0] - drivetrain.x[0, 0]) / (2.0 *
                                                             drivetrain.rb)
        vl, vr = get_diff_vels(vc, omega, drivetrain.rb * 2.0)

        # Log data for plots
        vlref_rec.append(vlref)
        vrref_rec.append(vrref)
        x_rec.append(pose.x)
        y_rec.append(pose.y)
        theta_rec.append(pose.theta)
        vl_rec.append(vl)
        vr_rec.append(vr)
        ul_rec.append(drivetrain.u[0, 0])
        ur_rec.append(drivetrain.u[1, 0])

        # Update nonlinear observer
        pose.x += vc * math.cos(pose.theta) * dt
        pose.y += vc * math.sin(pose.theta) * dt
        pose.theta += omega * dt

        if i < len(t) - 1:
            i += 1

    plt.figure(1)
    plt.plot(x_rec, y_rec, label="Ramsete controller")
    plt.plot(xprof, yprof, label="Reference trajectory")
    plt.xlabel("x (m)")
    plt.ylabel("y (m)")
    plt.legend()

    # Equalize aspect ratio
    xlim = plt.xlim()
    width = abs(xlim[0]) + abs(xlim[1])
    ylim = plt.ylim()
    height = abs(ylim[0]) + abs(ylim[1])
    if width > height:
        plt.ylim([-width / 2, width / 2])
    else:
        plt.xlim([-height / 2, height / 2])

    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_traj_xy")

    plt.figure(2)
    num_plots = 7
    plt.subplot(num_plots, 1, 1)
    plt.title("Time domain responses")
    plt.ylabel(
        "x position (m)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t[:-1], x_rec, label="Estimated state")
    plt.plot(t, xprof, label="Reference")
    plt.legend()
    plt.subplot(num_plots, 1, 2)
    plt.ylabel(
        "y position (m)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t[:-1], y_rec, label="Estimated state")
    plt.plot(t, yprof, label="Reference")
    plt.legend()
    plt.subplot(num_plots, 1, 3)
    plt.ylabel(
        "Theta (rad)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t[:-1], theta_rec, label="Estimated state")
    plt.plot(t, thetaprof, label="Reference")
    plt.legend()

    t = t[:-1]
    plt.subplot(num_plots, 1, 4)
    plt.ylabel(
        "Left velocity (m/s)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, vl_rec, label="Estimated state")
    plt.plot(t, vlref_rec, label="Reference")
    plt.legend()
    plt.subplot(num_plots, 1, 5)
    plt.ylabel(
        "Right velocity (m/s)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, vr_rec, label="Estimated state")
    plt.plot(t, vrref_rec, label="Reference")
    plt.legend()
    plt.subplot(num_plots, 1, 6)
    plt.ylabel(
        "Left voltage (V)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, ul_rec, label="Control effort")
    plt.legend()
    plt.subplot(num_plots, 1, 7)
    plt.ylabel(
        "Right voltage (V)",
        horizontalalignment="right",
        verticalalignment="center",
        rotation=45,
    )
    plt.plot(t, ur_rec, label="Control effort")
    plt.legend()
    plt.xlabel("Time (s)")

    if "--noninteractive" in sys.argv:
        latex.savefig("ramsete_traj_response")
    else:
        plt.show()
def main():
    T = 0.000001
    xlim = [0, 0.05]
    ylim = [-3, 3]
    x = np.arange(xlim[0], xlim[1], T)
    plt.xlim(xlim)
    plt.ylim(ylim)

    f_f = 349.23
    f_a = 440
    f_c = 261.63

    yf = np.sin(f_f * 2 * math.pi * x)
    ya = np.sin(f_a * 2 * math.pi * x)
    yc = np.sin(f_c * 2 * math.pi * x)
    ysum = yf + ya + yc

    num_plots = 4

    plt.subplot(num_plots, 1, 1)
    plt.ylim(ylim)
    plt.ylabel("Fmaj4")
    plt.plot(x, ysum)
    plt.gca().axes.get_xaxis().set_ticks([])

    plt.subplot(num_plots, 1, 2)
    plt.ylim(ylim)
    plt.ylabel("$F_4$")
    plt.plot(x, yf)
    plt.gca().axes.get_xaxis().set_ticks([])

    plt.subplot(num_plots, 1, 3)
    plt.ylim(ylim)
    plt.ylabel("$A_4$")
    plt.plot(x, ya)
    plt.gca().axes.get_xaxis().set_ticks([])

    plt.subplot(num_plots, 1, 4)
    plt.ylim(ylim)
    plt.ylabel("$C_4$")
    plt.plot(x, yc)

    plt.xlabel("$t$")

    if "--noninteractive" in sys.argv:
        latex.savefig("fourier_chord")

    plt.figure(2)
    N = 1000000  # Number of samples
    T = 1.0 / 2000.0  # Sample spacing
    x = np.arange(0.0, N * T, T)
    yf = np.sin(f_f * 2 * math.pi * x)
    ya = np.sin(f_a * 2 * math.pi * x)
    yc = np.sin(f_c * 2 * math.pi * x)
    ysum = yf + ya + yc
    yf = scipy.fftpack.fft(ysum)
    xf = np.linspace(0.0, 1.0 / (2.0 * T), N // 2)

    plt.plot(xf, 2.0 / N * np.abs(yf[:N // 2]))
    plt.xlabel("Frequency (Hz)")
    plt.xlim([0, 700])

    if "--noninteractive" in sys.argv:
        latex.savefig("fourier_chord_fft")
    else:
        plt.show()