Advanced Metal Research
GitHub Contact AMR

Weld program and .weldplan container

On this page
  1. The weld-program.v1 schema
  2. Conventions
  3. Curves
  4. Arc length
  5. Scalar functions
  6. Seams
  7. The seam frame
  8. Work and travel angles
  9. Orientation bands
  10. Weld joints
  11. The .weldplan container
  12. Manifest
  13. Checks when packing
  14. Checks when opening
  15. Seam worker protocol
  16. Related pages

A weld is described in three layers:

  1. The seam model: how one weld's line and torch angles are written. Weld nodes in a robot.v4.program.v2 document use it for their geometry when kind is seam.
  2. The .weldplan container: the program plus everything a planner needs to plan it, packed into one file with digests. This is what the weld planner accepts.
  3. The seam worker protocol: the JSON operations that detect seams in a STEP file, search torch angles and build the container.

A weld program says what weld is required, never how a robot gets there. It holds no joint values, no poses the robot must reach and no robot state. Rotation of the torch about its own electrode axis is left free by default, for the planner to use.

The weld-program.v1 schema#

weld_planner/v1/schema/amr-weld-planner-v1.weld-program.v1.schema.json is the original standalone weld program schema. It is now the source of the seam vocabulary, not a format the planner reads:

  • program.v2 copies its definitions of identifier, vec3, unit_vec3, scalar_function, analytic, curve_segment, curve, orientation_limit_axis, seam, weld_joint, groove_face, weave, source and workpiece. Tests keep the copies identical.
  • Two differences, both deliberate: in program.v2 a seam has no process block (travel speed, standoff and weave are fields of the weld node), and weld_joints sits inside workpiece rather than at the top level.
  • A .weldplan whose program.json is a weld-program.v1 document is refused. It has no weld nodes, so it would otherwise open and plan nothing.

Conventions#

  • Units are in field names: _m, _mm, _deg, _rad, _mm_s, _l_min. Geometry is in metres, process quantities in millimetres, angles authored by people in degrees.
  • Workpiece frame only. All seam geometry is in the frame named by workpiece.frame_id, which must be workpiece in a program with welds. The program's placement says where that frame sits on the cell.
  • No baked sampling. Anything that varies along a seam is a function of normalised arc length, never a per-sample array. The planner chooses the sampling.
  • Identifiers match ^[A-Za-z_][A-Za-z0-9_:.-]*$.

Curves#

Every curve is a list of clamped, optionally rational B-spline segments joined end to end.

FieldTypeRequiredDescription
segments[]arrayyesThe segments, in order
closedbooleannoThe last segment's end meets the first segment's start
length_mnumber, mnoCached total length. Derived and not authoritative.

Each segment:

FieldTypeRequiredDescription
degreeinteger, 1–7yes1 with two control points is a line; 2 rational is an exact arc or conic
control_points_xyz_marray of 3-vectors, myesAt least two
knots_uarray of numbersyesNon-decreasing and clamped. Length is control points + degree + 1.
weightsarray of numbers > 0noOmit for a non-rational spline. One per control point.
continuity_to_nextstringnoC0, G1, C1 or C2. Advisory: C0 marks a corner.
analyticobjectnoThe exact line, circle or arc, for readability. The B-spline wins if they disagree.
length_mnumber, mnoCached, derived
id, source_edge_idstringnoIdentity and the CAD edge it came from

A line is degree 1, two control points, knots_u [0, 0, 1, 1]. A quarter arc is degree 2, three control points, weights [1, 0.7071, 1], knots_u [0, 0, 0, 1, 1, 1].

Arc length#

The B-spline parameter u is not a distance. Everything along a seam is defined on normalised arc length s in [0, 1] over the whole curve: s = 0.5 is halfway along the weld, whatever the segments look like. Travel speed is mm/s of arc length.

Scalar functions#

A quantity that varies along the seam, such as an angle or a speed, is one of:

kindFields
constantvalue
piecewise_linearknots: [s, value] pairs, s strictly increasing from 0.0 to 1.0
bsplinedegree (≥ 1), knots_u, control_values
sampless and values (at least two each), optional interpolation: linear (default), cubic or step

Seams#

A seam is one pass of one weld.

