Skip to content
LIBRARY
Sources.KinematicPTP.md

Sources.KinematicPTP ​

Move as fast as possible from q0 to q1 within given kinematic constraints (point-to-point motion).

For each axis the block emits a minimum-time trajectory that does not exceed the maximum velocity qd_max or maximum acceleration qdd_max. The result is a trapezoidal velocity profile — accelerate at qdd_max, cruise at qd_max, decelerate at qdd_max — or a triangular profile when the move is too short to reach qd_max. The position, velocity, and acceleration are provided directly as outputs q, qd, qdd (one scalar per axis), together with a Boolean moving (true while an axis is still travelling) and the scalar endTime (the instant the movement stops). This matches Modelica.Blocks.Sources.KinematicPTP2.

All axes are phase-synchronized: a single shared normalized path parameter s(t) in [0, 1] (one trapezoidal profile, paced by the bottleneck axis) drives every axis via q[i] = q0[i] + (q1[i] - q0[i]) * s(t). Because they share the same s(t), every axis is at the same fraction of its travel at every instant, so the joint path is a straight line — and they necessarily start and reach their targets together (phase synchronization implies time synchronization). This matches Modelica.Blocks.Sources.KinematicPTP and KinematicPTP2, and the vectorized point_to_point trajectory.

The motion begins at startTime (default 0); before that the outputs hold at the start position with zero velocity and acceleration.

The block is stateless: q, qd, qdd are closed-form functions of time, so no integration or discrete events are involved.

Usage ​

BlockComponents.Sources.KinematicPTP(q0=fill(0.0, nout), q1=fill(1.0, nout), qd_max=fill(1.0, nout), qdd_max=fill(1.0, nout), startTime=0, sd_max=BlockComponents.ptp_sd_max(q0, q1, qd_max), sdd_max=BlockComponents.ptp_sdd_max(q0, q1, qdd_max), moveTime=BlockComponents.ptp_move_time(sd_max, sdd_max), eps0=eps(Float64))

Parameters: ​

NameDescriptionUnitsDefault value
noutNumber of output axes–1
q0Initial position for each axis–fill(0.0, nout)
q1Final position for each axis–fill(1.0, nout)
qd_maxMaximum velocity for each axis–fill(1.0, nout)
qdd_maxMaximum acceleration for each axis–fill(1.0, nout)
startTimeTime instant at which the movement startss0

Connectors ​

  • q - This connector represents a real signal as an output from a component (RealOutput)

  • qd - This connector represents a real signal as an output from a component (RealOutput)

  • qdd - This connector represents a real signal as an output from a component (RealOutput)

  • moving - This connector represents a boolean signal as an output from a component (BooleanOutput)

  • endTime - This connector represents a real signal as an output from a component (RealOutput)

Behavior ​

Source ​

