Skip to content
Christian edited this page Sep 29, 2026 · 5 revisions

The Winder V2 winds filament onto a spool: a puller draws the filament at a set speed, a tension arm measures the slack between puller and spool, the spool speed follows the tension arm, and a traverse lays the filament across the spool.

Winder V2 Winder V2 (EL7031-0030 spool)
Machine ID 2 98
Spool stepper EL7041-0052 EL7031-0030
Schema winder_v1.yaml winder_v1_7031_0030_spool.yaml

Both variants run the same code in winder_v2/. The XL Winder is a Winder V2 with Settings → Traverse Size → XL, which raises the outer traverse limit in the UI from 180 mm to 385 mm. Operating instructions: Winder-Manual.

Hardware

Role Terminal Purpose
0 EK1100 Bus coupler
1 EL2002 Laser pointer (output 1)
2 EL7041-0052 or EL7031-0030 Spool motor
3 EL7031 Traverse motor and end stop
4 EL7031-0030 Puller motor; its analog input reads the tension arm

Wiring is in the Winder-Manual; assigning roles in Identification.

How it works

flowchart TB
    subgraph CTRL["Winder V2 control"]
        direction LR
        LA["<a href='https://github.com/qitechgmbh/control/blob/jse-control-v2/qitech_control/src/machines/laser_v1.rs'>Laser V1</a>, optional<br/>via <a href='https://github.com/qitechgmbh/qitech_framework/blob/main/qitech_framework/src/machine/subscribe.rs'>RemoteProperty</a>"]:::framework
        PSC["<a href='https://github.com/qitechgmbh/control/blob/jse-control-v2/qitech_control/src/machines/winder_v2/puller/speed_controller.rs'>Puller speed controller</a><br/>direct or adaptive, jerk-limited"]:::control
        TA["<a href='https://github.com/qitechgmbh/control/blob/jse-control-v2/qitech_control/src/machines/winder_v2/tension_arm.rs'>Tension arm</a><br/>angle"]:::control
        SSC["<a href='https://github.com/qitechgmbh/control/blob/jse-control-v2/qitech_control/src/machines/winder_v2/spool/speed_controller.rs'>Spool speed controller</a><br/>min/max or adaptive"]:::control
        TR["<a href='https://github.com/qitechgmbh/control/blob/jse-control-v2/qitech_control/src/machines/winder_v2/traverse/mod.rs'>Traverse</a><br/>homing and traversing"]:::control
        LA -. "diameter" .-> PSC
        PSC -- "puller speed" --> SSC
        TA -- "arm angle" --> SSC
        SSC -- "spool rpm" --> TR
    end
    classDef frontend fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef control fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef framework fill:#fef3c7,stroke:#b45309,color:#78350f
    classDef lib fill:#ede9fe,stroke:#6d28d9,color:#4c1d95
    classDef hardware fill:#f1f5f9,stroke:#475569,color:#0f172a
Loading

Controllers

Tension arm

tension_arm.rs turns the voltage of the arm's angle sensor into an angle. The operating manual calls the arm the load arm.

  • Input: the analog input of the puller terminal EL7031-0030, in volts. 0–5 V maps to 0–1 revolution (modulo one revolution).
  • Zero: tension_arm.set_zero (UI: Set Zero Point) stores the current raw angle as the zero point, tension_arm.zero (revolutions, null until the arm is zeroed).
  • Output: tension_arm.angle = raw angle − zero, wrapped into 0°–360°. Values from 270° up are reported as negative, so the range is −90° to 270°.

Winding requires a zeroed arm. The zero point lives in memory only and is lost when the server restarts. The tension arm has no config parameters.

Spool speed controller

spool/speed_controller.rs sets the spool speed from the tension arm.

Filament tension. utils/filament_tension.rs converts the arm angle into a normalised tension, spool.speed_controller.filament_tension. It computes the filament path length puller → arm tip → traverse from fixed geometry and scales it so that 20° = 0.0 (slack, spool speeds up) and 90° = 1.0 (tight, spool slows down). Outside 20°–90° the tension is null and both algorithms target 0 rpm.

