Slew rate limiter inital acceleration is slow

We’re using a slew rate limiter to reduce the acceleration speed, so we can prevent our robot from wobbling. It’s working well for the most part, but our robot takes about 1-2 seconds to go from stationary to drive. Does anyone know how to solve this? We’ve tried tuning the rate limit and enabling/disabling coast mode on our motors.

Here’s the relevent code:

        SlewRateLimiter filter = new SlewRateLimiter(0.8);
        
        public void arcadeDrive(double leftSpeed, double rightSpeed) {
            m_drive.arcadeDrive(filter.calculate(leftSpeed), rightSpeed);
        }

What a slew rate limiter does it limit acceleration change. The 0.8 you are passing means that it will take one second for input to go from 0 to 0.8, 0.4 to -0.4 etc. Hence, is should only be taking around 1.25 seconds to get to full speed from stopped. When you say from stationary to drive do you mean stationary to some movement of stationary too full driving speed? The first is the intended behavior of a slew rate limiter.

1 Like

What other values did you try? If you want to accelerate faster you need a larger number

I think of it as taking 1/x to allow full speed, where x is my slew value. At .8 it takes 1/.8, or 1.25, seconds to allow full speed. If you use 4 it only takes 1/4 of a second to allow full speed.

SlewRateLimiters are not ideal for this scenario. You get much more driver-friendly results by using ramping.

Voltage ramping is the same thing as slew rate limiting voltage. They both limit the rate of change over time to some maximum value.

2 Likes

It only limits acceleration directly if you slew rate limit velocity. If the user is passing in voltage, the acceleration approaches a maximum while the voltage is changing at the slew rate limit.

Notice how the acceleration in the figure below is converging to a steady-state nonzero value when the voltage is changing, and it’s converging to zero when the voltage is constant.

Here’s the script I was using to test this stuff a few days ago for the WPILib 2023 differential drive acceleration limiter and implicit model follower PRs.

#!/usr/bin/env python3

import control as ct
import frccontrol as fct
import matplotlib.pyplot as plt
import numpy as np

DT = 0.005


class Drivetrain(fct.System):
    def __init__(self, dt):
        """Drivetrain subsystem.

        Keyword arguments:
        dt -- time between model/controller updates
        """
        fct.System.__init__(
            self,
            np.array([[-12.0]]),
            np.array([[12.0]]),
            dt,
            np.zeros((1, 1)),
            np.zeros((1, 1)),
        )

    def create_model(self, states, inputs):
        Kv = 3.02
        Ka = 0.642

        A = np.array([[-Kv / Ka]])
        B = np.array([[1.0 / Ka]])
        C = np.array([[1]])
        D = np.array([[0]])

        return ct.ss(A, B, C, D)


def plot_data(t_rec, x_rec, u_rec, a_rec, title):
    plt.figure()
    plt.title(title)
    plt.xlabel("Time (s)")
    plt.ylabel("Velocity (m/s)")
    plt.plot(t_rec, x_rec)

    plt.figure()
    plt.title(title)
    plt.xlabel("Time (s)")
    plt.ylabel("Voltage (V)")
    plt.plot(t_rec, u_rec)

    plt.figure()
    plt.title(title)
    plt.xlabel("Time (s)")
    plt.ylabel("Acceleration (m/s²)")
    plt.plot(t_rec, a_rec)


