Home > Reflections | ⏮️ ⏭️

2024-05-19 | 🕹️ PID 🫛 Pod ➕ Plus 🪞⌨️

🏎️ Pod Racing Continued

  • Yesterday (2024-05-18 | 🕹️ PID 🫛 Pod 🏁 Racer) I tried several iterations on my intuitive algorithm.

  • 📉 It seems that every added sophistication reduced overall performance.

  • 🤔 This shouldn’t really be surprising.

  • 🧠 Optimization is often unintuitive.

  • 💡 While I can intuit and implement a strategy, it’s hard to improve the overall performance without an explicit objective function.

  • ✅ Tractable optimization problems often take the form: minimize (or maximize) a single value under some constraints.

  • ☝️ Having a single optimization variable is important.

  • 🤯 Attempting to optimize multiple values simultaneously is much more difficult.

  • 🚫 It may be fair to say that it’s often intractable or even impossible.

  • 🌱 So when we’re tempted to optimize multiple values simultaneously, it can be fruitful to pick the objective we care most about and transform the others into constraints.

  • 💰 For example, if we want to simultaneously minimize the duration and cost of a project, we might instead minimize duration under the constraint that cost stays below some threshold.

  • 🏁 In this case, I think our optimization problem is something like: minimize time to complete the race under the constraint that we pass through every way point in the correct order.

  • ❓ This sounds nice. It seems like the most direction expression of our ultimate goal. But how do we solve it?

  • 🔨 A brute force approach could be to

  • 🗺️ generate all possible strategies to complete the race

  • 🗑️ delete every strategy that doesn’t pass through all the checkpoints in the correct order

  • 🥇 pick the remaining strategy with the best time

  • 😫 This problem seems computationally intractable.

  • ❓ What even is a strategy to complete the race?

  • 🛣️ Maybe a strategy is a path around the map.

  • 🌌 Not only are there a very large number of possible paths, but we’d need to add a constraint that our pod can actually follow the path given the game’s physics engine.

  • 💡 One step we could take toward tractability could be to reduce the vast search space with some simplifying assumptions.

  • ✨ Another approach could be to focus on solving a series of local optimization problems rather than a single global optimization problem.

  • 🤖 The first intuitive algorithm I implemented is essentially a series of local optimization problems.

  • 🎯 The implicit goals are to get to the next checkpoint as quickly as possible.

  • 🗣️ I say that’s the implied goal, rather than an explicit goal, because the explicit strategy is to

  • 🧭 always aim at the next checkpoint

  • 💯 maintain 100% throttle reduced proportionally to the difference between our pod’s heading and the next checkpoint

  • ⏱️ So it’s not even explicitly optimizing for time to each checkpoint.

  • 📉 Expressed as an optimization problem, it would be more accurate to say we’re minimizing error in trajectory to the next checkpoint.

  • 🤷 Honestly, I don’t think it’s technically even an optimization problem.

  • 👍 We really just have a heuristic: reduce throttle by an arbitrarily chosen factor that’s proportional to the error in trajectory to the next checkpoint.

  • 💪 Despite the lack of rigor, this heuristic performed quite well for a while.

  • 🚀 So let’s see if we can improve the performance with the use of PID controller.

👨‍💻 Let’s start by rewriting our sample Python code into TypeScript.

type PIDParams = {  
  kp: number  
  ki: number  
  kd: number  
  measurement: number  
  setpoint: number  
  time: number  
}  
type PIDState = {  
  control: number  
  error: number  
  integral: number  
  time: number  
}  
const pid = ({ kp, ki, kd, measurement, setpoint, time }: PIDParams, s: PIDState): PIDState => {  
  const error = setpoint - measurement  
  const de = error - s.error  
  const dt = time - s.time  
  const p = kp * error  
  const i = s.integral + ki * error * dt  
  const d = kd * de / dt  
  const control = p + i + d  
  return {  
    control,  
    error,  
    integral,  
    time  
  }  
}  
  • 🚀 Now let’s apply this function in our 🏎️ pod racing game.
  • 📏 Our measurement will be the 📐 angle between our pod’s heading and the next 🏁 checkpoint.
  • 🎯 Our setpoint will be 0️⃣ zero, implying that we want our 🏎️ pod pointed at the next 🏁 checkpoint.
  • 🕹️ Our control variable will be the desired reduction in ⛽ throttle.