dyad
"""
Move as fast as possible from `q0` to `q1` within given kinematic constraints
(point-to-point motion).

For each axis the block emits a minimum-time trajectory that does not exceed the
maximum velocity `qd_max` or maximum acceleration `qdd_max`. The result is a
trapezoidal velocity profile — accelerate at `qdd_max`, cruise at `qd_max`,
decelerate at `qdd_max` — or a triangular profile when the move is too short to
reach `qd_max`. The position, velocity, and acceleration are provided directly as
outputs `q`, `qd`, `qdd` (one scalar per axis), together with a Boolean `moving`
(true while an axis is still travelling) and the scalar `endTime` (the instant the
movement stops). This matches `Modelica.Blocks.Sources.KinematicPTP2`.

All axes are **phase-synchronized**: a single shared normalized path parameter
`s(t)` in `[0, 1]` (one trapezoidal profile, paced by the bottleneck axis) drives
every axis via `q[i] = q0[i] + (q1[i] - q0[i]) * s(t)`. Because they share the same
`s(t)`, every axis is at the same fraction of its travel at every instant, so the
joint path is a straight line — and they necessarily start and reach their targets
together (phase synchronization implies time synchronization). This matches
`Modelica.Blocks.Sources.KinematicPTP` and `KinematicPTP2`, and the vectorized
`point_to_point` trajectory.

The motion begins at `startTime` (default 0); before that the outputs hold at the
start position with zero velocity and acceleration.

The block is stateless: `q`, `qd`, `qdd` are closed-form functions of time, so no
integration or discrete events are involved.
"""
component KinematicPTP
  "Position output for each axis"
  q = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {"diagram": {"iconName": "output", "x1": 1000, "y1": 20, "x2": 1100, "y2": 120}}
    }
  }
  "Velocity output for each axis"
  qd = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 220, "x2": 1100, "y2": 320}
      }
    }
  }
  "Acceleration output for each axis"
  qdd = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550}
      }
    }
  }
  "= true while axis i is still moving; false once its target is reached (or if it does not move)"
  moving = [BooleanOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 680, "x2": 1100, "y2": 780}
      }
    }
  }
  "Time instant at which the movement stops"
  endTime = RealOutput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 880, "x2": 1100, "y2": 980}
      }
    }
  }
  "Number of output axes"
  structural parameter nout::Integer = 1
  "Initial position for each axis"
  parameter q0::Real[nout] = fill(0.0, nout)
  "Final position for each axis"
  parameter q1::Real[nout] = fill(1.0, nout)
  "Maximum velocity for each axis"
  parameter qd_max::Real[nout] = fill(1.0, nout)
  "Maximum acceleration for each axis"
  parameter qdd_max::Real[nout] = fill(1.0, nout)
  "Time instant at which the movement starts"
  parameter startTime::Time = 0
  "Shared-knob speed limit (bottleneck axis) for the synchronized profile"
  final parameter sd_max::Real = BlockComponents.ptp_sd_max(q0, q1, qd_max)
  "Shared-knob acceleration limit (bottleneck axis) for the synchronized profile"
  final parameter sdd_max::Real = BlockComponents.ptp_sdd_max(q0, q1, qdd_max)
  "Total duration of the (bottleneck) move"
  final parameter moveTime::Real = BlockComponents.ptp_move_time(sd_max, sdd_max)
  "Machine-epsilon guard for detecting a non-moving axis"
  final parameter eps0::Real = eps(Float64)
relations
  endTime = startTime + moveTime
  for i in 1:nout
    moving[i] = (abs(q1[i] - q0[i]) > eps0) and (time < startTime + moveTime)
  end
  for i in 1:nout
    q[i] = q0[i] + (q1[i] - q0[i]) * BlockComponents.ptp_q(time, 0.0, 1.0, sd_max, sdd_max, startTime)
    qd[i] = (q1[i] - q0[i]) * BlockComponents.ptp_qd(time, 0.0, 1.0, sd_max, sdd_max, startTime)
    qdd[i] = (q1[i] - q0[i]) * BlockComponents.ptp_qdd(time, 0.0, 1.0, sd_max, sdd_max, startTime)
  end