Algorithms (spool.speed_controller.algorithm):

  • Min/max (speed_algorithm_min_max.rs): target = speed_min + f(1 − tension) × (speed_max − speed_min) with the exponential curve f(x) = (e^(2x) − 1) / (e^2 − 1), which makes the speed more sensitive at low tension. An AngularAccelerationSpeedController limits the acceleration to 0.5/s × the higher of the target speed and the highest output speed of the last 5 s.
  • Adaptive (speed_algorithm_adaptive.rs): learns an effective spool radius, spool.speed_controller.adaptive.speed_factor (starts at 4.25 cm, limited to 4.25–20 cm). Whenever the tension is valid, also outside Wind mode, the factor changes by (tension − tension_target) × radius_learning_rate × Δt (cm, Δt in s): too much tension makes it larger and the spool slower. The adaptive maximum speed is puller.speed / speed_factor × max_speed_multiplier, and the target is speed_min + (1 − tension) × (adaptive maximum − speed_min). The acceleration limit is adaptive maximum × acceleration_factor; below a target of 0.1 rad/s it is multiplied by deacceleration_urgency_multiplier / (|target| + 0.01). It is never lower than 0.5 rad/s².

Output. The controller is enabled only in Wind mode; otherwise both algorithms ramp towards 0. The result is always clamped to speed_min…speed_max, stored as spool.speed_controller.speed, multiplied by the direction sign, converted to steps (200 steps/rev) and written to the spool stepper. spool.rpm is its absolute value.

Progress. Every cycle spool.progress changes by puller.speed × Δt, so it counts down while the puller runs in reverse. spool.reset_progress sets it to 0.

Parameter Unit Default Description
spool.direction forward / reverse forward Sign of the spool speed
spool.speed_controller.algorithm adaptive / min_max adaptive Speed algorithm
spool.speed_controller.speed_min rpm 0 Lower clamp for both algorithms. Min/max: speed at tension 1.0
spool.speed_controller.speed_max rpm 150 (max. 600) Upper clamp for both algorithms. Min/max: speed at tension 0.0
spool.speed_controller.adaptive.tension_target 0–1 0.7 Tension the adaptive algorithm steers to
spool.speed_controller.adaptive.radius_learning_rate – 0.5 Gain of the speed-factor learning
spool.speed_controller.adaptive.max_speed_multiplier – 4.0 Multiplier for the adaptive maximum speed
spool.speed_controller.adaptive.acceleration_factor 1/s 0.2 Base acceleration as a fraction of the adaptive maximum speed
spool.speed_controller.adaptive.deacceleration_urgency_multiplier – 15 Raises the acceleration limit when the target is close to 0

State: spool.mode (standby / hold / wind), spool.speed_controller.enabled. Measurements: spool.rpm, spool.progress (m), spool.speed_controller.speed (rpm), spool.speed_controller.filament_tension (0–1 or null), spool.speed_controller.adaptive.speed_factor (m). Command: spool.reset_progress.

Puller

puller/mod.rs and puller/speed_controller.rs drive the puller wheel.

Speed controller (puller.speed_controller.algorithm, UI: Speed Regulation with the options Fixed and Adaptive):

  • Direct: target = speed_desired.
  • Adaptive: target = speed_desired × (1 + modulation × speed_delta_max), never below 0. The laser diameter drives modulation (−1…1), see below.

Output chain. The target is used only while the puller is in Pull mode (winder Pull or Wind); otherwise it is 0. It is multiplied by the gear ratio (1, 5 or 10) and the direction sign, then a LinearJerkSpeedControllerDT limits it to ±50 m/min, ±5 m/min/s and ±10 m/min/s². The limited value is puller.speed. It is converted to steps (8 cm wheel, 200 steps/rev) and written to the puller stepper. The motor is enabled in Hold and Pull and disabled in Standby. The UI caps the target speed at 50 m/min divided by the gear ratio.

Laser-adaptive mode. When the winder is subscribed to a Laser V1, subscribe() in mod.rs binds two RemoteProperty values of the laser: the measurement diameter and the config diameter.target. While the subscription exists, the modulation is updated every cycle, whichever algorithm is selected:

  1. If |diameter − target| ≤ tolerance_limit: reset the distance counter and change nothing.
  2. Otherwise add the pulled distance (|puller.speed| × Δt) to puller.speed_controller.adaptive.distance_since_adjustment.
  3. Once the counter reaches adjustment_distance: change the modulation by increase_per_step (diameter too large: faster; too small: slower), clamp it to −1…1 and reset the counter.

