deploy webservice static-file routing for docsite folder URLs, per-instance step-cache db, and constant-height panel title bars.

This commit is contained in:
2026-08-27 14:51:37 +08:00
parent 1fc510af5e
commit 2a98ae7556
1468 changed files with 182837 additions and 18888 deletions
@@ -104,6 +104,10 @@
Simulate --> Map --> Train --> Evaluate --> Apply
</code></pre>
<h2 id="1-prerequisites">1. Prerequisites</h2>
<div class="TIP">
<h5>Tip</h5>
<p>For the complete data-collection checklist behind a project, see <a href="../manual/setup/project-data-checklist.html">Project Data Checklist</a>.</p>
</div>
<p>Before training you need:</p>
<table>
<thead>
@@ -131,6 +135,10 @@
</tr>
</tbody>
</table>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Before training, the workpiece + fixture must be correctly placed relative to the work offset, otherwise the simulated engagement (and therefore the trained coefficients) is wrong. See <a href="../manual/setup/program-zero-alignment.html">Program Zero Alignment</a> — particularly the high-fidelity caution that the G54 used must reflect the <strong>real</strong> machine offset, and the rough-resolution check for catching a wrong setup at the opening plunge.</p>
</div>
<h3 id="sensor-data-file-format">Sensor Data File Format</h3>
<p>The CSV must contain a header row with <code>ActualTime</code> and at least one force/torque channel:</p>
<table>
@@ -180,12 +188,52 @@ MachiningMotionResolution = FeedPerTooth;
<pre><code class="lang-csharp">EnablePhysics = true;
</code></pre>
<h3 id="milling-force-cycle-division">Milling Force Cycle Division</h3>
<p>Set the number of angular divisions per revolution before training. Higher values yield more accurate phase alignment:</p>
<pre><code class="lang-csharp">MillingForceCycleDivisionNum = 360;
<p><a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_MillingCycleDivisionNum">MillingCycleDivisionNum</a> is the number of angular divisions per spindle revolution used by the force evaluation. <strong>The default is 36, which is intended for normal simulation</strong> — force playback does not benefit from a finer division, and raising it only slows the physics down. <strong>Training is the exception</strong>: phase alignment and coefficient quality improve with a finer division, so set it in the training script:</p>
<pre><code class="lang-csharp">MillingCycleDivisionNum = 180; // training only; default 36 is for normal simulation
</code></pre>
<div class="NOTE">
<h5>Note</h5>
<p>This must be set <strong>before</strong> executing <code>TrainMillingPara</code>. A larger value produces a more accurate <code>AngleOffset</code> result and better milling coefficients.</p>
<p>This must be set <strong>before</strong> the simulation run that <code>TrainMillingPara</code> consumes.</p>
</div>
<p><strong>How much is enough — measured (2-flute D8, Al6061-T6, <code>MachiningResolution_mm</code> 0.03125, sensor series sampled at 0.1 ms):</strong></p>
<table>
<thead>
<tr>
<th><code>MillingCycleDivisionNum</code></th>
<th>R</th>
<th>Fc error @ t=0.2</th>
<th>Fn error @ t=0.2</th>
<th>ploughing coefficients</th>
</tr>
</thead>
<tbody>
<tr>
<td>36 (default)</td>
<td>91.5 %</td>
<td>−6.7 %</td>
<td>−8.8 %</td>
<td>badly off (Kpc +40 %)</td>
</tr>
<tr>
<td><strong>180</strong></td>
<td><strong>95.2 %</strong></td>
<td><strong>−6.6 %</strong></td>
<td><strong>−6.1 %</strong></td>
<td>Kpc +10 %</td>
</tr>
<tr>
<td>720</td>
<td>95.2 %</td>
<td>−6.9 %</td>
<td>−6.3 %</td>
<td>Kpc +11 %</td>
</tr>
</tbody>
</table>
<p><strong>180 is the sweet spot: the default 36 is genuinely too coarse for training (the ploughing coefficients degrade badly), and 720 buys nothing while costing 4× the physics.</strong> Do not assume &ldquo;larger is always better&rdquo;.</p>
<div class="WARNING">
<h5>Warning</h5>
<p>The value is <strong>process-wide, not per-project</strong>: it is not saved into the <code>.hincproj</code>, and it survives <code>ResetRuntime()</code> and project switches. Two consequences: (a) a training script must always set it explicitly — a fresh service instance starts at 36; (b) after training, the same instance keeps the large value, so subsequent normal simulations run slower until you set it back (or restart the instance).</p>
</div>
<hr>
<h2 id="3-configure-data-mapping">3. Configure Data Mapping</h2>
@@ -207,12 +255,94 @@ Offset options: <code>null</code>, <code>ShiftTime_s(&lt;seconds&gt;)</code>, <c
<p>When you have both controller data and sensor data:</p>
<pre><code class="lang-csharp">PlayNcFile(&quot;NC/machining.nc&quot;);
MapSingleByCsvFile(&quot;Data/controller.csv&quot;); // maps FileNo/LineNo → ActualTime
MapSeriesByCsvFile(&quot;Data/sensor.csv&quot;); // maps ActualTime → sensor series
MapSeriesByCsvFile(&quot;Data/sensor.csv&quot;); // ActualTime → sensor series
</code></pre>
<div class="NOTE">
<h5>Note</h5>
<p><strong>Why two-layer mapping?</strong> Running the NC through the system interpreter produces more accurate simulation paths than direct CSV playback. The controller data bridges simulation steps to real time via <code>FileNo</code>/<code>LineNo</code>, and the sensor data bridges real time to force/torque readings.</p>
</div>
<h3 id="-train-on-the-steady-part-of-the-cut--it-is-the-largest-single-lever">⚠ Train on the steady part of the cut — it is the largest single lever</h3>
<p><a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_MapSeriesByCsvFile_">MapSeriesByCsvFile</a> pairs each step with a window of sensor
samples. The window is anchored at the step's <strong>end</strong> time and runs forward for one cycle period, so
the samples a step is fitted against are the ones its <strong>neighbours</strong> produced, never its own.</p>
<p><strong>While the cut is steady, that costs nothing.</strong> A neighbouring step cutting the same arc at the
same chip load produces, at a given rotation angle, the same force this step would have produced, so
the borrowed samples are extra data rather than extra error. The pairing turns into a systematic
bias only where the <strong>cutter–workpiece engagement changes across the window</strong> — cutter entry, pass
exit, corners, depth changes.</p>
<p>Measured on the D8 2-flute Al6061-T6 case (one source file at a 1 ms sampling period; only the
training set and the <code>CycleSamplingMode</code> window vary):</p>
<table>
<thead>
<tr>
<th>training set</th>
<th>window</th>
<th>Fc error @ t=0.2</th>
<th>Fn error @ t=0.2</th>
<th>R</th>
</tr>
</thead>
<tbody>
<tr>
<td>whole path (1076 cutting steps)</td>
<td><code>SpindleCycle</code> (default)</td>
<td>−9.3 %</td>
<td>−9.9 %</td>
<td>94.3 %</td>
</tr>
<tr>
<td>whole path</td>
<td><code>FluteCycle</code></td>
<td>−7.1 %</td>
<td>−8.1 %</td>
<td>95.7 %</td>
</tr>
<tr>
<td><strong>steady part only (~800 steps)</strong></td>
<td><code>SpindleCycle</code> (default)</td>
<td><strong>−3.3 %</strong></td>
<td><strong>−2.3 %</strong></td>
<td><strong>98.5 %</strong></td>
</tr>
<tr>
<td><strong>steady part only (~800 steps)</strong></td>
<td><code>FluteCycle</code></td>
<td><strong>−3.2 %</strong></td>
<td><strong>−2.4 %</strong></td>
<td><strong>98.6 %</strong></td>
</tr>
</tbody>
</table>
<p>Two things to read off it. First, <strong>restricting training to the steady part is worth more than any
other setting</strong> — here it more than halved the error, and did so at the coarsest sampling period.
Second, <strong>once the transient is gone the window choice stops mattering</strong> (0.06 percentage points
between the two modes, versus 2.2 on the whole path): <code>FluteCycle</code>'s advantage on the whole path was
only ever a shorter exposure to the entry and exit ramps. Keep the <code>SpindleCycle</code> default and its
larger sample count.</p>
<p>In this case the transient was 272 of the 1076 cutting steps: the entry ramp, where the radial
engagement builds from 0.09 mm to the nominal 2 mm, and the pass exit, where it climbs to 5 mm as
the cutter runs off the far corner. Select the training section by requiring the engagement to be
constant — <code>CuttingWidth_mm</code> and <code>CuttingDepth_mm</code> steady across neighbouring steps — rather than by
eyeballing the toolpath.</p>
<div class="NOTE">
<h5>Note</h5>
<p><strong>Gaps in the sensor data are handled.</strong> A step whose pairing window contains no measured row —
an acquisition dropout, or rows removed to keep the steady section only — is excluded from the
mapping, and a single <code>Map-ShotGap--StepsSkipped</code> warning reports how many steps were skipped.
A window-edge row is only interpolated when the rows bracketing the edge span at most two
spindle revolutions; rows farther apart sit across a gap, and no value is fabricated from them.
Watch for that warning after mapping measured data: a large count means the acquisition and the
play do not overlap the way you think they do.</p>
</div>
<div class="WARNING">
<h5>Warning</h5>
<p><a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_MachiningResolution_mm">MachiningResolution_mm</a> must not be too coarse for
training: at 1/16 of the cutter diameter and coarser (D8 case: 0.0625 and 0.125), usable
samples dropped by half and the phase pairing degraded badly — at 0.0625 it failed to
recognise one of the six cutting passes outright, and coefficients came out wildly wrong
(Fc −12 % to −33 %). Around 1/256 of the diameter (0.03125 here) the result saturates;
refining further showed no benefit.</p>
</div>
<hr>
<h2 id="4-run-simulation">4. Run Simulation</h2>
<pre><code class="lang-csharp">PlayNcFile(&quot;NC/file1.nc&quot;);
@@ -230,9 +360,46 @@ MapSeriesByCsvFile(&quot;Data/sensor.csv&quot;); // maps ActualTime → se
<h2 id="5-export-simulation-data">5. Export Simulation Data</h2>
<p>Export step data and waveform data for analysis:</p>
<pre><code class="lang-csharp">WriteStepFiles(&quot;Output/[NcName].step.csv&quot;);
WriteShotFiles(&quot;Output/[NcName].shot.csv&quot;, 1);
WriteShotFiles(&quot;Output/[NcName].shot.csv&quot;, 0.1); // 2nd arg = sampling period in ms
</code></pre>
<p>The shot file contains time-resolved force columns: <code>Tool.Fx/Fy/Fz</code>, <code>Workpiece.Fx/Fy/Fz</code>, <code>Spindle.Mx/My/Mz</code>.</p>
<h3 id="-the-shot-sampling-period-is-the-dominant-accuracy-lever">⚠ The shot sampling period is the dominant accuracy lever</h3>
<p>The second argument of <a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_WriteShotFiles_">WriteShotFiles</a> is the sampling period in <strong>milliseconds</strong>, and when a simulated shot file is fed back into training it — not the angular division count, not the machining resolution — sets the accuracy ceiling. Each row is interpolated from the per-division force waveform, so the information per revolution is <code>min(MillingCycleDivisionNum, samples per revolution)</code>, where</p>
<pre><code>samples per revolution = 60000 / (rpm × samplingPeriod_ms)
</code></pre>
<p>Measured on the same case (2-flute D8, Al6061-T6, <code>MachiningResolution_mm</code> 0.03125, S1270 → one revolution = 47.2 ms):</p>
<table>
<thead>
<tr>
<th>sampling period</th>
<th>samples / rev</th>
<th>R</th>
<th>Fc error @ t=0.2</th>
<th>Fn error @ t=0.2</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 ms</td>
<td>47</td>
<td>94.2 %</td>
<td>−9.3 %</td>
<td>−9.9 %</td>
</tr>
<tr>
<td><strong>0.1 ms</strong></td>
<td><strong>472</strong></td>
<td><strong>95.2 %</strong></td>
<td><strong>−6.6 %</strong></td>
<td><strong>−6.1 %</strong></td>
</tr>
</tbody>
</table>
<p>At 1 ms the waveform is sampled only ~47×/rev, and raising <code>MillingCycleDivisionNum</code> from 180 to 720 changes nothing because the extra grid points are pure interpolation of the same data. <strong>Pick the sampling period first</strong>: for training on simulated data use a period at or below the division period (<code>60000 / (rpm × MillingCycleDivisionNum)</code> ms); when comparing against a real measurement, match the sampling period to the physical DAQ rate, otherwise the two sides carry different information densities and the comparison is biased.</p>
<div class="NOTE">
<h5>Note</h5>
<p>A fine period makes large files — the case above went from 13 MB at 1 ms to 128 MB at 0.1 ms for a 6-cut program. Budget disk accordingly, and do not commit such files to a repository.</p>
</div>
<p>For coordinate system explanations, see <a href="../manual/analysis/milling-physics-coordinates.html">Milling Physics Coordinates</a>.</p>
<hr>
<h2 id="6-train-milling-parameters">6. Train Milling Parameters</h2>
@@ -335,7 +502,7 @@ WriteShotFiles(&quot;Output/[NcName].shot.csv&quot;, 1);
<h2 id="complete-script-example">Complete Script Example</h2>
<pre><code class="lang-csharp">MachiningResolution_mm = 0.0625;
EnablePhysics = true;
MillingForceCycleDivisionNum = 360;
MillingCycleDivisionNum = 180; // training only; default 36 suits normal simulation, 720 buys nothing
ClearTimeMappingData();
AddTimeDataByFile(&quot;lineA&quot;, &quot;Mapping/sensor1.csv&quot;, &quot;18:25:51.7100&quot;, &quot;18:26:12.9910&quot;);
@@ -347,7 +514,7 @@ TrainMillingPara(Fx|Fy|Fz, &quot;MillingPara/trained.mp&quot;);
LoadCuttingParaByFile(&quot;MillingPara/trained.mp&quot;);
WriteStepFiles(&quot;Output/[NcName].step.csv&quot;);
WriteShotFiles(&quot;Output/[NcName].shot.csv&quot;, 1);
WriteShotFiles(&quot;Output/[NcName].shot.csv&quot;, 0.1);
</code></pre>
<h2 id="see-also">See Also</h2>
<ul>