Home > Reflections | โฎ๏ธ โญ๏ธ

2024-05-19 | ๐Ÿ•น๏ธ PID ๐Ÿซ› Pod โž• Plus ๐ŸชžโŒจ๏ธ

reflections-2024-05-19

๐ŸŽ๏ธ 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.