Mach Vision System
Introduction
IntroductionOverview
Overview
The Keyence Vision integrationsystem lets a Mach-controlled CNC router use a smart camera to find printed fiducial marks and either (a) report their offset for downstream G-code or (b) automatically align an entire program to a part by capturing a few marks and applying a modal coordinate transform.
The implementation is driver-based. VisionModule owns all calibration math, fit solving, G-code emission, and OEM-parameter wiring; individual driver classes (currently only Keyence, for VS-series smart cameracameras (such as the VS-L320MX)L320MX) to locatehandle the centerwire ofprotocol. New cameras are added by writing a printednew fiducialdriver, marknot by changing the M-code surface.
All pixel-to-machine-units math, fit solving, and reportmodal theG-code X/Y(G51 offset/ fromG68 / G52) emission live in VisionModule. Drivers are dumb — they trigger the camera centerand toreturn thepixel mark center.deviations.
Typical workflow: the machine jogs or moves under G-code so the camera is positioned within roughly two inches of the printed mark, then an M280 call triggers the camera to measure and stores the offset values in OEM parameter registers where the operator screen and downstream G-code can read them.
Communication uses the Keyence Non-Procedural command interface over TCP/IP. The control sends TRG on the data output port and the camera replies with the configured Data Output (Non-Procedural) tool values.
Components
| File | Purpose | |
Modules/Addons/ |
Top-level orchestrator. Calibration (K matrix), alignment-run state, fit solving, modal G-code emission, OEM-parameter wiring, pound-var output. | |
Modules/Addons/Vision/VisionMath.lua |
Pure-math helpers: pixel → machine-unit transform (ApplyK), 3-point similarity fit, 4-point rectangle fit, Procrustes / least-squares solvers. |
|
Modules/Addons/Vision/Drivers/KeyenceVisionDriver.lua |
Keyence VS-series Non-Procedural TCP, SelectJob, GrabImage, Capabilities. |
|
Modules/CommonMCodeModule.lua |
Hosts the _m280 | _m282, _m283, _m284 implementations. |
Profiles/Mill/Macros/m280.mcs … m284.mcs |
G-code macro |
|
|
OEM parameter definitions (Connection, Calibration, Output sections). |
M-Code Summary
| M-code | Purpose |
| M280 | Begin an alignment run. Clears the dot list and cancels any active G51 / G68 / G52. Optional P selects fit mode (4 = rectangle, 3 = similarity). |
| M281 | Capture one mark. Triggers the camera, applies the K matrix, stores the observed point in pound vars and the dot list. Optional D1 persists the FTP-pushed inspection image. |
| M282 | Solve and apply the fit as modal G51 / G68 / G52. Optional P selects which piece of the fit to emit (1 = shift, 2 = rotation, 3 = scale, omitted = all). |
| M283 | Calibrate the K matrix. Q = quick diameter calibration (no motion). C = full cal-sheet walk (recommended). |
| M284 | Select the active camera job/program (capability-gated — only drivers that advertise select_job support this). |
System Architecture
Data Flow
- Driver Trigger. The active driver fires the camera and returns a pixel deviation
(dx_px, dy_px)in its native frame (typically top-left origin). - Image-center shift.
VisionModule.CenterRawPixels()subtracts(resX/2, resY/2)using the OEM-configured camera resolution so all internal pixel coordinates are signed offsets from image center. - Camera-vs-machine axis remap. Three OEM toggles —
VisionCameraSwapXY,VisionCameraInvertX,VisionCameraInvertY— normalize the camera frame to the machine frame.M283auto-detects and writes these the first time you calibrate. - K matrix.
VisionModule.PxToMachine()applies the calibrated 2x2 K matrix to convert pixels to machine units (in or mm). K captures combined scale, axis-swap residual, and small rotation between the camera and the machine. - Observed point.
observed = commanded + K*(px - px_center_offset). The commanded XY is read frommcAxisGetPosat trigger time. - Fit. When
M282runs,VisionMath.RectangleFit(4-point) orVisionMath.SimilarityFit(3-point) solves a rigid-body transform from commanded → observed. - Apply.
M282emits the solved transform as modalG52(shift),G68(rotation), andG51(scale) viamcCntlGcodeExecuteWait. The rest of the program runs in the corrected frame.
Tool-Table Convention
The camera is treated as a tool. M283 C writes the camera-vs-spindle X/Y offsets directly into the tool table for the camera tool number, so the machine knows where the camera is relative to the spindle. The convention is:
- Activate the spindle tool. Jog over the cal-sheet center and zero work-coord X and Y.
- Tool-change (
Tn M6) to the camera tool. Jog so the start dot is in the FOV. - Run
M283 C1. The routine walks the sheet, solves K, and writes the camera tool's X/Y offsets so that with the camera tool active a commanded XY puts the camera — not the spindle — at that XY.
VisionCameraToolNumber must match the tool number you use for the camera. When set, every M280 / M281 / M282 / M283 verifies that this tool is the active spindle tool and aborts otherwise — this stops captures from running with the wrong tool offset in effect. Set it to 0 to disable the check.
Camera Setup (Keyence)
Network Configuration
- Default
camera IP expected by Mach:IP:192.168.208.80— configurable viaOEM parameterKeyenceVisionIP. The camera must be set to this address (or the OEM parameter must be changed to match the camera)VisionIP. - TCP
port for command + data output:port:8500(default)Non-Procedural data output) — configurable viaKeyenceVisionPortVisionPort. - Socket
I/Otimeout:3seconds(default)— configurable viaKeyenceVisionTimeoutVisionTimeout. - End delimiter:
CR(0x0D)0x0D).(camera default; the moduleModule assumesthis).this.
VS Creator Job Requirements
The job loaded on the camera must be configured to emit the mark-center offset in response to a TRG command:
The cameraCamera must be in Run Mode.Data Output (Non-Procedural) only fires in Run Mode.Configure the triggerTrigger sourceas= Communication / Command soTRGinitiates a measurement.AddConfigure a Data Output (Non-Procedural) toolthat emitsemitting comma-separatedvaluesfields.in oneEither ofthethesefollowingformatsformats:is accepted:dx,dy— the X and Y offset from the camera center to the mark center.judgment,dx,dyjudgment,count,dx,dy,width,height,angle,score(full form —judgmentrecommended(1for=calOK, 0 = NG) followed by the offsets.sheet)
ConfirmForM283 Qand image saving viaM281 D1, theendcamera must be configured to FTP-push inspection images to a folder reachable from Mach.- End delimiter
is= CR.
Image Folder (optional)
TheSet offsetVisionImageWatchDir valuesto arethe returned in whatever unitsfolder the camera jobFTP-pushes isimages configuredinto. toM281 output.D1Makelooks surefor an image newer than the trigger timestamp and copies it into Modules/Addons/Vision/Images/ with a timestamped filename. VisionImageGrabTimeout (default 2 s) controls how long the watch loop waits.
The 8500 protocol cannot toggle FTP push from the controller, so leave camera FTP push enabled at the camera unitsside matchand let the unitsD expectedflag byon theM281 programdecide readingwhether theeach OEMframe parameters.is kept.
OEM Parameters
Connection
Configuration Parameters
| Name | Type | Default | Description |
|
No = | success ||
| |||
|
Keyence | Driver name. Selects which class under Vision/Drivers/ is loaded. |
|
VisionIP |
text | 192.168.208.80 | Camera IP address. |
VisionPort |
integer | 8500 | TCP |
|
3 | Socket I/O timeout (seconds). | |
VisionActiveJob |
integer | 0 | Camera program/job id. Persistent; written by M284. 0 = leave camera on whatever job it has loaded. |
VisionImageWatchDir |
text | (empty) | Folder the camera FTP-pushes images into. Empty disables image grab. |
VisionImageGrabTimeout |
float | 2 | Seconds to wait for a fresh image to appear in |
VisionCameraSwapXY |
yesno | No | Swap camera X and Y axes. Auto-set by M283. |
VisionCameraInvertX |
yesno | No | Negate camera X. Auto-set by M283. |
VisionCameraInvertY |
yesno | No | Negate camera Y. Auto-set by M283. |
VisionTriggerSettleSec |
float | 0.25 | Pre-trigger dwell used by M283 only. Lets servo motion settle before capture. |
VisionCameraToolNumber |
integer | 0 | Camera tool number. M280 / M281 / M282 / M283 require this tool be active. 0 disables the check. |
ResultCalibration
Parameters
| Name | Type | Default | Description |
VisionCal_K11 |
float | 0 | K matrix row 1 col 1 (px → machine units). |
VisionCal_K12 |
float | 0 | K matrix row 1 col 2. |
VisionCal_K21 |
float | 0 | K matrix row 2 col 1. |
VisionCal_K22 |
float | 0 | K matrix row 2 col 2. |
VisionCal_Date |
text | (empty) | ISO timestamp of the last successful calibration. |
VisionCal_Units |
choice | in | Units the K matrix is in (in or mm). |
VisionCameraResX |
integer | 1776 | Camera image width in pixels. |
VisionCameraResY |
integer | 1776 | Camera image height in pixels. |
VisionPxCenterOffsetX |
float | 0 | Optional sub-pixel correction applied after image-center shift, before K. |
VisionPxCenterOffsetY |
float | 0 | Same, Y. |
Output
| Name | Type | Default | Description |
VisionPoundVarBase |
integer | 500 | Base address of the 5-slot, 8-stride pound-var output block. See Pound Variables. |
Runtime / Last Trigger (read-only)
AfterThese everyare written M280call,by the module writesafter every trigger and run. Surface them as DROs on the camera result to the following OEM parameters. G-code, scripts, and screen DROsfor can read them at any time.diagnostics.
| Name | Type | Description |
|
1 = OK, 0 = NG, -1 = unknown / not |
|
VisionLastOffsetPxX |
float | Last |
VisionLastOffsetPxY |
float | Last centered pixel Y offset. |
VisionLastOffsetX |
float | Last machine-unit X offset (K ). |
|
||
|
||
|
Detected mark height in current machine units. | |
VisionLastAngle |
float | Detected mark angle (deg, 0 if N/A). |
VisionLastScore |
float | Driver-reported correlation/score. |
VisionLastStatus |
text | |
VisionRun_Active |
float | 1 while a run is in progress, 0 otherwise. |
VisionRun_Mode |
integer | 4 = rectangle, 3 = similarity. |
VisionRun_DotCount |
integer | Captures taken so far in this run. |
VisionRun_Tx / _Ty |
float | Solved shift ( |
VisionRun_Theta |
float | Solved rotation (radians, G68 R). |
VisionRun_Sx / |
float | Solved scale (G51). |
VisionRun_CenterX / |
float | Rotation center (G68 X / Y). |
ModulePound-Variable Loading
Output
Every M281 writes the captured point into a fixed pound-variable block based at VisionPoundVarBase (default 500). The block is 5 slots of 8 vars each (40 vars total). Slot 0 is always the most recent capture (mirrored from whichever indexed slot just got written). Slots 1–4 hold the four M281 captures in the order they were taken during the current run.
For a non-default VisionPoundVarBase — call it B — replace 500 below with B. The address of slot s field f is #(B + s*8 + f).
| Address | Slot | Field | Description |
#500 |
0 (latest) | X | Observed X of the most recent capture, in machine units (commanded_x + dx_units). Mirrored from the slot that was just written. |
#501 |
0 (latest) | Y | Observed Y of the most recent capture, in machine units. |
#502 |
0 (latest) | Width | Detected feature width of the most recent capture, in current machine units (driver pixels × X-axis K column norm). |
#503 |
0 (latest) | Height | Detected feature height of the most recent capture, in current machine units. |
#504 |
0 (latest) | Angle | Detected angle of the most recent capture, in degrees. 0 if the camera job does not report angle. |
#505 |
0 (latest) | Score | Driver-reported correlation / match score for the most recent capture. |
#506 |
0 (latest) | reserved | Reserved for future use (planned: Z height). |
#507 |
0 (latest) | reserved | Reserved for future use (planned: detected area). |
#508 |
1 (point 1) | X | Observed X of the first M281 capture in this run, machine units. |
#509 |
1 (point 1) | Y | Observed Y of the first M281 capture, machine units. |
#510 |
1 (point 1) | Width | Detected width at point 1, machine units. |
#511 |
1 (point 1) | Height | Detected height at point 1, machine units. |
#512 |
1 (point 1) | Angle | Detected angle at point 1, degrees. |
#513 |
1 (point 1) | Score | Driver score at point 1. |
#514 |
1 (point 1) | reserved | Reserved (planned: Z). |
#515 |
1 (point 1) | reserved | Reserved (planned: area). |
#516 |
2 (point 2) | X | Observed X of the second M281 capture, machine units. |
#517 |
2 (point 2) | Y | Observed Y of the second M281 capture, machine units. |
#518 |
2 (point 2) | Width | Detected width at point 2, machine units. |
#519 |
2 (point 2) | Height | Detected height at point 2, machine units. |
#520 |
2 (point 2) | Angle | Detected angle at point 2, degrees. |
#521 |
2 (point 2) | Score | Driver score at point 2. |
#522 |
2 (point 2) | reserved | Reserved (planned: Z). |
#523 |
2 (point 2) | reserved | Reserved (planned: area). |
#524 |
3 (point 3) | X | Observed X of the third M281 capture, machine units. |
#525 |
3 (point 3) | Y | Observed Y of the third M281 capture, machine units. |
#526 |
3 (point 3) | Width | Detected width at point 3, machine units. |
#527 |
3 (point 3) | Height | Detected height at point 3, machine units. |
#528 |
3 (point 3) | Angle | Detected angle at point 3, degrees. |
#529 |
3 (point 3) | Score | Driver score at point 3. |
#530 |
3 (point 3) | reserved | Reserved (planned: Z). |
#531 |
3 (point 3) | reserved | Reserved (planned: area). |
#532 |
4 (point 4) | X | Observed X of the fourth M281 capture, machine units. Used as the fourth fiducial in a 4-point rectangle fit. |
#533 |
4 (point 4) | Y | Observed Y of the fourth M281 capture, machine units. |
#534 |
4 (point 4) | Width | Detected width at point 4, machine units. |
#535 |
4 (point 4) | Height | Detected height at point 4, machine units. |
#536 |
4 (point 4) | Angle | Detected angle at point 4, degrees. |
#537 |
4 (point 4) | Score | Driver score at point 4. |
#538 |
4 (point 4) | reserved | Reserved (planned: Z). |
#539 |
4 (point 4) | reserved | Reserved (planned: area). |
VisionPoundVarBase must be ≥ 100 and must not overlap Mach's reserved ranges (5061..5081 probe results, 5201..5500 fixture offsets). The module isvalidates loadedthis automaticallyon by both the screen environment and the M-code environment whenever KeyenceVisionEnabled equals 1. Loading happens in:
CommonGUIModule.LoadAllModules()— exposed as globalkvfor screen scripts.ModuleLoader.RequireCommonMCodeModulesAndGlobals()— exposed as globalkvfor M-code scripts.
When KeyenceVisionEnabled is 0, the module is not loaded and no TCP code runs.every M280 stilland existsaborts but logswith a messageclear error if misconfigured.
In a 3-point similarity run (M280 P3), only slots 1, 2, and returns3 successare withoutpopulated contacting— theslot camera.4 is left at its previous-run value. Slot 0 still mirrors whichever capture was most recent.
M280 — TriggerBegin CameraAlignment ReadRun
Overview
Overview
TriggersStarts a singlenew measurementalignment onrun. Clears the in-memory dot list, validates the pound-var base, verifies the camera and stores the result in the KeyenceVisionLast* OEM parameters. The G-code programtool is responsibleactive, forand firstemits positioningG69 theG50 cameraG52 overX0 theY0mark.to cancel any active rotation/scale/shift modal that a previous alignment may have left set.
Parameters
| Letter | Description |
| P | 4 or omitted = 4-point rectangle fit (recovers shift, rotation, X/Y scale).3 = 3-point similarity fit (recovers shift, rotation, uniform scale). |
Examples
M280 ( default 4-point rectangle fit )
M280 P3 ( 3-point similarity fit )
M281 — Capture One Mark
Overview
Triggers the camera at the current commanded position, applies the K matrix, and stores the observed point in the dot list and pound vars. The G-code program is responsible for first positioning the camera over the mark.
M281 aborts the program if the camera reports NG / no mark, if the K matrix is zero (no calibration), or if the camera tool is not active.
Parameters
| Letter | Description |
| D | Image-save flag.0 or omitted = discard the FTP-pushed image.1 = persist the image into Modules/Addons/Vision/Images/ with a timestamped filename. Requires VisionImageWatchDir to be set. |
Examples
G0 X1.0 Y1.0
M281 ( capture, do not save image )
G0 X1.0 Y1.0
M281 D1 ( capture and save image )
M282 — Solve and Apply Fit
Overview
Solves a fit from the captured dot list and emits the result as modal G52 (shift), G68 (rotation), and G51 (scale). The rest of the program runs in the corrected frame until the modals are cancelled.
G51, G68, and G52 are modal in Mach 4. Repeated M282 calls overwrite the previous values rather than compounding.
Parameters
| Letter | Description |
| P | Piece selector (optional).0 or omitted = G68 + G51 + G52) (default).1 = G52).2 G68 3 G51 Sx, Sy). |
Examples
Emitted G-code
The default (P omitted) emits, in order:
G69 G50 G52 X0 Y0 ( cancel prior rot/scale/shift )
G52 X{tx} Y{ty} ( shift )
G68 X{cx} Y{cy} R{theta_deg} ( rotate about centroid )
G51 X{sx} Y{sy} ( scale )
To cancel everything when the alignment job is finished, run:
G69 G50 G52 X0 Y0
Calibration Sheet
Overview
The MachMotion Vision Calibration Sheet is a printed US-Letter-portrait sheet with five black-and-white target marks arranged on a 5.00″ × 8.00″ rectangle plus a center mark. It is the reference geometry used by M283 C1 to solve the full 2×2 K matrix and write the camera-vs-spindle tool offset.
Sheet Layout
| Dot | Position (in) | Notes |
| 1 — Start | (-2.5, -4.0) | Lower-left. Operator parks the camera over this dot before issuing M283 C1. |
| 2 | (-2.5, +4.0) | Upper-left. |
| 3 | (+2.5, +4.0) | Upper-right. |
| 4 | (+2.5, -4.0) | Lower-right. |
| 5 — End | (0, 0) | Sheet center. Used to write the camera tool's X/Y offset and to verify the residual at the end of the cal walk. |
- Outer rectangle: 5.00″ wide (X) × 8.00″ tall (Y), centered on dot 5.
- Mark style: 0.5″ diameter quartered black/white target (high-contrast, symmetric — works well with the Keyence circle / blob detector).
- Walk order: 1 (Start) → 2 → 3 → 4 → 5 (End / Center).
- Trusted axis: X (8.5″ page width prints more accurately than the 11″ page length on most consumer printers).
Printing
- Print the supplied PDF on US Letter (8.5″ × 11″) at 100% scale / Actual Size. Do not use "Fit to Page" or "Shrink to Fit" — the cal math depends on the printed dimensions matching the table above.
- After printing, measure the X distance between dot 1 and dot 4 (or dot 2 and dot 3) with a ruler or caliper. It should read 5.00″ ± 0.01″. If your printer is off by more, scale-correct the PDF before reprinting.
- Use clean, matte paper. Glossy finishes can produce specular reflections that confuse the camera.
Mounting on the Machine
- Tape the sheet flat on the table or fixture. Any wrinkles, bubbles, or curls translate directly into apparent dot-position error.
- Orient the sheet so its X+ arrow points in the same direction as machine X+ and its Y+ arrow points the same direction as machine Y+. Small rotation is fine — the cal routine recovers it — but the sheet's X+ should not be pointing at machine Y+.
- Pick a working area where the spindle and camera both reach all five dots without colliding with fixturing.
A wavy or wrinkled sheet is the single biggest source of calibration error. Tape down all four corners and at least the center of each long edge. Heavier card-stock prints are noticeably better than 20-lb copy paper.
Calibration Procedure
- Mount the sheet as described above.
- Anchor the sheet to the spindle. Activate the spindle tool. Jog so the spindle tip is centered over dot 5 (the middle mark). Zero work-coord X and Y. The cal routine assumes the active WCS reads (0, 0) at dot 5 with the spindle.
- Tool-change to the camera. Issue
Tn M6for the camera tool number (matchesVisionCameraToolNumber). The camera will land somewhere offset from dot 5 because the tool-table X/Y offsets are not calibrated yet — that is normal. - Park over the start dot. Jog the camera in X/Y until dot 1 (lower-left, X-2.5 Y-4.0) is visible in the FOV. Centering is not required; the cal routine accepts any offset that keeps the dot in frame and that survives a 0.1″ bump in X and Y for the axis-detect step.
- Run the cal. Issue
M283 C1. The routine:- Auto-detects camera axis orientation (bumps +0.1″ X then +0.1″ Y, writes
VisionCameraSwapXY/InvertX/InvertY). - Walks dots 1 → 2 → 3 → 4 capturing pixel offset and machine position at each.
- Solves the rigid Procrustes fit and the per-axis K scale.
- Writes K to
VisionCal_K11..K22. - Moves to dot 5 (sheet center) and writes the camera tool's X/Y offsets so the camera lands on (0, 0) when commanded there.
- Re-walks all 5 dots in absolute G90 to report verify residuals.
- Auto-detects camera axis orientation (bumps +0.1″ X then +0.1″ Y, writes
- Inspect the result popup. Look for:
- Scale X ≈ Scale Y — should be within ~1% (e.g. 0.00203 / 0.00203). Wildly different values mean the sheet is rotated 90°, the camera is mounted askew, or the print scale is bad.
- Sheet rotation — small (< 1°). Larger means the sheet was taped down rotated, which is OK as long as it is consistent and small.
- Verify RMS — typically ≤ 0.020″ on a printed sheet. Higher means the sheet is wavy, the lens is dirty, or the lighting is changing during the walk.
Example Cal G-Code
( Vision K-matrix calibration - cal sheet 1 )
( Spindle tool active, X/Y already zeroed over sheet center, )
( camera tool then activated and jogged over dot 1. )
G90 G20 G17 ( absolute, inches, XY plane )
G54
T98 M6 ( camera tool - change number to match your setup )
M283 C1 ( walk sheet 1, solve K, write camera tool offset )
M30
Verifying the Calibration
After M283 C1 finishes, the camera tool's X/Y tool-table offset should put the camera over the commanded XY. Run the verify program in Example 2 to walk all five dots in absolute G90 with M280 / M281 / M282 active — the residual at the center capture should be < 0.005″ on a flat, well-printed sheet.
Re-run M283 C1 any time the camera is removed and remounted, the lens is touched, the working distance changes (different fixture height), or the verify residual drifts beyond your tolerance.
Camera Z Height & Working Distance
Why Z Height Matters
A smart camera does not measure distance — it measures pixels. The number of pixels per inch (or per mm) is set by the lens focal length and the working distance from the lens to the surface being imaged. The K matrix calibrated by M283 bakes that pixels-per-unit relationship in at the working distance present at calibration time. Move the camera closer or farther from the work surface and every pixel now represents a different real-world distance, so every M281 capture will report the wrong offset.
The Z distance from the camera lens to the top of the material being inspected MUST match the Z distance that was present when M283 C1 was run. Any change in standoff — thicker stock, different fixture, raised/lowered camera bracket, refocused lens — invalidates the calibration and requires a re-cal.
How Much Does It Matter?
For a typical machine-vision lens at a working distance WD, the pixel-to-unit scale changes roughly linearly with WD. A 10% change in standoff produces a ~10% change in measured offsets — on a 1.000″ commanded distance, that is a 0.100″ error. Even a 1% standoff change (about 0.060″ on a typical 6″ working distance) produces a 0.010″ scale error, which is enough to put the verify residual out of spec.
Best Practices
- Rigid camera mount. The camera bracket must not deflect. A loose or springy mount lets vibration change the working distance and degrades repeatability.
- Calibrate at the same Z you will inspect at. If the camera is on the spindle and is moved up/down with the Z axis, calibrate with the camera tool's Z offset and material thickness configured the way they will be at runtime. The cal sheet must be at the same height as the part top.
- Match material thickness. If your production stock is 0.250″ thick, the cal sheet should sit on a 0.250″ spacer (or directly on the part fixture) so its top surface is at the same Z as the parts you will inspect.
- Don't refocus after calibrating. Twisting the lens focus ring changes the optical magnification and silently invalidates K. If you must refocus, re-run
M283 C1immediately after. - Lock down the lens. Most VS-series lenses have a focus lock screw — tighten it after focusing.
- Fixed-Z cameras are easiest. A camera mounted to the gantry or a column at a fixed Z (not the spindle) keeps standoff constant for all parts of a given thickness and is the most robust setup.
When to Re-Calibrate
Re-run M283 C1 whenever any of the following change:
- The camera bracket is loosened, removed, or shimmed.
- The lens is rotated, refocused, swapped, or replaced.
- The aperture is opened/closed enough to noticeably change depth of field.
- Material thickness changes by more than ~5% of the working distance.
- The camera tool's Z offset is changed.
- A different fixture, sub-plate, or vise raises/lowers the part top.
- The verify-walk residual at the center dot starts drifting beyond your tolerance.
Symptom of a wrong Z: calibration converges with a small RMS residual but the production verify dot reports an offset that scales with distance from the work zero (e.g. small error near origin, large error at the far corners). That is a pure scale error and almost always means the working distance changed between cal and run.
M283 — Calibrate K Matrix
Overview
Calibrates the pixel-to-machine-units transform (the K matrix) and the camera-vs-spindle tool offsets. Two modes — exactly one of Q or C must be provided.
Both modes start by running an automatic axis-orientation check: the machine bumps +0.1 in X then +0.1 in Y, captures the resulting raw pixel deltas, and writes VisionCameraSwapXY / VisionCameraInvertX / VisionCameraInvertY so that subsequent triggers report machine-frame pixels. A mark must be in the FOV at the start, and the mark must remain in the FOV after a 0.1-unit bump in X and Y.
Parameters
| Letter | Description |
| Q | Quick diameter calibration. Qd sets K = diag(d/width_px, d/height_px) using a single trigger over a printed dot of known physical diameter d. No motion (besides the axis-detect bump). Useful for first setup or quick checks — assumes the camera is roughly axis-aligned, no off-diagonal terms. |
| C | Cal-sheet walk. Cn selects sheet id n from VisionModule.CAL_SHEETS. The routine commands incremental moves between dots on the printed sheet, captures pixel offset at each, and solves the full 2x2 K by least-squares plus a rigid Procrustes fit. Also writes the camera tool's X/Y tool-table offsets so the camera can be commanded directly in work coords. Recommended. |
M283 Q — Quick Diameter Calibration
- Activate the camera tool (
Tn M6). - Jog the camera so a printed dot is roughly centered in the FOV.
- Issue
M283 Qd, where d is the dot's physical diameter (in current units). - The routine runs the axis-detect bump, triggers the camera, computes K from the reported width/height, and re-triggers to confirm the residual.
( Quick cal: dot in FOV, half-inch printed diameter )
T98 M6
M283 Q0.5
M283 C — Cal-Sheet Walk (Recommended)
- Tape the printed cal sheet flat on the table, oriented as printed (X+ right, Y+ up).
- Activate the SPINDLE tool. Jog over the sheet center (dot 5) and zero work-coord X and Y. This anchors the sheet origin to the spindle.
- Activate the CAMERA tool (
Tn M6). Jog so the camera FOV is over the start dot (dot 1, lower-left). The dot must be visible in frame — perfect centering is not required. - Issue
M283 C1for sheet 1 (US Letter portrait, ±2.5 x ±4.0 in).
On success, a popup reports the per-axis scale, rotation, K matrix, sheet rotation, RMS residual, the tool-offset shift written to the camera tool, and the verify-walk residuals at each dot.
( Cal sheet walk - sheet center is current XY zero, camera tool active )
T98 M6
M283 C1
M284 — Select Camera Job
Overview
Switches the camera to a different program/job. Capability-gated — drivers that do not advertise select_job raise a clean error.
Parameters
| Letter | Description |
| P | Job id to switch to. If omitted (or P0), the persistent OEM VisionActiveJob is applied. On success, VisionActiveJob is updated. |
Examples
M284 P3 ( switch to camera job 3 and remember it )
M284 ( apply VisionActiveJob )
Example G-Code Programs
Example 1 — Calibration (cal-sheet walk)
( Vision K-matrix calibration - cal sheet 1 )
( 1. Tape sheet flat, X+ right / Y+ up )
( 2. Spindle tool active, jog over sheet CENTER, zero X and Y )
( 3. Camera tool active, jog over START dot (lower-left -2.5,-4.0) )
G90 G20 G17 ( absolute, inches, XY plane )
G54
T98 M6 ( camera tool - change number to match your setup )
M283 C1 ( walk sheet 1, solve K, write camera tool offset )
M30
Example 2 — Verify Calibration (5-dot walk in absolute G90)
( Vision alignment test - walks the 5-dot calibration sheet pattern )
( Sheet center = 0,0 in current work coords )
( Corners at (+/- 2.5, +/- 4.0) inch )
( Walk order: 1 -> 2 -> 3 -> 4 (4-point fit) -> 5 (center verify) )
G90 G20 G17 ( absolute, inches, XY plane )
G54 ( use whatever WCS is zeroed at sheet center )
T98 M6 ( camera tool )
M280 ( begin 4-point alignment run )
G0 X-2.5 Y-4.0 ( dot 1 - lower left )
M281
G0 X-2.5 Y4.0 ( dot 2 - upper left )
M281
G0 X2.5 Y4.0 ( dot 3 - upper right )
M281
G0 X2.5 Y-4.0 ( dot 4 - lower right )
M281
M282 ( solve + apply modal G51 / G68 / G52 )
G0 X0 Y0 ( dot 5 - center, post-fit verify )
M281 ( capture; pound vars #500..#507 show residual )
( Inspect #500 (X), #501 (Y) - both should be ~0 if fit is good. )
( To clear the transform when done: )
( G69 G50 G52 X0 Y0 )
M30
Example 3 — Quick Diameter Cal
( Quick K calibration from a single dot of known diameter )
( Dot must be in the FOV when M283 Q runs. )
G90 G20
T98 M6
M283 Q0.5 ( half-inch printed dot )
M30
Example 4 — 4-Point Rectangle Fit on a Production Part
( Locate a 6 x 4 in part by its 4 corner fiducials, then run the cut )
( Operator drops the part on the fixture roughly square; fiducials )
( are at the nominal corners (0,0), (6,0), (6,4), (0,4). )
G90 G20 G17
G54
T98 M6 ( camera tool )
M280 ( default 4-point rectangle fit )
G0 X0 Y0
M281 ( fid 1 )
G0 X6.0 Y0
M281 ( fid 2 )
G0 X6.0 Y4.0
M281 ( fid 3 )
G0 X0 Y4.0
M281 ( fid 4 )
M282 ( solve and apply modal transform )
T1 M6 ( swap to cutter )
( ...your normal cutting program in part-design coords... )
( ...G51/G68/G52 modally re-map every move to the actual part... )
( At end of program, cancel the transform: )
G69 G50 G52 X0 Y0
M30
Example 5 — 3-Point Similarity Fit
( Locate a part by 3 fiducials (similarity fit - shift, rotate, uniform scale) )
G90 G20 G17
G54
T98 M6
M280 P3 ( 3-point similarity fit )
G0 X0 Y0
M281
G0 X8.0 Y0
M281
G0 X4.0 Y6.0
M281
M282 ( apply )
T1 M6
( ...cutting program... )
G69 G50 G52 X0 Y0
M30
Example 6 — Reading the Result with Pound Variables
( Single-mark inspection: trigger and use the result in G-code math )
( Requires VisionPoundVarBase = 500 (default). )
G90 G20
T98 M6
M280 ( begin run so M281 has somewhere to store the dot )
G0 X10.0 Y8.0
M280M281 (read offset,capture; soft-fail#500=obs X, #501=obs Y, #502=W_px, #503=H_px )
( Apply the deviation as a fixture offset on cameraG55: error))
G10 L2 P2 X[#500] Y[#501]
M30
Example 7 — Switching Camera Jobs Mid-Program
(Halt Use a coarse fiducial finder for the programrough ifalignment, then switch )
( to a precise circle-fit job for the final inspection step. )
G90 G20
T98 M6
M284 P1 ( camera failsjob or1 returns= NG)coarse finder )
M280
G0 X0 Y0
M281
G0 X10.0 Y8.0Y0
M280M281
P1Behavior
IfKeyenceVisionEnabledis0, the call is logged and returns success immediately.Opens a TCP socket toKeyenceVisionIP:KeyenceVisionPortwith timeoutKeyenceVisionTimeout.SendsTRG\r.Reads response lines, skipping theTRGecho and any empty lines, up to 8 lines total.Parses the first data line as eitherdx,dy(judgment defaults to -1) orjudgment,dx,dy.WritesKeyenceVisionLastJudgment,KeyenceVisionLastOffsetX,KeyenceVisionLastOffsetY, andKeyenceVisionLastStatus.IfPis non-zero and the camera errored or returned NG (judgment = 0), halts the program withw.Error().
If the configured Data Output emits only dx,dy (no judgment field), KeyenceVisionLastJudgment is set to -1. In that case M280 P1 will halt only on transport errors (timeout, refused, malformed response), not on a missing judgment field.
Reading the Result from G-code
After M280 completes, the operator screen and any G-code or macro script can read the result from the OEM parameters listed above. From a G-code subroutine or another macro, the values can be retrieved with the standard wrapper helpers:
local dx = w.GetOEMParamValue("KeyenceVisionLastOffsetX")
local dy = w.GetOEMParamValue("KeyenceVisionLastOffsetY")
local judge = w.GetOEMParamValue("KeyenceVisionLastJudgment")
local status = w.GetOEMParamValueString("KeyenceVisionLastStatus")Reading into Pound Variables with M242
M242 is the general MachMotion macro for reading an OEM parameter and writing the value into a pound variable. Use it after M280 to expose the Keyence result to standard G-code math.
Parameters:
V— the pound variable that will receive the value read from the OEM parameter.(Data:"<ParamName>")— a G-code comment on the same line that names the OEM parameter to read.
Example — trigger the camera, then load each result into a pound variable:
G0 X10.0 Y8.Y6.0
M280M281
G0 X0 Y6.0
M281
M282 (read offset)apply M242alignment V550using job 1 captures )
M284 P2 (Data:"KeyenceVisionLastOffsetX") (#550camera job 2 = Xprecise offset)circle M242fit V551)
G0 X5.0 Y3.0
M281 (Data:"KeyenceVisionLastOffsetY") (#551fine-grained =measurement Yat offset)part M242 V552 (Data:"KeyenceVisionLastJudgment") (#552 = judgment)
(Now #550 and #551 can be usedcenter, in G-codealigned math,frame e.g.)
applyG69 correction)G50 G10G52 L2X0 P1Y0
X[#550] Y[#551]M30
M242 reads numeric OEM parameters. KeyenceVisionLastStatus is a string and should be read with w.GetOEMParamValueString() from a Lua script rather than with M242.
Error Handling and Diagnostics
Error Types
| Symptom | Likely Cause / Fix |
timeout |
Camera unreachable, wrong IP/port, or no TRG. |
connection refused |
Camera not in Run Mode, or another client already holds the socket. |
NG / no mark found |
The configured camera job did not find a fiducial in the FOV. Re-jog over the mark, check lighting, verify the camera job is locating the right feature. |
K matrix is zero -- run M283 to calibrate first |
VisionCal_K11..K22 are all zero. Run M283 C1 (or M283 Q). |
camera tool Tn is not the active tool |
VisionCameraToolNumber is set, but a different tool is active. Issue Tn M6 for the camera tool first, or clear the OEM to disable the check. |
VisionPoundVarBase is in reserved range |
Move the base out of 5061..5081 or 5201..5500; pick a value ≥ 100. |
ER,TRG,01 |
TRG in its current |
ER,TRG,06 |
Command not executable — |
ER,TRG,07 |
Camera-side |
| Malformed response | The Data Output |
Status Storage
On every call (success or fail),trigger, is KeyenceVisionLastStatusVisionLastStatusupdated.updated On— failureOK iton containssuccess, otherwise the error texttext. returned from the parse or socket layer; on successSurface it contains "OK". Operators can surface this on the screen as a diagnosticscreen DRO.DRO for live diagnostics.
Axis Orientation Mismatch
TheIf the camera reportsis anmounted offsetrotated 90° or with one or both axes flipped relative to itsthe ownmachine, center,every nottrigger toreports pixels in the spindlewrong frame and downstream cal / fit residuals will be large or tool.non-converging. AnySymptoms mechanicalinclude offseta betweenresidual that grows with the camerabump distance instead of shrinking, or a sheet-rotation result around 90°.
Run M283 C1 (or M283 Q) with a mark in the FOV. The pre-flight axis-detect bump test automatically writes the correct values for VisionCameraSwapXY, VisionCameraInvertX, and the working tool must be accounted for separately (for example, by applying a head-shift or fixture-offset adjustment in G-code after reading KeyenceVisionLastOffsetXVisionCameraInvertY / KeyenceVisionLastOffsetY).
Protocol Reference
(Keyence Driver)
The moduleKeyence driver implements a minimal subset of the Keyence VS Non-Procedural protocol. See the Keyence VS series user manual for the full command set.
| Command | Description |
TRG |
Trigger a single measurement. Camera echoes TRG\r and |
FVR |
Firmware version read. Used by the GUI Test Connection button. |
PW |
Program M284 when the driver advertises select_job. |
- Each command is terminated with
CR(0x0D). - Maximum command size: 500 bytes.
- Camera-side per-command timeout: 3 seconds.
- Error responses follow
ER,<cmd>,<nn>\r, wherennis a two-digit error code (01 unknown, 02 args, 03 range, 04 type, 05 not acceptable, 06 not executable, 07 timeout).
Camera vs. Tool Position
The camera reports an offset relative to its own optical center, not to the spindle or working tool. The cal-sheet walk (M283 C) handles this correctly by writing the camera-vs-spindle offset directly into the camera's tool-table entry. After cal, with the camera tool active, a commanded XY positions the camera at that XY. After tool-changing back to the cutter, the same XY positions the cutter at that XY — the alignment transform from M282 applies to both.
Appendix A — OEM Parameter Reference
Every OEM parameter the Vision system reads or writes, grouped by section as it appears in the Configuration → Vision screen. Parameters in the Last Result and Run State sections are runtime telemetry — they are written by the module on every capture / fit and are not intended to be edited by the operator. All names are prefixed with Vision and stored in the profile-level OEM database.
Connection
| Name | Type | Default | Description |
VisionEnabled |
yesno | No | Master enable. When No, every Vision M-code returns a clean "Vision is not enabled" error and no driver is loaded. |
VisionDriver |
choice | Keyence | Driver selector. Currently only the Keyence VS-series driver is shipped; new cameras are added by writing additional drivers under Modules/Addons/Vision/Drivers. |
VisionIP |
text | 192.168.208.80 | Camera IP address. The PC and the camera must be on the same subnet. |
VisionPort |
integer | 8500 | TCP port for the camera's data-output channel. Keyence VS default is 8500. |
VisionTimeout |
integer (s) | 3 | Per-command socket I/O timeout. Increase only if the network is unreliable; longer timeouts mask real failures. |
VisionActiveJob |
integer | 0 | Persistent "current job" selector. Updated by M284 Pn. M284 with no argument re-applies whatever value is stored here. |
VisionImageWatchDir |
text | (empty) | Local folder the camera FTP-pushes inspection images to. Used by M281 D1 to grab and rename the most recent image. |
VisionImageGrabTimeout |
float (s) | 2.0 | How long M281 D1 waits for a new image to appear in the watch folder before giving up. |
VisionCameraResX |
integer (px) | 1776 | Camera image width in pixels. Used to convert from native top-left pixel coords to signed image-center offsets. |
VisionCameraResY |
integer (px) | 1776 | Camera image height in pixels. Same role as VisionCameraResX on the Y axis. |
VisionTriggerSettleSec |
float (s) | 0.25 | Dwell M283 inserts after each commanded move and before triggering the camera. Lets motion settle so the captured pixel reading is stable. Production captures (M281) do not auto-dwell — precede them with a programmed G4 P if needed. |
VisionCameraSwapXY |
yesno | No | Camera-to-machine axis remap: swap raw pixel X and Y. Auto-set by the M283 axis-detect pre-flight bump. |
VisionCameraInvertX |
yesno | No | Camera-to-machine axis remap: negate raw pixel X (after any swap). Auto-set by M283. |
VisionCameraInvertY |
yesno | No | Camera-to-machine axis remap: negate raw pixel Y (after any swap). Auto-set by M283. |
Calibration
| Name | Type | Default | Description |
VisionCal_K11 |
float | 0 | Row 1, column 1 of the 2×2 K matrix. Pixels → machine-units in the X direction (plus any small camera/machine rotation cross-term goes into K12). |
VisionCal_K12 |
float | 0 | Row 1, column 2 of K. Captures small Y-pixel-into-X-units cross-coupling from camera/machine rotation. |
VisionCal_K21 |
float | 0 | Row 2, column 1 of K. Captures X-pixel-into-Y-units cross-coupling. |
VisionCal_K22 |
float | 0 | Row 2, column 2 of K. Pixels → machine-units in the Y direction. |
VisionCal_Date |
text (ISO) | (empty) | Local-time ISO timestamp written by M283 on a successful calibration. Useful as a screen DRO so an operator can see when the camera was last calibrated. |
VisionCal_Units |
choice | in | Units the K matrix was calibrated in (in or mm). The Vision module checks this against the current G20/G21 mode at trigger time. |
VisionPxCenterOffsetX |
float (px) | 0 | Optional bias added to the centered pixel X before applying K. Lets you correct for a camera whose optical center is not exactly the image-array center. |
VisionPxCenterOffsetY |
float (px) | 0 | Same as VisionPxCenterOffsetX, on the Y axis. |
Do not edit VisionCal_K11..K22 by hand. They are written atomically by M283; partial edits leave the camera mis-calibrated. Re-run M283 C1 instead.
Output
| Name | Type | Default | Description |
VisionPoundVarBase |
integer | 500 | Base pound-variable index for M281 output. M281 writes 8 consecutive pound vars starting at this base (see Pound-Variable Output). Must be ≥ 100 and outside the reserved Mach ranges 5061..5081 and 5201..5500. |
VisionCameraToolNumber |
integer | 0 | Camera tool number used in the tool table. M280 / M281 / M282 / M283 verify this tool is the active spindle tool and abort otherwise. Set to 0 to disable the check (not recommended for production). |
Last Result (telemetry — read-only)
| Name | Units | Description |
VisionLastJudgment |
1=OK / 0=NG | Pass/fail flag from the most recent camera trigger. |
VisionLastOffsetPxX |
px | Raw centered pixel offset X reported by the driver, after image-center shift but before swap/invert and K. |
VisionLastOffsetPxY |
px | Raw centered pixel offset Y, same conventions as VisionLastOffsetPxX. |
VisionLastOffsetX |
units | Pixel offset converted to machine units via the K matrix. This is the value M281 stores in pound var #base+0. |
VisionLastOffsetY |
units | Pixel offset converted to machine units, Y axis. |
VisionLastWidth |
units | Detected feature width from the camera (pixels × X-axis K column norm), in current machine units. |
VisionLastHeight |
units | Detected feature height (pixels × Y-axis K column norm), in current machine units. |
VisionLastAngle |
deg | Detected feature angle, if the camera job emits it. |
VisionLastScore |
— | Camera-side match score (driver-specific). Useful as a quality gate. |
VisionLastStatus |
text | OK on success, otherwise the error string. Surface as a screen DRO for live diagnostics. |
Run State (telemetry — read-only)
| Name | Units | Description |
VisionRun_Active |
0/1 | 1 between M280 and M282; 0 otherwise. |
VisionRun_Mode |
4 / 3 | 4 = rectangle (4-point) fit, 3 = similarity (3-point) fit. Set by M280 P. |
VisionRun_DotCount |
integer | How many M281 captures have been collected so far. |
VisionRun_Tx / VisionRun_Ty |
units | Solved shift, emitted as G52. |
VisionRun_Theta |
radians | Solved rotation, emitted as G68 R (converted to degrees on output). |
VisionRun_Sx / VisionRun_Sy |
scale | Solved per-axis scale (rectangle fit only), emitted as G51 X Y. |
VisionRun_CenterX / VisionRun_CenterY |
units | Rotation/scale center, emitted as G68 X Y / G51. |
Appendix B — Pixel-to-Machine-Units Math
This appendix walks the full transform chain a single capture goes through, from the camera's wire-format pixel reading to the observed point that M281 stores. Every step is implemented in VisionModule.lua / VisionMath.lua; the field references in parentheses point at the OEM parameters that drive each step.
Step 1 — Camera Native Pixels
The Keyence VS-series camera reports a hit as two floating-point pixel values: (px_native_x, px_native_y). The native frame is camera-defined — for the VS series, origin is the top-left of the image, X grows to the right, Y grows downward. Image dimensions are VisionCameraResX × VisionCameraResY pixels. The driver hands these values to the module unchanged.
Step 2 — Image-Center Shift
VisionModule.CenterRawPixels() converts the top-left-origin native pixel to a signed offset from the image center, and optionally applies a per-camera optical-center bias (VisionPxCenterOffsetX, VisionPxCenterOffsetY):
px_centered_x = px_native_x - (VisionCameraResX / 2) - VisionPxCenterOffsetX
px_centered_y = px_native_y - (VisionCameraResY / 2) - VisionPxCenterOffsetY
After this step, (0, 0) means the feature is exactly under the optical axis. The PxCenterOffset terms are normally zero — only set them if you have evidence the camera's optical center is biased away from the array center.
Step 3 — Camera-to-Machine Axis Remap
The camera can be physically mounted rotated 90° or with one axis flipped relative to the machine. The three boolean OEMs VisionCameraSwapXY, VisionCameraInvertX, VisionCameraInvertY normalize the centered pixel into the machine frame. They are applied in this order:
if VisionCameraSwapXY then
swap(px_centered_x, px_centered_y)
end
if VisionCameraInvertX then px_centered_x = -px_centered_x end
if VisionCameraInvertY then px_centered_y = -px_centered_y end
After this step, "positive pixel X" means the same direction as "positive machine X." M283's pre-flight bump test (a small +X then +Y move) sets these three flags automatically by checking which raw pixel axis moved and which sign.
Step 4 — K Matrix (Pixels → Machine Units)
VisionMath.ApplyK() applies the calibrated 2×2 K matrix to convert from machine-frame pixels to machine units (inches or millimeters, per VisionCal_Units):
| dx_units | | K11 K12 | | px_centered_x |
| | = | | * | |
| dy_units | | K21 K22 | | px_centered_y |
In an ideal install where the camera is square to the machine and the same scale on both axes, K = diag(s, s) where s is units-per-pixel. The off-diagonal terms K12 and K21 capture residual rotation between the camera array and the machine that the Step 3 boolean remap can't represent (because rotation is continuous, not 90°-discrete). They are normally small but nonzero after a real M283 C1 walk.
dx_units / dy_units is the offset from the optical axis to the detected feature, expressed in current G-code units. It is not a machine coordinate — it is a delta. Step 5 converts the delta into an absolute observed point.
Step 5 — Observed Point in Machine Coordinates
At the moment of the trigger, VisionModule reads the commanded XY from mc.mcAxisGetPos for the active WCS and adds the units-frame delta to get the observed point of the feature:
observed_x = commanded_x + dx_units
observed_y = commanded_y + dy_units
This is the value M281 stores into pound vars #base+0 / #base+1 and into the dot list used by M282. The fit step (M282) compares this observed point against the commanded point that was active when the trigger fired and solves a rigid-body transform from the commanded grid to the observed grid.
Full Transform — One Equation
Combining Steps 1–5, the observed point as a function of the camera's wire-format reading is:
observed = commanded + K * R * (px_native - res/2 - PxCenterOffset)
where R is the discrete ±1 / swap remap from Step 3, K is the continuous 2×2 calibration matrix from Step 4, and res/2 is the image-center vector. The cal-sheet walk in M283 C solves for both R (axis-detect bump) and K (Procrustes fit over the dot grid).
Where Each Step Lives in Code
| Step | Function | File |
| 1. Native px | KeyenceVisionDriver:Trigger() |
Modules/Addons/Vision/Drivers/KeyenceVisionDriver.lua |
| 2. Center shift | VisionModule.CenterRawPixels() |
Modules/Addons/Vision/VisionModule.lua |
| 3. Swap / invert | VisionModule.RemapToMachineFrame() |
Modules/Addons/Vision/VisionModule.lua |
| 4. K matrix | VisionMath.ApplyK() |
Modules/Addons/Vision/VisionMath.lua |
| 5. Observed point | VisionModule.PxToMachine() + _m281 |
VisionModule.lua / CommonMCodeModule.lua |