def run_voltage_slew_rate_limit_sim(drivetrain, t1, t2):
    """Runs simulation with voltage slew rate limit and returns t_rec, x_rec,
    u_rec, and a_rec.
    """
    t_rec = []
    x_rec = []
    u_rec = []
    a_rec = []
    x = np.array([[0]])

    # dx/dt = Ax + Bu where u = αt and [α] = V/T
    # d/dt(dx/dt = Ax + Bαt)
    # da/dt = A dx/dt + Bα
    # 0 = A dx/dt + Bα for steady-state acceleration
    # Bα = -A dx/dt
    # α = -B⁻¹A dx/dt
    # α = -(1/Ka)⁻¹(-Kv/Ka) dx/dt
    # α = Ka(Kv/Ka) dx/dt
    # α = Kv dx/dt
    #
    # Plug in max acceleration.
    #
    # α = Kv (u_max / Ka)
    # α = Kv/Ka u_max
    # α = -A u_max

    Kv = 3.02
    Ka = 0.642 * 2

    alpha = -drivetrain.sysc.A @ np.array([[12]])

    ts = np.arange(0, t1, DT)
    for i, t in enumerate(ts):
        u = np.clip(alpha * t, np.array([[-12]]), np.array([[12]]))
        a = drivetrain.sysc.A @ x + drivetrain.sysc.B @ u
        x = drivetrain.sysd.A @ x + drivetrain.sysd.B @ u

        t_rec.append(t)
        x_rec.append(x[0, 0])
        u_rec.append(u[0, 0])
        a_rec.append(a[0, 0])

    ts = np.arange(t1, t2, DT)
    for i, t in enumerate(ts):
        u = np.array([[12]]) - alpha * (t - t1)
        u = np.clip(
            np.array([[12]]) - alpha * (t - t1), np.array([[-12]]), np.array([[12]])
        )
        a = drivetrain.sysc.A @ x + drivetrain.sysc.B @ u
        x = drivetrain.sysd.A @ x + drivetrain.sysd.B @ u

        t_rec.append(t)
        x_rec.append(x[0, 0])
        u_rec.append(u[0, 0])
        a_rec.append(a[0, 0])

    plot_data(t_rec, x_rec, u_rec, a_rec, "Slew Rate Limit")


def run_imf_sim(drivetrain, t1, t2):
    """Runs simulation with voltage slew rate limit and returns t_rec, x_rec,
    u_rec, and a_rec.
    """
    t_rec = []
    x_rec = []
    u_rec = []
    a_rec = []
    x = np.array([[0]])

    # IMF ref model
    Kv = 3.02
    Ka = 0.642 * 2
    A = np.array([[-Kv / Ka]])
    B = np.array([[1.0 / Ka]])
    C = np.array([[1]])
    D = np.array([[0]])
    sysc_ref = ct.ss(A, B, C, D)

    A_imf = -np.linalg.solve(drivetrain.sysc.B, drivetrain.sysc.A - sysc_ref.A)
    B_imf = np.linalg.solve(drivetrain.sysc.B, sysc_ref.B)

    ts = np.arange(0, t1, DT)
    for i, t in enumerate(ts):
        u = A_imf @ x + B_imf @ np.array([[12]])
        a = drivetrain.sysc.A @ x + drivetrain.sysc.B @ u
        x = drivetrain.sysd.A @ x + drivetrain.sysd.B @ u

        t_rec.append(t)
        x_rec.append(x[0, 0])
        u_rec.append(u[0, 0])
        a_rec.append(a[0, 0])

    ts = np.arange(t1, t2, DT)
    for i, t in enumerate(ts):
        u = A_imf @ x + B_imf @ np.array([[0]])
        a = drivetrain.sysc.A @ x + drivetrain.sysc.B @ u
        x = drivetrain.sysd.A @ x + drivetrain.sysd.B @ u

        t_rec.append(t)
        x_rec.append(x[0, 0])
        u_rec.append(u[0, 0])
        a_rec.append(a[0, 0])

    plot_data(t_rec, x_rec, u_rec, a_rec, "IMF")


def main():
    drivetrain = Drivetrain(DT)

    run_voltage_slew_rate_limit_sim(drivetrain, 1, 2)
    # run_imf_sim(drivetrain, 1, 2)

    plt.show()


if __name__ == "__main__":
    main()

If you want open-loop, slew rate limiting voltage is your only option. If you have encoders and a drivetrain model, you can use DifferentialDriveAccelerationLimiter to limit linear and angular acceleration directly, or ImplicitModelFollower to make the drivetrain behave like a more benign one. The latter is also nice for making the robot easier to control by novice drivers.

4 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.