FieldTypeRequiredDescription
ididentifieryesUnique
referenceobjectyesWhere the 0° work-angle direction comes from; see The seam frame
orientationobjectyeswork_angle_deg, travel_angle_deg, optional spin and limits
curvecurve or referencenoThe line the electrode tip follows: inline, or the weld_line of the joint in weld_joint_ref
weld_joint_refidentifier or nullnoThe weld joint this seam is on. Null means hand-authored.
spanobjectnostart_s, end_s: weld only part of the curve. start_s > end_s wraps through 0, on a closed curve only.
directionstringnoforward or reverse along the curve
passobjectnoindex, role (single, root, fill, cap, tack), offset_in_frame_rb_m
weld_geometryobjectnoISO 2553 sizing: leg_length_mm, throat_mm, root_gap_mm, bevel_angle_deg, intermittent
leadobjectnolead_in_mm, lead_out_mm along the tangent
toleranceobjectnoposition_mm, work_angle_deg, travel_angle_deg, each > 0
sampling_hintobjectnoAdvisory only: max_chord_deviation_mm, max_segment_mm, min_segment_mm
originobjectnoWhy this seam is a separate piece: group_id, index_in_group, group_size, split_reason (none, reachability, continuity, manual), boundary_continuity
labelstringnoDisplay name

tolerance.position_mm is the tolerance of the verifier's tracking certificate for this seam. It defaults to 0.5 mm. See The tracking certificate.

The seam frame#

At each s the seam has a right-handed frame:

P(s)  point on the seam curve
t(s)  unit tangent, in the travel direction
r(s)  reference direction, orthogonalised against t
b(s)  = t × r

reference.mode says where r comes from:

modeNeedsReference direction
rail_curverail (a curve)From the seam point toward the matching point on the rail. Points match by normalised arc length, per segment when the two curves have the same number of segments.
fixed_vectorfixed_direction_xyzA fixed direction, orthogonalised against the tangent
rotation_minimizingseed_direction_xyzA seed direction carried along the curve without twisting

reference.semantics records how the reference was made: bisector, member_face (with member_ref), gravity_projected or custom.

Work and travel angles#

r' = R(t,  work_angle)  · r       roll in the plane across the seam
b' = t × r'
u  = R(b', −travel_angle) · r'    tilt along travel

u points from the weld point toward the torch body. The electrode axis is −u. R is a right-handed rotation.

  • Work angle > 0 rotates r toward b. 0° puts the torch on the reference direction.
  • Travel angle > 0 is drag (backhand): the torch leans back over the finished weld.
  • Spin, the rotation about the electrode axis, is free by default. preferred and locked take an angle_deg function and a reference direction.

Orientation bands#

orientation.limits.work_angle_deg and orientation.limits.travel_angle_deg bound how far a planner may move each angle. A profile outside its band is a different weld, so a consumer must refuse it rather than clamp it.

FieldTypeDescription
min, maxscalar function, degWhere the angle may be at all, as a function of s
max_deviation_degnumber ≥ 0, degHow far the angle may move from the authored profile, either way
max_deviation_plus_deg, max_deviation_minus_degnumber ≥ 0, degThe same, split by direction
max_rate_deg_per_mmnumber ≥ 0, deg/mmThe largest rate of change per millimetre of seam arc

The shipped weld preset gmaw_steel_fillet ("GMAW · mild steel · 6 mm fillet") authors 0° work and 12° travel, with bands of −15° to 15° work and 0° to 20° travel, each at most 0.5°/mm.

Weld joints#

A weld joint is an objective fact from the CAD: where two parts meet. Seams refer to joints by weld_joint_ref. In program.v2 the list is workpiece.weld_joints.

FieldTypeRequiredDescription
ididentifieryesUnique
typestringyesbutt, tee, lap, corner, edge, cruciform, plug or unknown
members[]arrayyesAt least two: solid_id, optional contact face_ids and role (base, branch, unspecified)
weld_linecurveyesThe line where the parts meet
weld_symbolstringnoISO 2553, for example fillet or v_groove
contactobjectnokind (coincident, gap, overlap, interference), gap_mm, overlap_area_mm2, max_face_deviation_mm
groove_faces[]arraynoPer weld-line segment, the two exposed faces: solid_id, face_id, surface_type, outward_normal_xyz
dihedral_angle_degscalar function, degnoThe angle between the groove faces, through the open side
bisector_railobjectnoA rail along the dihedral bisector: curve, offset_m, continuous, approximate, max_deviation_mm, within_tolerance
accessibilityobjectnoWhich stretches no torch direction can reach, found by casting rays: blocked_spans, reachable_runs, open_fraction, suggested_start_s and how they were measured
accessible_sides, confidence, evidence, labelnoAdvisory data from detection

The .weldplan container#

A .weldplan is a zip file that carries one plan request. It is built by one implementation, weldplan/plan_request.py, through the seam worker's build_plan_request operation, so the bytes are reproducible: the same inputs give the same bytes and the same digests.

MemberRequiredContents
mimetypeyesFirst, stored uncompressed: a fixed media type string, so the format can be identified without parsing JSON
manifest.jsonyesWhat is inside, with digests; see below
program.jsonyesThe robot.v4.program.v2 document, including its placement
cell.jsonyesThe cell descriptor: joints, axes, limits, frames. Mesh references are removed and meshes_omitted is true; the planner uses its own copy of the robot's meshes.
tooling.jsonwhen the program names a toolThe fitted torch
source/<name>.stepwhen the program has weldsThe workpiece geometry, as bytes
fixtures.jsonnoWorkcell bodies in the cell's world frame. Absent means the cell's own placeholder stands; an empty list means there are none.
context/…noThe planning context OLP compiled: the corrected URDF, the merged planning numbers, the sphere model and the meshes. The planner reads the robot from here rather than from its own disk.

JSON members are written with sorted keys and 2-space indentation, and every zip entry carries the fixed timestamp 1980-01-01 00:00:00.

Manifest#

FieldDescription
schemaamr-weld-planner-v1.plan-request.v1
request_id, label, created_utcRequest identity
app{module: "amr-weld-planner/v1", ui_version}
program{path, program_id, schema, node_count, weld_count}
cell{path: "cell.json", id}
planning_context{root: "context/", files: [...]}, when a context is packed
source{kind: "none"}, or {kind: "step", path, filename, sha256, bytes}
fixtures{path: "fixtures.json", count} or null
planner_owns[]Sentences naming what the request leaves to the planner: positioner joint values, the cell's collision meshes, and the torch orientation within each seam's bands
contents[]{path, sha256, bytes} for every member except mimetype and manifest.json

Checks when packing#

Packing refuses the request, with a sentence per problem, when:

  • the program fails its own program.v2 validation
  • the program has welds and workpiece.frame_id is not workpiece
  • a weld is still a hand-placed manual_pose_wp. OLP lowers those to seams before packing.
  • there is no placement, or its anchor_xyz_m, offset_xyz_m or rpy_rad is not three finite numbers, or it names no cell_id
  • the placement's cell_id, or the program's robot.model_id, differs from the packed cell's id
  • the cell has no work frame
  • the program has welds and no STEP was given
  • the program names a tool and no tooling was given, or pins one whose digest differs

Packing also stamps two pins into program.json. A tool reference gets the SHA-256 of the packed tooling.json. A robot pin of all zeros takes the packed robot description's identity. A robot pin that names a different description is refused: the description changed since the program was written, so review and accept the new robot settings in OLP, then plan again.

Checks when opening#

The planner refuses a .weldplan unless:

  • it is a zip file whose mimetype entry is the expected one
  • manifest.json exists with the expected schema
  • every member in contents exists and matches its SHA-256
  • program.json is robot.v4.program.v2
  • the program's robot.robot_description_sha256 equals the packed cell's
  • a program tool has a tooling.json whose SHA-256 matches its pin
  • every declared planning-context file exists

The planner's answer names the request by the SHA-256 of the whole file. That digest becomes the plan's program_digest and the root of its plan_id.

Seam worker protocol#

The seam worker is python -m seam_worker.workers --stdin, run in weld_planner/v1. It reads one JSON object from stdin and writes one JSON object to stdout, then exits. OLP starts one per request and forwards the operations through POST /api/offline-programming/v1/seam.

cd weld_planner/v1
echo '{"operation": "sample_seam_frames", "curve": {"segments": [{"degree": 1,
  "control_points_xyz_m": [[0,0,0],[0.1,0,0]], "knots_u": [0,0,1,1]}]},
  "reference": {"mode": "fixed_vector", "fixed_direction_xyz": [0,0,1]}, "samples": 3}' \
  | pixi run -e default python -m seam_worker.workers --stdin
