Skip to content

Latest commit

 

History

History
404 lines (317 loc) · 17.7 KB

Development.md

File metadata and controls

404 lines (317 loc) · 17.7 KB

Development

If you want to get involved in the development and contribute, this page is for you.

Mailing List

Please subscribe to the mailing list

Compilation

Due to the speed of the compiler on the BeagleBone, you might want to do development on other machines for faster turn-around times (unless you really need to actually access the hardware GPIOs or run PRU code, this works just fine. With -n, you can just simulate).

On a non-Beaglebone machine, pass an empty ARM_COMPILE_FLAGS environment variable:

 ARM_COMPILE_FLAGS="" make
 # or export to environment.

To speed up development (on all machines, but in particular if you compile on the BeagleBone), it is definitely useful to have ccache installed (package-installing is not enough, read the manpage to make sure to properly enable it). It makes make clean ; make cycles much faster.

Tests

When doing development and adding new features, is advisable to run the tests (and write your own). To avoid re-inventing the testing-framework wheel, we use the Google test framework, for which there are typically already packages available:

 sudo apt-get install pkg-config libgtest-dev libgmock-dev
 make test
 # Or, for more thorough memory-leak or initialization issue check:
 make valgrind-test

NixOS

If you develop on nixos or have the nix package manager installed, use the shell.nix to set-up your development environment by calling nix-shell in the root of the environment.

nix-shell

Coverage

To see if there is code that has not been covered in tests yet, there is a target make coverage, that creates a src/coverage.html report with links to individual files that show which lines have been visited.

You also need to have gcovr installed. Run this in the src/ directory.

 sudo apt-get install gcovr
 make coverage

Note as always with coverage: if everything is green, it doesn't mean that the code is sufficiently covered as it only reports if a line has been executed, not if you have tested all the paths that you can reach a particular line. But if a line is red it definitely means that none of the unit tests even touched that piece of code.

gcode2ps

Manual inspection is also useful. A little tool to visually inspect the planner output is src/gcode2ps. It is a tool that reads gcode and outputs the raw gcode path and the actual machine path with speed annotations. Generates a printable PostScript file.

$ src/gcode2ps
Utility to visualize the GCode path and the resulting machine
movement with speed colorization, dependent on configuration.
(Without config, only GCode path is shown)

Usage: ./gcode2ps [options] <gcode-file>
Options:
        -o <output-file>  : Name of output file; stdout default.
        -c <config>       : BeagleG machine config.
        -T <tool-diameter>: Tool diameter in mm.
        -t <threshold-angle> : Threshold angle for accleration opt.
        -q                : Quiet.
        -s                : Visualize movement speeds.
        -D                : Don't show dimensions.
        -M                : Don't show machine path, only GCode path.
        -i                : Toggle show IJK control lines.
        -l                : [EXPERIMENTAL]: visualize laser intensity
[---- Visualization ---- ]
        -w<width>         : Width in point (no unit) or mm (if appended)
        -e<distance>      : Eye distance in mm to show perspective.
        -a<frames>        : animation: create these number of frames showing rotation around vertical.
        -g<grid>          : Show grid on XY plane. Optional with 'in' unit suffix (default: mm).
[---- Rotation. Multiple can be applied in sequence ----]
        -R<roll>          : Roll: Rotate around axis pointing towards and through canvas
        -P<roll>          : Pitch: Rotate around horizontal axis.
        -Y<roll>          : Yaw: Rotate around vertical axis.
        -V<view>          : Shortcut: view. One of {front, isometric, left, right, top}

With the -s option, it is possible to visualize the speed, so you can see where the tool-path is accelerated and decelerated (this is directly taken out of the planner output, so it is exactly what BeagleG would do with a real amchine. This is why it is invaluable as debugging tool).

For easier handling (or sending examples to the mailing list), you might want to convert the result into a PNG image. The tool prints out how you'd call ghostscript to do the conversion:

$ src/gcode2ps -w800 -o /tmp/hello.ps -c machine.config -s somegcode.gcode
-- Convert to image /tmp/hello.ps.png with --
gs -q -dBATCH -dNOPAUSE -sDEVICE=png16m -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dEPSCrop -sOutputFile=/tmp/hello.ps.png /tmp/hello.ps

Depending on the G20 or G21 setting in the gcode file, the measurement units are displayed in imperial or metric. The -g option allows to underlay a grid in the X/Y plane, the second example uses -g 10mm to show a grid with an 10mm raster.

If you don't give a machine-configuration (-c) or give the -M option, only the gcode path is displayed. Rapid moves (G0) are light-gray, regular moves (G1) black. Helper lines such as the radius indication of the G3 call or the spline control-point vectors in the second example are shown in dotted lightblue (you can switch that off with -i).

