CrackRobotics

Step 1

PID control

Hold a torque-driven shoulder on target against gravity with P, I and D terms, and keep the integral from winding up.

What it computes

A PID controller turns an error e=qtarget−qe = q_{\text{target}} - q into a command uu, here the shoulder motor's torque:

u=kp e+ki∫e dt+kd dedtu = k_p\,e + k_i \int e\,dt + k_d\,\frac{de}{dt}

P pulls towards the target and D damps the motion. Both give zero torque at zero error, but holding the arm up against gravity takes a steady torque. The I term builds it up from the error that's left.

Windup

The output is clamped to ±\pmlimit. While it's clamped, the integral keeps growing without changing uu. That's windup: the stored-up torque later throws the arm past the target. Anti-windup by conditional integration skips integrating while the output is saturated. (Back-calculation, which bleeds the integral towards the clamped output, is the other common fix.)

# every dt seconds:
integral_new = integral + error * dt
derivative = (error - prev_error) / dt    # 0 on the first call
u = kp * error + ki * integral_new + kd * derivative
u_out = clip(u, -limit, limit)
if u_out == u:                            # not saturated
    integral = integral_new
prev_error = error
return u_out

The program

Gravity isn't compensated, and the base, elbow and wrist are held for you. The shoulder starts at 0.2 rad with a target of 0.8 rad; at t = 2 s the hand is pushed down with 20 N for 0.1 s. Your PID runs at 500 Hz with kp = 175, ki = 350, kd = 9 and a 12 N·m limit. Leave pid_response under your class: the grader tests PID through it.

See it in context

Pick & Place's first step, Joints, targets & PD control, explains PD control, but there MuJoCo's position servos run it for you.

Your task

Complete the class PID: set up its memory in __init__, and make update(error, dt) return the clamped output, with anti-windup. The grader feeds it fixed error sequences, one with a long saturation. Live, the shoulder must settle within ±0.5° by 1.5 s with under 10 % overshoot, and be back within ±0.5° less than 0.5 s after the push.

Goals

  • Program runs without errors
  • update() returns kp·e + ki·∫e dt + kd·de/dt, clamped to ±limit (5 fixed error sequences)
  • Anti-windup: after 3 s pinned at +limit, the output turns negative within 0.05 s of the error doing so
  • After the step, the shoulder settles within ±0.5° of 0.8 rad by t = 1.5 s
  • The step overshoots by less than 10 % (of the 0.6 rad step)
  • After the 20 N push at t = 2 s, the shoulder is back within ±0.5° within 0.5 s
Loading physics…
Loading 3D view…
0.0 / 0.0 s

Python is loading. It takes a few seconds the first time.