metadata {
  "Dyad": {
    "labels": [
      {"label": "$(instance)", "x": 500, "y": 1100, "rot": 0},
      {
        "label": "q",
        "x": 845,
        "y": 70,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "qd",
        "x": 845,
        "y": 270,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "qdd",
        "x": 845,
        "y": 500,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "moving",
        "x": 845,
        "y": 730,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "endTime",
        "x": 845,
        "y": 930,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      }
    ],
    "icons": {"default": "dyad://BlockComponents/KinematicPTP.svg"}
  }
}
end
Flattened Source
dyad
"""
Move as fast as possible from `q0` to `q1` within given kinematic constraints
(point-to-point motion).

For each axis the block emits a minimum-time trajectory that does not exceed the
maximum velocity `qd_max` or maximum acceleration `qdd_max`. The result is a
trapezoidal velocity profile — accelerate at `qdd_max`, cruise at `qd_max`,
decelerate at `qdd_max` — or a triangular profile when the move is too short to
reach `qd_max`. The position, velocity, and acceleration are provided directly as
outputs `q`, `qd`, `qdd` (one scalar per axis), together with a Boolean `moving`
(true while an axis is still travelling) and the scalar `endTime` (the instant the
movement stops). This matches `Modelica.Blocks.Sources.KinematicPTP2`.

All axes are **phase-synchronized**: a single shared normalized path parameter
`s(t)` in `[0, 1]` (one trapezoidal profile, paced by the bottleneck axis) drives
every axis via `q[i] = q0[i] + (q1[i] - q0[i]) * s(t)`. Because they share the same
`s(t)`, every axis is at the same fraction of its travel at every instant, so the
joint path is a straight line — and they necessarily start and reach their targets
together (phase synchronization implies time synchronization). This matches
`Modelica.Blocks.Sources.KinematicPTP` and `KinematicPTP2`, and the vectorized
`point_to_point` trajectory.

The motion begins at `startTime` (default 0); before that the outputs hold at the
start position with zero velocity and acceleration.

The block is stateless: `q`, `qd`, `qdd` are closed-form functions of time, so no
integration or discrete events are involved.
"""
component KinematicPTP
  "Position output for each axis"
  q = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {"diagram": {"iconName": "output", "x1": 1000, "y1": 20, "x2": 1100, "y2": 120}}
    }
  }
  "Velocity output for each axis"
  qd = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 220, "x2": 1100, "y2": 320}
      }
    }
  }
  "Acceleration output for each axis"
  qdd = [RealOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550}
      }
    }
  }
  "= true while axis i is still moving; false once its target is reached (or if it does not move)"
  moving = [BooleanOutput() for i in 1:nout] {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 680, "x2": 1100, "y2": 780}
      }
    }
  }
  "Time instant at which the movement stops"
  endTime = RealOutput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "output", "x1": 1000, "y1": 880, "x2": 1100, "y2": 980}
      }
    }
  }
  "Number of output axes"
  structural parameter nout::Integer = 1
  "Initial position for each axis"
  parameter q0::Real[nout] = fill(0.0, nout)
  "Final position for each axis"
  parameter q1::Real[nout] = fill(1.0, nout)
  "Maximum velocity for each axis"
  parameter qd_max::Real[nout] = fill(1.0, nout)
  "Maximum acceleration for each axis"
  parameter qdd_max::Real[nout] = fill(1.0, nout)
  "Time instant at which the movement starts"
  parameter startTime::Time = 0
  "Shared-knob speed limit (bottleneck axis) for the synchronized profile"
  final parameter sd_max::Real = BlockComponents.ptp_sd_max(q0, q1, qd_max)
  "Shared-knob acceleration limit (bottleneck axis) for the synchronized profile"
  final parameter sdd_max::Real = BlockComponents.ptp_sdd_max(q0, q1, qdd_max)
  "Total duration of the (bottleneck) move"
  final parameter moveTime::Real = BlockComponents.ptp_move_time(sd_max, sdd_max)
  "Machine-epsilon guard for detecting a non-moving axis"
  final parameter eps0::Real = eps(Float64)
relations
  endTime = startTime + moveTime
  for i in 1:nout
    moving[i] = (abs(q1[i] - q0[i]) > eps0) and (time < startTime + moveTime)
  end
  for i in 1:nout
    q[i] = q0[i] + (q1[i] - q0[i]) * BlockComponents.ptp_q(time, 0.0, 1.0, sd_max, sdd_max, startTime)
    qd[i] = (q1[i] - q0[i]) * BlockComponents.ptp_qd(time, 0.0, 1.0, sd_max, sdd_max, startTime)
    qdd[i] = (q1[i] - q0[i]) * BlockComponents.ptp_qdd(time, 0.0, 1.0, sd_max, sdd_max, startTime)
  end
metadata {
  "Dyad": {
    "labels": [
      {"label": "$(instance)", "x": 500, "y": 1100, "rot": 0},
      {
        "label": "q",
        "x": 845,
        "y": 70,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "qd",
        "x": 845,
        "y": 270,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "qdd",
        "x": 845,
        "y": 500,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "moving",
        "x": 845,
        "y": 730,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      },
      {
        "label": "endTime",
        "x": 845,
        "y": 930,
        "rot": 0,
        "attrs": {"font-size": "120", "font-family": "sans-serif", "fill": "#939598"}
      }
    ],
    "icons": {"default": "dyad://BlockComponents/KinematicPTP.svg"}
  }
}
end


Test Cases ​

No test cases defined.