0.1.3 • Published 6 years ago

yogastudio v0.1.3

Weekly downloads
9
License
MIT
Repository
github
Last release
6 years ago

Getting started

npm install -g yogastudio

cd ~/sw # or whever you want to work from
git clone git@github.com:tlbtlbtlb/ys-example
cd ys-example
npm install
npm test
yogac  # compile the example
yogastudio  # Start the studio UI

Yoga Language

Yoga is a purely functional language for real-time control. Yoga programs are differentiable with respect to their parameters, which means that you can adjust the output of a program in a web-based UI and it will adjust the parameters to achieve that desired output. Yoga programs can also be visualized with parameters replaced by UI controls that allow fast, interactive parameter tuning.

The syntax of Yoga is close enough to JavaScript that you can use JavaScript syntax highlighting in your editor.

Assignments and binding have the same syntax A = B, but different semantics. The = operator creates a binding when its LHS is a not-previously-defined name. When the LHS is an output parameter name or structure reference, it's an assignment

For update parameters, assignments write to a different place than references to the same name. For example,

  struct Foo {
    R bar;
  }

  function incrBar( | Foo s | ) {
    s.bar = s.bar + 1;
  }

returns a new TestStruct with a higher bar. A traditional functional language syntax would look like:

    sNext = Foo{bar: sPrev.bar+1};

which becomes cumbersome as Foo becomes large. Large structs seem common in robotics applications.

Update parameters allow convenient calling of stateful functions like filters. For example,

    struct JointState { ... }

    function jointCtl(JointCmd cmd | JointState s | JointSensors sens, JointGoal goal, JointConfig c ) {
      cmd.torque = lpfilter2(s.torqueFilter, c.torqueFilterPeriod, c.torqueFilterQ, sens.pos - goal.pos);
    }

The call to lpfilter2 materializes the state variables necessary for a 2nd-order low-pass filter under s.torqueFilter, and updates them at every iteration. Otherwise we'd need something like:

    function jointCtl(JointState sPrev, JointSensors sens, JointGoal goal, JointConfig c ) {
      let torque, nextTorqueFilter = lpfilter2(sPrev.torqueFilter, c.torqueFilterPeriod, c.torqueFilterQ, sens.pos - goal.pos);
      return JointCmd{torque: torque}, JointState{torqueFilter: nextTorqueFilter}
    }

which gets quite hairy when filters are embedded inside deeply nested functions.

Types

Every type `T` is equipped with several functions that allow them to be used like vectors in a linear algebra:

- `T operator *(R aCoeff, T a)`: scalar multiplication
- `T linearComb(R aCoeff, T a, R bCoeff, T B)`: computes `aCoeff*a + bCoeff*b`
- `R linearMetric(T a, T b)`: computes a dot product

Operator reference

 () [] -> .                      left to right
 ! -                             right to left
 ^ (exponentiation)              left to right
 | (pipe)                        left to right
 * / %                           left to right
 + -                             left to right
 < <= > >=                       left to right
 == !=                           left to right
 &&                              left to right
 ||                              left to right
 ?:                              right to left
 = += -= etc.                    right to left

Function reference

sin, cos, tan sec, csc, cot sinh, cosh, tanh min, max abs, sign exp, log lim(x, lo, hi) vis(x, ...) normsq(x), normsq(v), normsq(m)

Real-Time Networking

Yoga programs can control hardware in real time by talking over the network. In the Dexter4 system the following components all talk over Ethernet:

  • a host processor (an Intel NUC)
  • 6 peripheral processors (AVR32 microcontrollers)
  • 2+ high speed cameras

JsonNetworkEngine

A JsonNetworkEngine communicates to another process over JSON-RPC, where each line is a JSON message. It's mostly used through the ChildPipe subclass, which forks and execs a process and connects to the child process's stdin and stdout. (Using stdin and stdout allows using ssh to run the process remotely.)

With some batching and explicit time synchronization, JsonNetworkEngines can handle 300 FPS video feeds (only the metadata is sent, the compressed video data is written to a local disk in real time and uploaded afterwards.)

Look at timeseq_video.js for an example. It creates RemoteCameraEngines (a subclass of JsonNetworkEngine) that sshs to a video server, and receives over the pipe a tuple for each frame with (timestamp, filename, offset within file) that allow quick random access to video frames.

SscNetworkEngine

An SscNetworkEngine communicates to a hardware interface over UDP. There are drivers for AVR32 microcontrollers that you can incorporate in your own hardware. We routinely communicate with 5 devices at a time, each running at 1000 Hz update rate.

The protocol over UDP is a simple binary packet format, usually with single-byte type IDs followed by fixed-length binary representations of the data. Currently, floating-point data isn't used over the wire. Timestamps are 64-bit values representing clock ticks on the microcontroller. Values that are floating-point on the host are usually fixed-point on the microcontroller, such as 8.24 (8 bits whole part, 24 bits fractional part, negative numbers use 2s complement). There are routines in ssc_io.h to read and write fixed-point formats.

References