SoftSwitch

SoftSwitch cross-fades between two sets of signals and their first and second derivatives, and can work out its own fade time from velocity, acceleration and jerk limits instead of taking a duration.
3.30–3.34

SoftSwitch cross-fades between two sets of signals and their first and second derivatives, and can work out its own fade time from velocity, acceleration and jerk limits instead of taking a duration.

Use it for a handover where the output’s derivatives matter — where a feedforward path or a compliance model is consuming them, and a fade that respects a speed limit is worth more than a fade of a fixed length.

flowchart LR
    i1(["input1, input1Dot, input1DDot"]) --> B["SoftSwitch"]
    i2(["input2, input2Dot, input2DDot"]) --> B
    i3(["toggle"]) --> B
    i4(["doInstantToggle"]) --> B
    i5(["timeScaleFactor"]) --> B
    i6(["timeScaleFactorDot"]) --> B
    B --> o1(["output, outputDot, outputDDot"])
    B --> o2(["isOn"])
    B --> o3(["isOff"])
    B --> o4(["isDone"])
    B --> o5(["faderValue"])

The derivatives are correct here. outputDot really is the rate of change of output, right through the fade, including the part that comes from the blend itself moving. SimpleSwitch3D does not do this — if you need derivatives you can trust during a handover, this is the block.

Two ways to set the timing. With maxConstraintsEnable false you give a duration. With it true you give velocity, acceleration and jerk limits and the block computes the duration so the output respects them.

In limit mode, the timing is worked out at the moment you flip the toggle, from the gap between the inputs at that instant. If the inputs move apart afterwards, the limits will be exceeded.

Signals

Inputs

Path Unit Range Description
input1 signal unit unbounded The set selected when toggle is false. Where the block starts.
input1Dot signal unit per second unbounded Its rate of change. You supply this; it is used to make outputDot correct.
input1DDot signal unit per second² unbounded Its acceleration.
input2 signal unit unbounded The set selected when toggle is true.
input2Dot signal unit per second unbounded Its rate of change.
input2DDot signal unit per second² unbounded Its acceleration.
toggle - true or false Which set the output travels to. A level, not a pulse. Flipping it back mid-fade reverses smoothly, with no step in any derivative.
doInstantToggle - true or false While true, all three outputs snap to the selected set. A level — leave it true and the block never fades again.
timeScaleFactor - any Stretches the fade’s clock. 0 freezes it with nothing lost. A negative value is used as its magnitude; the fade does not run backwards.
timeScaleFactorDot 1/s any How fast timeScaleFactor is itself changing. Set this whenever you ramp the scale factor, or outputDDot will be wrong.

If the derivative inputs are not really the derivatives of the value inputs, the outputs will be a consistent blend of inconsistent data. Nothing checks this.

Outputs

Path Unit Description
output signal unit The blended value.
outputDot signal unit per second Its true rate of change, including the blend’s own contribution.
outputDDot signal unit per second² Its true acceleration.
isOn - The fade to input2 is complete. Not just started.
isOff - The fade to input1 is complete. Starts true.
isDone - No fade is in progress, in either direction.
faderValue - How far through the fade, 0 at input1 and 1 at input2. This is the shaped fraction, so scaling another signal by it matches this switch’s own profile.

Parameters

Path Unit Default Range Effect
maxConstraintsEnable - false - False: the fade takes fadeInTime or fadeOutTime. True: the duration is derived from the limit parameters below.
switchType - 0 0 or 1 0 quintic, 1 jerk-limited. See the table below. Changing it during a fade produces a kink — change it only when isDone is true.
fadeInTime s 5.0 above 0 Fade duration to input2, when limits are off. A value under one task period makes the switch snap.
fadeOutTime s 5.0 above 0 Fade duration back to input1, when limits are off.
delayBeforeFadeIn s 0.0 0 upward Wait before a fade to input2 starts. Applies in both timing modes.
delayBeforeFadeOut s 0.0 0 upward Wait before a fade back starts.
fadeInMaxVel signal unit per second zeros 0 upward Per channel. The most the output may move per second while fading in. 0 means this limit is not applied.
fadeInMaxAcc signal unit per second² zeros 0 upward Per channel. 0 means not applied.
fadeInMaxJerk signal unit per second³ zeros 0 upward Per channel. 0 means not applied.
fadeOutMaxVel signal unit per second zeros 0 upward Per channel, for the fade back.
fadeOutMaxAcc signal unit per second² zeros 0 upward Per channel.
fadeOutMaxJerk signal unit per second³ zeros 0 upward Per channel.
fadeInJerkFactor - 0.333 0 to 0.5 Shapes the jerk-limited profile. Ignored when maxConstraintsEnable is true — the block computes its own.
fadeInAccFactor - 0.429 0 to 0.5 Same, and same caveat.
fadeOutJerkFactor - 0.333 0 to 0.5 Same.
fadeOutAccFactor - 0.429 0 to 0.5 Same.

All are persistent and survive a controller restart. Negative limits and times are used as their magnitude. No parameters exist below this block.

switchType What the machine does Limits that apply
0 quintic Value, velocity and acceleration all start and end at zero. The smoothest shape Any of the three that is non-zero
1 jerk-limited Reaches its acceleration and velocity plateaus and holds them, so it covers ground faster A channel is ignored unless both its velocity and acceleration limits are set. A zero jerk limit degenerates the profile to a second-order one

