Live data from Hacker News

Show HN: SpaceX Dragon simulator docking autopilot in Clojure

github.com

21–30 of 38 posts

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#21

https://www.reddit.com/r/spacex/comments/gigmfh/spacex_iss_d... does it in one line but it's kind of cheating.

Very simple P-controller based autopilot derived from the one-liner:

  let a = setInterval(() => { 
      // retrieve values from simulation
      let roll = fixedRotationZ;
      let pitch = fixedRotationX;
      let yaw = fixedRotationY;
      let rpy = new THREE.Vector3(roll, pitch, yaw);
  
      let rollRate = -rateRotationZ/10.0;
      let pitchRate = -rateRotationX/10.0;
      let yawRate = -rateRotationY/10.0;
  
      let rpyRate = new THREE.Vector3(rollRate, pitchRate, yawRate);
  
      let pos = camera.position.clone();
      let posRate = motionVector.clone();
      let targetPos = issObject.position.clone();
  
      let targetRpy = new THREE.Vector3(0,0,0);
      let dRpy = targetRpy.clone().sub(rpy);
  
      // P-controller controls target roll,pitch,yaw-rate (targetRpyRate) to reach target orientation
      let targetRpyRate = new THREE.Vector3(
          dRpy.x/1.0, 
          dRpy.y/1.0, 
          dRpy.z/1.0
      );
  
      let minRpyRate = new THREE.Vector3(-1.5, -1.5, -1.5);
      let maxRpyRate = new THREE.Vector3(+1.5, +1.5, +1.5);
      targetRpyRate.clamp(minRpyRate, maxRpyRate)
  
      let dRpyRate = targetRpyRate.clone().sub(rpyRate);
   
      // don't calculate with displayed x,y,z but the internal coordinates 
      // (z-axis is forward and backward)
      let dPos = targetPos.clone().sub(pos);
      let d = dPos.length();
      // P-controller controls target motion (targetPosRate) to reach target position
      let targetPosRate = new THREE.Vector3(
          dPos.x / 100.0,
          dPos.y / 100.0,
          dPos.z / 100.0
      );
      let minPosRate = new THREE.Vector3(-0.1, -0.1, -0.01);
      let maxPosRate = new THREE.Vector3(+0.1, +0.1, +0.01);
  
      // thresholds for bang contRol
      let dRpyRateControlThreshold = new THREE.Vector3(0.0, 0.0, 0.0);
      let dPosRateControlThreshold = new THREE.Vector3(0.001, 0.001, 0.001);
  
      // define some phases where we want to have different movement behaviour
  
      if (dRpyRate.length() > 0.1)
      {
          // not correctly oriented, stop movement
          targetPosRate.x = 0;
          targetPosRate.y = 0;
          targetPosRate.z = 0;
      }
      else if (Math.abs(dPos.z)  +dRpyRateControlThreshold.x)
          rollLeft();
      if (dRpyRate.y  +dRpyRateControlThreshold.y)
          pitchUp();
      if (dRpyRate.z  +dRpyRateControlThreshold.z)
          yawLeft();
  
      // bang control to reach target motion
      if (dPosRate.x  +dPosRateControlThreshold.x)
          translateRight();
      if (dPosRate.y  +dPosRateControlThreshold.y)
          translateUp();
      if (dPosRate.z  +dPosRateControlThreshold.z)
          translateBackward();
  
  },100);

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#22
post #13
post #9

Earlier quoted context omitted.

Anything is a oneliner when you erase all the `\n`s

Python begs to differ

You can add semicolons at the end of python lines to replace \n's when calling from the command line.

  python3 -c "n=1;print(n)"

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#23
post #13

Earlier quoted context omitted.

Python begs to differ

You can add semicolons at the end of python lines to replace \n's when calling from the command line. python3 -c "n=1;print(n)"

How does it deal with code blocks normally defined by indentation? (As an aside, indentation being part of syntax is the only thing keeping me disgusted with python - I like the language otherwise.)

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#24
post #20

Not to say this is bad or incorrect, but this is very unidiomatic Clojure. I would question the amount of global state, and the broad use of loops inside threads. This can’t be fun to try and test! Perhaps better would be to make pure functions to take current telemetry as an argument and return actions as outputs, perhaps all at once or more likely individually for each control. You could then capture all of this in…

I'm a Clojure fan but I'm not sure a modeling and simulation problem such as this is the best use case for it. Sure, you can do it, but it probably wouldn't be my first choice. Last I checked, Aerospace software is still dominated by C/C++ and Ada (still alive because it found itself a niche), and there's good reasons for it. The satellite simulators I worked on had to be able to execute actual flight code from the vehicle, so you needed to get fairly low level.

