Skip to content

Print Loop — Process Flow

The WAAM print loop is a closed-loop cycle that prints a layer of metal, measures the resulting surface error, and feeds that measurement back into the toolpath for the next layer. Each iteration drives the surface height closer to its nominal value.

┌──────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 1. EXECUTE G-CODE + RECORD ROSBAG │ │
│ │ gcode_file_parser_client │ │
│ │ ros2 bag record … │ │
│ └──────────────────┬──────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 2. MAP SURFACE-Z ERROR │ │
│ │ rosbag_parser │ │
│ │ → plot.html + error_map.csv │ │
│ └──────────────────┬──────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 3. PROPORTIONAL CONTROL CORRECTION │ │
│ │ simple_proportional_control │ │
│ │ → corrected_layer.gcode │ │
│ └──────────────────┬──────────────────────────┘ │
│ │ │
│ └───────────────────────────┘ │
│ loop back to step 1 │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 4. AGGREGATE ERROR STACKUP (post-hoc, any time) │
│ aggregate_error_stackup │
│ → stackup.html + stackup_summary.csv │
│ Reads all L*.csv from state_dir │
│ Confirms controller convergence over layers │
└──────────────────────────────────────────────────────┘

Using Haskell-style notation to express what each stage consumes and produces:

-- Step 1: execute toolpath, observe physical result
gcode_file_parser_client :: GCode -> IO PhysicalDeposition
-- (concurrently)
ros2_bag_record :: [Topic] -> IO BagDirectory
-- Step 2: associate error with robot XYZ
rosbag_parser :: BagDirectory -> GCode -> (HTML, CSV)
-- CSV schema: X, Y, Z, err_mm
-- Step 3: correct next layer feedrates
simple_proportional :: GCode_next -> CSV_err -> K_p -> GCode_corrected
-- F_corrected = F_nominal * (1 + K_p * z_err_mm)
-- Step 4: aggregate multi-layer error for convergence analysis
aggregate_error_stackup :: StateDir -> (AggregateHTML, SummaryCSV)
-- Reads all L{N}.csv, produces 3D stackup + error trend chart

Each layer print records the following topics into an MCAP bag:

TopicPurpose
/TSO_1/image_raw/compressedThermal stickout camera 1
/TSO_0/image_raw/compressedThermal stickout camera 0
/TSO_2/image_raw/compressedThermal stickout camera 2
/cams/table_1/image_raw/compressedTable-mounted camera 1
/cams/table_0/image_raw/compressedTable-mounted camera 0
/kuka/poseRobot TCP pose (PoseStamped)
/wire_stickout/err_surface_mmSurface height error (Float64)
/kuka/velocity_cartesianCartesian velocity
/therm/image_raw/compressedThermal camera
/kuka/sequence_numberMotion sequence counter
Terminal window
# start recording (in one terminal)
ros2 bag record -o L<N>_Z<ZZ> \
--topics \
/TSO_1/image_raw/compressed \
/TSO_0/image_raw/compressed \
/TSO_2/image_raw/compressed \
/cams/table_1/image_raw/compressed \
/cams/table_0/image_raw/compressed \
/kuka/pose \
/wire_stickout/err_surface_mm \
/kuka/velocity_cartesian \
/therm/image_raw/compressed \
/kuka/sequence_number
# execute gcode (in another terminal)
ros2 run gcode_file_parser_client gcode_file_parser_client <layer.gcode>
Terminal window
uv run python map_surface_err_to_xyz_pos.py \
<bag_directory>/ \
<layer.gcode> \
--nominal_z <ZZ> \
--no-gcode \
-o <STATE/ERR/L<N>.html> \
--smooth 12 \
--trim_first_n_err_vals 32 \
--trim_last_n_err_vals 32
Terminal window
uv run python simple_proportional.py \
<next_layer.gcode> \
<L<N>_error_map.csv> \
-k <K_p> \
-o <next_layer_corrected.gcode>

Then go back to Step 1 with the corrected G-code.

Step 4 — Aggregate Error Stackup (post-hoc)

Section titled “Step 4 — Aggregate Error Stackup (post-hoc)”
Terminal window
# from ros2_ws/lib/control/aggregate_error_stackup/
uv run python aggregate_error_stackup.py <STATE/ERR/job_dir/>

Run at any time during or after a print job. Reads all L*.csv in the state directory and produces stackup.html (3D stackup + error trend) and stackup_summary.csv (per-layer error statistics). Serve for remote viewing with python3 -m http.server 8080.

Instead of running each step manually, the print-loop orchestrator drives the entire cycle from a single YAML config file:

Terminal window
# from ros2_ws/lib/control/print_loop/
uv run python print_loop.py my_job.yaml

The orchestrator:

  1. Resolves all gcode file paths from the slices_dir automatically.
  2. Starts/stops bag recording and gcode execution as subprocesses.
  3. Runs map_surface_err_to_xyz_pos.py and simple_proportional.py.
  4. Opens the error-map HTML for visual inspection between layers.
  5. Waits for operator confirmation before printing the next layer.
  6. Saves resumable state to {state_dir}/{job_name}_state.json.

Use --dry-run to verify path resolution without executing anything:

Terminal window
uv run python print_loop.py my_job.yaml --dry-run

See example_config.yaml in ros2_ws/lib/control/print_loop/ for a template configuration.


See the sub-pages for detailed descriptions of each step.