Setup

  1. Connect both sets of three signals. All six must have the same number of channels.

  2. Leave toggle false. Confirm output matches input1 and isOff is true.

  3. Start in time mode: leave maxConstraintsEnable false and set fadeInTime and fadeOutTime to a second or two.

  4. Set toggle true and watch all three outputs travel to the input2 set.

    Step 4 hands the machine over to whatever input2 is carrying, at whatever rate the gap and the fade time imply. In time mode nothing limits that rate. Check the gap first.

  5. Confirm isOn and isDone go true and the outputs match the input2 set.

  6. Now try limit mode: set fadeInMaxVel per channel, set maxConstraintsEnable true, and toggle again.

  7. Confirm the fade actually got slower or faster. If nothing changed, the solve found no limits to apply and silently fell back to fadeInTime — nothing reports this.

Tuning

  1. Decide which mode you want. Limits are usually the right answer for a handover on a real axis, because they bound what the machine does rather than how long it takes.
  2. In limit mode, set the velocity limit first, per channel, in the signal’s own units. Leave acceleration and jerk at 0 until you need them.
  3. Verify the mode took effect by comparing the fade duration against the time-mode default. A silent fallback looks exactly like a correctly derived five-second fade.
  4. Remember the slowest channel sets the duration for all of them. One channel with a much smaller limit will slow the whole switch.
  5. With the jerk-limited shape, set both a velocity and an acceleration limit per channel or that channel is skipped entirely.
  6. Close the gap between the two input sets before switching wherever you can. In limit mode this shortens the fade automatically.
  7. Use timeScaleFactor for an override, and set timeScaleFactorDot while you ramp it or outputDDot will be wrong for the duration of the ramp.

Two fades between the same two values, with the fade time derived from avelocity limit of 0.5 and of 1.5. The faster fade’s velocity peaks at exactly1.5 — the limit is met, not approached.

In limit mode the duration is whatever it needs to be. In time mode the duration is fixed and the velocity is whatever it turns out to be.

Symptom Cause Action
Setting limits changed nothing The solve found nothing to constrain and fell back to the fade times, silently Check every limit is non-zero on at least one channel
Limits are set but ignored with the jerk-limited shape That shape skips any channel without both a velocity and an acceleration limit Set both, per channel
The fade was slower than one channel’s limit implies Expected: the slowest channel sets the duration for all Raise that channel’s limits
The output exceeded the limit I set The inputs moved apart after the toggle; the duration was fixed at the moment you flipped it Close the gap before switching
fadeInJerkFactor has no effect Expected: the factors are computed for you in limit mode Turn limits off to use them
A kink appeared mid-fade switchType was changed while a fade was running Change it only when isDone is true
The output moved very fast in time mode Expected: time mode does not limit the rate Use limit mode
The switch snapped instead of fading A fade time under one task period Lengthen it
The outputs stepped instead of fading doInstantToggle is true Set it false — it is a level, not a pulse
The fade froze part-way timeScaleFactor is 0 Set it above 0. Nothing was lost
A negative timeScaleFactor did not reverse the fade Expected: the magnitude is used Use the toggle to reverse
outputDDot was wrong while I changed the override timeScaleFactorDot was left at 0 Set it while ramping
isOn and isOff are both false mid-fade Expected: both require the fade to have completed Use faderValue for progress
The derivative outputs never made sense The derivative inputs may not match the value inputs Nothing checks this — verify upstream
The machine moved when I flipped the toggle Expected: the two input sets held different values Match them first
Everything became invalid and stayed that way An input that is not a number is not caught Fix the upstream signal

A starting point for a limit-based handover on a single axis:

maxConstraintsEnable = true
switchType           = 0
fadeInMaxVel         = 0.5
fadeInMaxAcc         = 5.0
fadeOutMaxVel        = 1.0
fadeOutMaxAcc        = 10.0
fadeInTime           = 5.0
fadeOutTime          = 5.0

fadeInTime and fadeOutTime are still worth setting sensibly — they are what you fall back to if the limits do not apply.

Limits and errors

Limit Set by What happens Reported
Derivative consistency Fixed outputDot and outputDDot are the true derivatives of output, through the fade Not applicable
Timing mode maxConstraintsEnable Limits derive the duration; otherwise the fade times do Not reported
Limit solve failure Nothing If no limit constrains anything, the block silently uses fadeInTime/fadeOutTime instead Not reported
Limit validity window Fixed The duration is solved when the toggle changes, from the gap at that moment. Later input movement is not accounted for Not reported
Limits per channel The limit arrays Each channel respects its own; the slowest sets the duration for all Not reported
Zero limits Checked A zero limit is skipped, not treated as zero Not reported
Jerk-limited shape Fixed A channel without both a velocity and an acceleration limit is ignored. A zero jerk limit degenerates the profile to second order Not reported
Factor parameters Fixed The four factors are ignored in limit mode Not reported
Output rate in time mode Nothing Not limited at all Not reported
Sub-cycle fades Checked A fade time under one task period makes the switch snap isDone stays true
Limit and time signs Checked Negatives are used as their magnitude Read the values back
switchType mid-fade Nothing Not guarded. An in-flight fade changes shape and keeps its old timing Not reported
timeScaleFactor Partly checked Negatives are negated. 0 freezes the fade with nothing lost Not reported
Values that are not numbers Nothing Not checked anywhere Not reported
Startup Fixed All three outputs start on the input1 set; isOff and isDone start true, and any fade in flight is cleared isDone
Channel count Fixed at build time All inputs, outputs and limit arrays share one channel count Not reported

This block logs nothing, ever — unlike the other switches, it has no fade time ceiling and no warnings. Every condition above shows as a value on a trace, or not at all.


Verified against motorcortex-control3 3.30.0 (bc348fd).