Writing puller.speed_controller.algorithm through the API resets the modulation and the counter to 0. In the UI, turn on Settings → Puller → Adaptive Speed (Experimental) and pick the laser as Reference Machine. Only then does Adaptive appear under Speed Regulation.

Parameter Unit Default Description
puller.direction forward / reverse forward Sign of the puller speed
puller.gear_ratio one_to_one / one_to_five / one_to_ten one_to_one Multiplies the target speed by 1, 5 or 10 before the limits
puller.speed_controller.algorithm direct / adaptive direct Speed algorithm
puller.speed_controller.speed_desired m/min 0 Target speed; base speed in adaptive mode
puller.speed_controller.adaptive.speed_delta_max fraction 0 Relative speed change at modulation ±1 (UI: Max Speed Deviation, %)
puller.speed_controller.adaptive.increase_per_step fraction 0 Modulation change per step (UI: Change Per Step, %)
puller.speed_controller.adaptive.tolerance_limit mm 0 Dead band around the diameter target (UI: Allowed Diameter Deviation)
puller.speed_controller.adaptive.adjustment_distance m 0 Pulled length between two steps (UI: Distance Between Steps)

The speed, acceleration and jerk limits and the wheel size are constants in puller/mod.rs, not config. State: puller.mode (standby / hold / pull). Measurements: puller.speed (m/min, after gear ratio, direction and limits), puller.speed_controller.speed_target (m/min, before them), puller.speed_controller.adaptive.modulation, puller.speed_controller.adaptive.distance_since_adjustment (m).

Traverse

traverse/mod.rs moves the filament guide between the inner and the outer limit. Position 0 is the end stop; positive is outward.

  • Inputs: the end stop (first digital input of the EL7031), the EL7031 step counter (traverse.position, mm) and the spool rpm.
  • Homing (UI: Go to Home): moves to the end stop fast, backs off, approaches it again slowly, sets the counter to 0 and checks after 100 ms that the position is still 0 (±0.01 mm). Then traverse.homed is true.
  • Traversing (Wind mode): first moves out at 100 mm/s, then runs back and forth between limit_inner + padding and limit_outer − padding at spool rev/s × step_size, so every spool revolution moves the guide by one step size.
  • Manual moves (goto_limit_inner, goto_limit_outer): 100 mm/s, 10 mm/s for the last 1 mm, stop within 0.01 mm.
  • Mode side effects: entering Hold (winder Hold or Pull, coming from Standby or Wind) starts homing, entering Traverse (winder Wind) starts traversing, and Standby switches the motor off.
Parameter Unit Default Description
traverse.limit_inner mm 22 Inner turning point. Range 0 … limit_outer − 0.9
traverse.limit_outer mm 92 Outer turning point. Range limit_inner + 0.9 … 385
traverse.step_size mm 1.75 Traverse travel per spool revolution (winding pitch)
traverse.padding mm 0.88 Distance from each limit at which the traverse turns

When one limit is written through the API, the bound of the other limit moves with it (minimum gap 0.9 mm) and its value is clamped if needed.

Command Allowed when
traverse.goto_home Traverse in Hold mode (winder Hold or Pull), not homing, not traversing
traverse.goto_limit_inner Hold mode, homed, not homing, not traversing, not already moving in
traverse.goto_limit_outer Hold mode, homed, not homing, not traversing, not already moving out

State: traverse.mode (standby / hold / traverse), traverse.state, traverse.homed, traverse.endstop_triggered. Measurement: traverse.position (mm).

Laser pointer

laser_pointer.rs switches output 1 of the EL2002 with laser_pointer.enable / laser_pointer.disable and reports laser_pointer.enabled. It is independent of the mode.

Automatic spool action

check_spool_automatic_action in mod.rs stops or pulls after a set length. Every cycle, if the action isn't no_action and spool.progress ≥ required_meters, it sets the winder mode to Hold or Pull, the same way the mode commands do. In the UI this is the Spool Autostop card on the control page.

Parameter Unit Default Description
spool_automatic.required_meters m 250 Length after which the action runs (UI: Target Length)
spool_automatic.action no_action / pull / hold no_action Mode to switch to

Clone this wiki locally