Nowadays I write web applications like everyone else on the planet and use Java, JS and/or Clojure (when I can get away with it).

But cool little project, nonetheless.

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#25
post #6

I have a question about control flow of the overall system. Note that I haven't actually run this or debugged it. Or frankly, thought about it for more than a minute or two. Its more of an architectural question. So in dragon.clj there's a case statement to execute commands in the translate function such that the command to go down or left in an abstract calculated sense is implemented as hitting a virtual down or le…

Heh, never thought about it that way! But good question. Yes, it can translate on 2 axes at the same time, since the control loops for each axis run in separate threads, so there is no control locking. The same is with rotation - it can e.g. pitch, roll and yaw at the same time.

So if you click one of the rotate buttons after it has zeroed out the offsets will it correct? Is it using a PID model or something else?

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#27
post #20

Not to say this is bad or incorrect, but this is very unidiomatic Clojure. I would question the amount of global state, and the broad use of loops inside threads. This can’t be fun to try and test! Perhaps better would be to make pure functions to take current telemetry as an argument and return actions as outputs, perhaps all at once or more likely individually for each control. You could then capture all of this in…

Each time someone complains a working program is not "idiomatic" in what ever language I die a little inside.

That makes it sound you approach this from a haughty presumptuous, uncharitable angle.

It sounds like you exclude the possibility that the author did not try it "your way" first, for example, but found a simpler way to implement it.

"That's not the most obvious to do it IMO" or "I would have preferred to do it like this..." are much better ways to approach this. Programs should be beautiful and understandable, but "idiomatic" sounds it excludes lots of simple beautiful solutions to a problem out of pure dogma.

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#28
post #16

Earlier quoted context omitted.

Definitely cheating, it's not autopilot. It is just updating your co-ordinates every 500ms on a straight line. I would like to see a real autopilot written in javascript that you can paste into the console.

I wrote an autopilot[0] for the simulator in JS, which sends key events to the game. [0]: https://gist.github.com/ForestKatsch/01069f29e8316df11774025...

This is awesome! As a recent student of PIDs, it's even better after uncommenting the createPidDisplay statement on line 566 :)

Only feature request is a 'Do a Barrel Roll!' button.

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#29
post #23

Earlier quoted context omitted.

You can add semicolons at the end of python lines to replace \n's when calling from the command line. python3 -c "n=1;print(n)"

How does it deal with code blocks normally defined by indentation? (As an aside, indentation being part of syntax is the only thing keeping me disgusted with python - I like the language otherwise.)

it doesn't :) though it works fine if your terminal allows multiline input:

  python -c "
  for x in stuff:
    this(x)
    that(x)
  "
at some point i even wrote a hacky little wrapper that let you use braces and replaced¹ them with spaces+newlines, something like this:

  pyc-wrap "for x in stuff { this(x); that(x); }"
i can dig the code up if you need it :)

========

though after that i sort of went of the deep end adding perl-style "it" ($_) variables for loop variables:

  for stuff { this($); that($);
(yes, closing braces at the end were optional...)

and for the result of the previous line:

  2; $+1; hex($);
  # roughly translates to:
  x = 2; x = x+1; x = hex(x);
surprisingly pleasant to use for shell-like tasks actually!

edit: almost forgot - if during execution you used a name `foo` not found in locals/globals, `foo` was treated as an external command, and resolved to something like

  lambda *args: os.system('foo', *args)
so that you could (sorta) easily call external commands. fun times :)

---

¹ slightly more involved than a dumb code.replace('{', ':\n') because you need to maintain the indent level... but around that level of sophistication

Re: Show HN: SpaceX Dragon simulator docking autopilot in Clojure

#30
post #27
post #20

Not to say this is bad or incorrect, but this is very unidiomatic Clojure. I would question the amount of global state, and the broad use of loops inside threads. This can’t be fun to try and test! Perhaps better would be to make pure functions to take current telemetry as an argument and return actions as outputs, perhaps all at once or more likely individually for each control. You could then capture all of this in…

Each time someone complains a working program is not "idiomatic" in what ever language I die a little inside. That makes it sound you approach this from a haughty presumptuous, uncharitable angle. It sounds like you exclude the possibility that the author did not try it "your way" first, for example, but found a simpler way to implement it. "That's not the most obvious to do it IMO" or "I would have preferred to do i…

Clojure is a mostly pure functional language, it is very uncommon to have almost all logic reach out to global variables to do work. It’s also rare to see multithreaded code that eschews most of the mechanisms built into the language for multithreading. These two things together (pure functional programming and strong concurrency primitives) are arguably the fundamental motivations behind the language. You value simplicity: Clojure can help make this code simpler. If there’s a succinct way to say that without the word idiomatic, one with fewer negative connotations, I will try to use that in the future.
Post reply on HN