operationRequest fieldsResponse
detect_jointsstep_base64; optional topology, rail_offset_m, intersector, seam_options (min_length_m, max_corner_angle_deg){topology, joints}: the B-rep topology, and the weld joints annotated with accessibility, plus one default seam per reachable run
check_torch_fitsstep_base64, joint, seam, torch; optional options{clearance}: how much of the seam the torch barrel can reach
plan_torch_pathstep_base64, joint, seam, torch, default_standoff_mm, search (work_deviation_deg, travel_deviation_deg; optional bin sizes, knot_spacing_mm, sample_step_mm, ray_count, standoff_cost_deg_per_mm); optional direction (forward or reverse), intersector{direction, plan, rays_cast, samples, warnings}: the work and travel profile that welds the most arc, closest to what was authored
sample_seam_framescurve, reference; optional samples, max_chord_m, max_angle_rad{frames}: the {P, t, r, b} frame at each sample
build_plan_requestprogram, planning_context ({files: {path: base64}}), and step_base64 or workpiece_absent: true; optional step_filename, tooling, fixtures, request_id, label, created_utc{weldplan_base64, bytes, sha256}

plan_torch_path searches one direction per call. A reverse search answers with the reversed weld in the welder's own terms, and sets direction_changed in the plan.

A failure is written as {"error": {"code": "…", "detail": "…"}}. The worker's own codes are invalid_request (exit status 2) and worker_failed (exit status 1). OLP adds payload_too_large, busy, timeout, canceled, unavailable and invalid_response; see the OLP seam route.