3D projections

By default, the output shows a top view of the X/Y axis, but you can choose a view with -V or choose rotation. In this example, the same 3D path shown with -Vtop (which is also the default) vs. -Visometric (you can abbreviate these views, e.g. -Vt and -Viso would work as well).

The default view is orthogonal, but with -e<eye-distance>, you can add perspective. Note: the 3D projection is calculated in the PostScript itself, so you can edit the file and modify the projection angles there.

Animation

As a, wait for it, silly twist, you can also generate animations; enable with the -a option. Here -e150 -a36, generating 36 frames.

Laser visualization

For laser cutters, the spindle speed is usually translated to the power of the laser. With the -l option, the spindle speed is translated to the brightness of the GCode path (higher spindle => darker line), thus showing you an output of a laser scanning application (Note, the actual machine control does not yet implement spindle PWM change per line-segment, so it can't yet be used for laser cutters. TBD.).

Generated loops hardware counter using internal eQEP

The AM335x processor provides an Enhanced Quadrature Encoder Pulse (eQEP). This can be useful to test the correctness of the number of steps and debug possible drifts. In order to access it via the Linux file system, it's necessary to load the correct device tree overlay at boot.

For example, to enable the eQEP0, it's necessary to add a line inside /boot/uEnv.txt:

cape_enable=bone_capemgr.enable_partno=bone_eqep0

and reboot.

After reboot, /sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/ should be populated with some files:

driver  enabled  modalias  mode  period  position  power  subsystem  uevent

For eQEP0 P9_42 and P9_27 are respectively eQEP0A and eQEP0B. In order to test a specific axis, opportunely connect the direction output on P9_42 and the step output on P9_27.

Run echo 0 > /sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/position to reset the counter to zero and start some motion. You will see the value inside the position file change accordingly to the number of LOOPS generated from the PRU.

For development testing

This tool is used for the visual end2end output creating a HTML page with images of a set of testing *.gcode files:

make -C src test-html

Overview: processing pipeline

The processing is event driven: The incoming GCode gets fed through the GCodeParser which then pipes the events to the GCodeMachineControl. Movement commands are pipelined and queued as they need to be buffered to do path and accleration planning. This also prevents machine stalls while waiting on GCode input.

This is a overview of the pipeline components:

Processing

Data flow - layers of processing

The above pictures gives a general idea of the flow of information.

Typically, a higher-level component creates a sequence of operations and calls some Enqueue() method on a lower level component. The decomposing into each of these components allows for easy testing and additional buffering.

GCodeParser

Parses G-Codes and emits new target coordinates to move to. Commands that are not a simple straight line (Arcs and Bezier curves) are converted into smaller movements.

Output are absolute realworld coordinates for logical axes (e.g. X:100mm) and the desired speed to travel to it. They are emitted as callback; in our case they are calling GCodeMachineControl which passes it straight to the planner.

Type: {AxisRegister abs_pos, float speed}

Planner

Converts the desired movement and its speed into motor movement commands that obey the machine constraints.

For a straight move, might emit a single output or create an acceleration and a deceleration segment. The Planner might need to buffer segments before it can emit them because it needs some look-ahead to know when to start decelerating a bunch of smaller movements to come to a stop in time.

Output is relative motor steps for each of the motors (e.g. Motor2:+322 steps), and the start- and end-speed of the axis that travels most of the steps (called defining axis in BeagleG lingo) - all the other axes are scaled linearly over the move: this results in the linear coordinated move.

Type: {int motor_steps[], float start_steps_per_sec, float end_steps_per_sec}

MotorOperations

Queue that takes the linear segment requests and passes them to a hardware backend generating the steps.

Also provides a way to query where exactly the motor position is at any given time to be able to be ready for e-stop/pause/resume operations.

MotionQueueMotorOperations

This implementation of MotorOperations is an intermediate that converts line segements into parameters used by the current hardware implementations to generate steps. Passes this on to MotionQueue.

TODO: Naming. Since the MotionSegment struct is very specific to the step delay calcualations via a Taylor series, this should be better reflected. There will be other sample-based backends implemented in an FPGA, that use entirely different parameters.

Might need to break up longer segments into multiple as each can be at most 64k steps (due to the limits imposed by the fixed-point integer arithmetic). Prepares parameters needed for the hardware to calculate the timings between steps using a Taylor-series.

Output: various parameters in a packed struct to be enqueued to the hardware (Type: struct MotionSegment); the receiving type is a MotionQueue.

MotionQueue

This receives the hardware parameters from the MotorQueueMotorOperations and executes it in some micro-controller or FPGA; in this first implementation, this is the PRU in the BeagleBone. The PRUMotionQueue is the object on the host side communicating via a RingBuffer (residing in PRU SRAM); the PRU side is a fairly simple program implemented in assembly. This can be various implementations.

There is also a motion queue implementation that simulates the firmware and outputs data for graphical inspection (SimFirmwareQueue).

APIs

The functionality is implemented in a stack of independently usable APIs. Each part of the stack has some well-defined task, and possibly delegates action levels down.

  • gcode-parser.h : C++-API for parsing G-Code that calls callback parse events, while taking care of many internals, e.g. interpreting slightly different dialects and automatically translates everything into metric, absolute coordinates for ease of downstream receivers. This API in itself is independent of the rest, so it might be useful in other contexts as well.

  • gcode-machine-control.h : highlevel C++-API to control a machine via G-Code: it receives G-Code events and provides the services actually needed to control a machine - from dealing with homing, showing the current position or issue machine moves. The machine moves are delegated to the planner for further processing. Depends on the gcode-parser APIs as input and motor-operations as output. Provides the functionality provided by the machine-control binary. If you want, you can use this API to programmatically control your machine without the detour through GCode.

  • planner.h : From a sequence of requested target positions with a desired speed of segments, plans actually pysically possible moves with speed/accleration segment joining, maps the axis moves to step count for motors and emits the resuling number of steps with start- and end-speed to MotorOperations.

  • motor-operations.h : Low-level motor motion C++-API. Receives travel speeds and speed transitions from the planner. This is a good place to implement stepmotor driver backends. There are various implementations here for visualization, and one (MotionQueueMotorOperations) that is preparing the output for the next lower hardware level: the MotionQueue. For that, it prepares parameters for the discrete approximation in the PRU motion-queue backend.

  • motion-queue.h : Even lower level interface: queue between motor-operations and hardware creating motion profiles in realtime. The implementation is done in the BeagleBone-PRU, but is separated out enough that it is not dependent on it: The required operations could be implemented in microcontrollers or FPGAs (32 bit operations help...). There is a simulation implementation (sim-firmware.cc) that illustrates what to do with the parameters. The simulation just outputs the would-be result as CSV file (good for debugging and visualization with gnuplot).

  • determine-print-stats.h: Highlevel API facade to determine some basic stats about a G-Code file; it processes the entire file and determines estimated print time, filament used etc. Since this takes the actual travel planning into account, the values are a correct prediction of the actual print or CNC time. Implements a GCodeParser::EventReceiver and a MotorOperations interface to get all the necessary information to calculate the needed time.

  • gcode2ps is a utility that outputs gcode and resulting motor-movements as PostScript image to help visualize in development.

The interfaces are C++ objects.

API show case: GCode stats printing

Due to the separation of the various components, it is possible to create exact predition of the machine times with the gcode-print-stats binary. The stacking is a bit differently, as it replaces the MotorOperations with an implementation that just adds up how long motors would move.

Since this goes through the same motion planning and takes current speed and acceleration into account, it can predict the runtime of a job down to the second.

It only takes a couple of seconds to simulate and pre-calculate the wall-time of many-hour long jobs. This might be useful to display to the user.

gcode-print-stats

Roadmap

Not everything I'd like to have is implemented yet, but getting closer as weekend hacking permits.

  • Shoveller: Assuming an emergency stop and no steps were missed, restart the gcode from the last position.
  • Implement the code for soft pause/resume.
  • Planner: make sure to slow down early enough to obey machine constraints (it does not yet for tiny segments at the end of movement)
  • High performance/light Status Server
  • Change the code to be compatible with remoteproc new kernel feature.
  • Include some tests for the pru code (maybe by using a PRU virtualization)
  • Needed for full 3D printer solution: add PWM/PID-loop for heaters.
  • Motion: consider limited constant jerk movements.

Random thought collction for roadmap points above

Shoveller

  • Initial impementation: assume hard stop (e.g. emergency stop) and be able to revover the state back from the PRU to be put back into the linear segment steps queue. Goal: be able to restart the operation after completely empty PRU state.
  • Note, in this first implementation we just assume hard stops and starts and that steppers don't use steps.
  • We might use that later to be able to do other operations 'in-between' after an emergency stop (e.g. manually jogging away the tool, then restart will move it back to where we stopped.

Planner

  • right now, we just do proper 'ramp up' of acceleration, but not deceleration. In order to do that, we need to delay emitting LinearSegmentSteps until we know that we can stop within the deceleration constraints. Otherwise we need to edit the speeds backwards.

Status Server

  • Issue #38 has some good discussion.