Skip to content
LIBRARY
PlanarMechanics.SensorsTest.md

PlanarMechanics.SensorsTest

Sensor test with two free-falling bodies: validates all 6 sensor types.

Translates the "Sensors (two free falling bodies)" test from test_PlanarMechanics.jl. Two bodies (m=1, I=1) start at the origin with zero velocity and fall under gravity. A fixed base sits at the origin. Three absolute sensors are attached to body1, and six relative sensors measure pairs (body1-vs-base and body1-vs-body2).

Usage

MultibodyComponents.PlanarMechanics.SensorsTest()

Behavior

julia
using MultibodyComponents #hide
using ModelingToolkit #hide
@named sys = MultibodyComponents.PlanarMechanics.SensorsTest() #hide
full_equations(sys) #hide
<< @example-block not executed in draft mode >>

Source

dyad
"""
Sensor test with two free-falling bodies: validates all 6 sensor types.

Translates the "Sensors (two free falling bodies)" test from test_PlanarMechanics.jl.
Two bodies (m=1, I=1) start at the origin with zero velocity and fall under gravity.
A fixed base sits at the origin. Three absolute sensors are attached to body1, and six
relative sensors measure pairs (body1-vs-base and body1-vs-body2).
"""
test component SensorsTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 615, "x2": 120, "y2": 715, "rot": 0}
      },
      "tags": []
    }
  }
  body1 = Body(m = 1.0, I = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 715, "x2": 412, "y2": 815, "rot": 0}
      },
      "tags": []
    }
  }
  body2 = Body(m = 1.0, I = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 1270, "x2": 412, "y2": 1370, "rot": 0}
      },
      "tags": []
    }
  }
  abs_pos_sensor = AbsolutePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 295, "x2": 265, "y2": 395, "rot": 0}
      },
      "tags": []
    }
  }
  abs_v_sensor = AbsoluteVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 820, "x2": 265, "y2": 920, "rot": 0}
      },
      "tags": []
    }
  }
  abs_a_sensor = AbsoluteAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 160, "y1": 990, "x2": 260, "y2": 1090, "rot": 0}
      },
      "tags": []
    }
  }
  rel_pos_sensor1 = RelativePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 163.5,
          "y1": 415,
          "x2": 263.5,
          "y2": 515,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  rel_pos_sensor2 = RelativePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1145, "x2": 265, "y2": 1245, "rot": 0}
      },
      "tags": []
    }
  }
  rel_v_sensor1 = RelativeVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 163.5,
          "y1": 615,
          "x2": 263.5,
          "y2": 715,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  rel_v_sensor2 = RelativeVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1395, "x2": 265, "y2": 1495, "rot": 0}
      },
      "tags": []
    }
  }
  rel_a_sensor1 = RelativeAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 163.5, "y1": 85, "x2": 263.5, "y2": 185, "rot": 0}
      },
      "tags": []
    }
  }
  rel_a_sensor2 = RelativeAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1270, "x2": 265, "y2": 1370, "rot": 0}
      },
      "tags": []
    }
  }
  distance = MultibodyComponents.PlanarMechanics.Distance() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 370, "y1": 1070, "x2": 470, "y2": 1170, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # initial conditions for body1
  initial body1.r = [0, 0]
  initial body1.v = [0, 0]
  initial body1.phi = 0
  initial body1.w = 0
  # initial conditions for body2
  initial body2.r = [0, 0]
  initial body2.v = [0, 0]
  initial body2.phi = 0
  initial body2.w = 0
  # absolute sensors on body1
  connect(body1.frame_a, abs_pos_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 292, "y": 765},
            {"x": 292, "y": 565},
            {"x": 145, "y": 565},
            {"x": 145, "y": 345}
          ],
          "E": 2
        }
      ],
      "renderStyle": "standard"
    }
  }
  connect(body1.frame_a, abs_v_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 292, "y": 765}, {"x": 292, "y": 970}], "E": -1},
        {"S": -1, "M": [{"x": 145, "y": 870}], "E": 2}
      ],
      "junctions": [{"x": 145, "y": 970}],
      "renderStyle": "standard"
    }
  }
  connect(body1.frame_a, abs_a_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 292, "y": 765},
            {"x": 292, "y": 970},
            {"x": 145, "y": 970},
            {"x": 145, "y": 1040}
          ],
          "E": 2
        }
      ],
      "renderStyle": "standard"
    }
  }
  # rel_pos_sensor1: frame_a=body1, frame_b=base
  connect(body1.frame_a, rel_pos_sensor1.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 292, "y": 765}], "E": -1},
        {"S": -1, "M": [{"x": 145, "y": 565}], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 292, "y": 565}, {"x": 145, "y": 465}],
      "renderStyle": "standard"
    }
  }
  connect(world.frame_b, rel_pos_sensor1.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {
          "S": -1,
          "M": [{"x": 135, "y": 20}, {"x": 302, "y": 20}, {"x": 302, "y": 465}],
          "E": 2
        }
      ],
      "junctions": [{"x": 135, "y": 665}],
      "renderStyle": "standard"
    }
  }
  # rel_pos_sensor2: frame_a=body1, frame_b=body2
  connect(body1.frame_a, rel_pos_sensor2.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 135, "y": 765}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 135, "y": 1195}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_pos_sensor2.frame_b, distance.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 282, "y": 1195}], "E": -2},
        {"S": -2, "M": [], "E": 2},
        {"S": 3, "M": [{"x": 500, "y": 1120}, {"x": 500, "y": 1195}], "E": -2}
      ],
      "junctions": [{"x": 282, "y": 1320}, {"x": 280, "y": 1195}],
      "renderStyle": "standard"
    }
  }
  # rel_v_sensor1: frame_a=base, frame_b=body1
  connect(world.frame_b, rel_v_sensor1.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(body1.frame_a, rel_v_sensor1.frame_b) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 292, "y": 765}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 292, "y": 665}],
      "renderStyle": "standard"
    }
  }
  # rel_v_sensor2: frame_a=body1, frame_b=body2
  connect(body1.frame_a, rel_v_sensor2.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 135, "y": 765}, {"x": 135, "y": 1445}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_v_sensor2.frame_b) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 282, "y": 1320}, {"x": 282, "y": 1445}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # rel_a_sensor1: frame_a=body1, frame_b=base
  connect(body1.frame_a, rel_a_sensor1.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {
          "S": -1,
          "M": [{"x": 292, "y": 30}, {"x": 145, "y": 30}, {"x": 145, "y": 135}],
          "E": 2
        }
      ],
      "junctions": [{"x": 292, "y": 765}],
      "renderStyle": "standard"
    }
  }
  connect(world.frame_b, rel_a_sensor1.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 135, "y": 665}], "E": -1},
        {"S": -1, "M": [{"x": 282, "y": 240}, {"x": 282, "y": 135}], "E": 2}
      ],
      "junctions": [{"x": 135, "y": 240}],
      "renderStyle": "standard"
    }
  }
  # rel_a_sensor2: frame_a=body1, frame_b=body2
  connect(rel_a_sensor2.frame_a, body1.frame_a, distance.frame_a) {
    "Dyad": {
      "edges": [
        {"S": -1, "M": [], "E": 1},
        {"S": 2, "M": [{"x": 135, "y": 765}], "E": -2},
        {"S": -2, "M": [], "E": -1},
        {"S": 3, "M": [], "E": -2}
      ],
      "junctions": [{"x": 135, "y": 1320}, {"x": 135, "y": 1120}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_a_sensor2.frame_b) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 3}}}}
end
Flattened Source
dyad
"""
Sensor test with two free-falling bodies: validates all 6 sensor types.

Translates the "Sensors (two free falling bodies)" test from test_PlanarMechanics.jl.
Two bodies (m=1, I=1) start at the origin with zero velocity and fall under gravity.
A fixed base sits at the origin. Three absolute sensors are attached to body1, and six
relative sensors measure pairs (body1-vs-base and body1-vs-body2).
"""
test component SensorsTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 615, "x2": 120, "y2": 715, "rot": 0}
      },
      "tags": []
    }
  }
  body1 = Body(m = 1.0, I = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 715, "x2": 412, "y2": 815, "rot": 0}
      },
      "tags": []
    }
  }
  body2 = Body(m = 1.0, I = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 1270, "x2": 412, "y2": 1370, "rot": 0}
      },
      "tags": []
    }
  }
  abs_pos_sensor = AbsolutePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 295, "x2": 265, "y2": 395, "rot": 0}
      },
      "tags": []
    }
  }
  abs_v_sensor = AbsoluteVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 820, "x2": 265, "y2": 920, "rot": 0}
      },
      "tags": []
    }
  }
  abs_a_sensor = AbsoluteAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 160, "y1": 990, "x2": 260, "y2": 1090, "rot": 0}
      },
      "tags": []
    }
  }
  rel_pos_sensor1 = RelativePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 163.5,
          "y1": 415,
          "x2": 263.5,
          "y2": 515,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  rel_pos_sensor2 = RelativePosition(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1145, "x2": 265, "y2": 1245, "rot": 0}
      },
      "tags": []
    }
  }
  rel_v_sensor1 = RelativeVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 163.5,
          "y1": 615,
          "x2": 263.5,
          "y2": 715,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  rel_v_sensor2 = RelativeVelocity(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1395, "x2": 265, "y2": 1495, "rot": 0}
      },
      "tags": []
    }
  }
  rel_a_sensor1 = RelativeAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 163.5, "y1": 85, "x2": 263.5, "y2": 185, "rot": 0}
      },
      "tags": []
    }
  }
  rel_a_sensor2 = RelativeAcceleration(resolve_in_frame = ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 165, "y1": 1270, "x2": 265, "y2": 1370, "rot": 0}
      },
      "tags": []
    }
  }
  distance = MultibodyComponents.PlanarMechanics.Distance() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 370, "y1": 1070, "x2": 470, "y2": 1170, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # initial conditions for body1
  initial body1.r = [0, 0]
  initial body1.v = [0, 0]
  initial body1.phi = 0
  initial body1.w = 0
  # initial conditions for body2
  initial body2.r = [0, 0]
  initial body2.v = [0, 0]
  initial body2.phi = 0
  initial body2.w = 0
  # absolute sensors on body1
  connect(body1.frame_a, abs_pos_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 292, "y": 765},
            {"x": 292, "y": 565},
            {"x": 145, "y": 565},
            {"x": 145, "y": 345}
          ],
          "E": 2
        }
      ],
      "renderStyle": "standard"
    }
  }
  connect(body1.frame_a, abs_v_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 292, "y": 765}, {"x": 292, "y": 970}], "E": -1},
        {"S": -1, "M": [{"x": 145, "y": 870}], "E": 2}
      ],
      "junctions": [{"x": 145, "y": 970}],
      "renderStyle": "standard"
    }
  }
  connect(body1.frame_a, abs_a_sensor.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [
            {"x": 292, "y": 765},
            {"x": 292, "y": 970},
            {"x": 145, "y": 970},
            {"x": 145, "y": 1040}
          ],
          "E": 2
        }
      ],
      "renderStyle": "standard"
    }
  }
  # rel_pos_sensor1: frame_a=body1, frame_b=base
  connect(body1.frame_a, rel_pos_sensor1.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 292, "y": 765}], "E": -1},
        {"S": -1, "M": [{"x": 145, "y": 565}], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 292, "y": 565}, {"x": 145, "y": 465}],
      "renderStyle": "standard"
    }
  }
  connect(world.frame_b, rel_pos_sensor1.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {
          "S": -1,
          "M": [{"x": 135, "y": 20}, {"x": 302, "y": 20}, {"x": 302, "y": 465}],
          "E": 2
        }
      ],
      "junctions": [{"x": 135, "y": 665}],
      "renderStyle": "standard"
    }
  }
  # rel_pos_sensor2: frame_a=body1, frame_b=body2
  connect(body1.frame_a, rel_pos_sensor2.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 135, "y": 765}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 135, "y": 1195}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_pos_sensor2.frame_b, distance.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 282, "y": 1195}], "E": -2},
        {"S": -2, "M": [], "E": 2},
        {"S": 3, "M": [{"x": 500, "y": 1120}, {"x": 500, "y": 1195}], "E": -2}
      ],
      "junctions": [{"x": 282, "y": 1320}, {"x": 280, "y": 1195}],
      "renderStyle": "standard"
    }
  }
  # rel_v_sensor1: frame_a=base, frame_b=body1
  connect(world.frame_b, rel_v_sensor1.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(body1.frame_a, rel_v_sensor1.frame_b) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 292, "y": 765}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 292, "y": 665}],
      "renderStyle": "standard"
    }
  }
  # rel_v_sensor2: frame_a=body1, frame_b=body2
  connect(body1.frame_a, rel_v_sensor2.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 135, "y": 765}, {"x": 135, "y": 1445}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_v_sensor2.frame_b) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 282, "y": 1320}, {"x": 282, "y": 1445}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # rel_a_sensor1: frame_a=body1, frame_b=base
  connect(body1.frame_a, rel_a_sensor1.frame_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {
          "S": -1,
          "M": [{"x": 292, "y": 30}, {"x": 145, "y": 30}, {"x": 145, "y": 135}],
          "E": 2
        }
      ],
      "junctions": [{"x": 292, "y": 765}],
      "renderStyle": "standard"
    }
  }
  connect(world.frame_b, rel_a_sensor1.frame_b) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 135, "y": 665}], "E": -1},
        {"S": -1, "M": [{"x": 282, "y": 240}, {"x": 282, "y": 135}], "E": 2}
      ],
      "junctions": [{"x": 135, "y": 240}],
      "renderStyle": "standard"
    }
  }
  # rel_a_sensor2: frame_a=body1, frame_b=body2
  connect(rel_a_sensor2.frame_a, body1.frame_a, distance.frame_a) {
    "Dyad": {
      "edges": [
        {"S": -1, "M": [], "E": 1},
        {"S": 2, "M": [{"x": 135, "y": 765}], "E": -2},
        {"S": -2, "M": [], "E": -1},
        {"S": 3, "M": [], "E": -2}
      ],
      "junctions": [{"x": 135, "y": 1320}, {"x": 135, "y": 1120}],
      "renderStyle": "standard"
    }
  }
  connect(body2.frame_a, rel_a_sensor2.frame_b) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 3}}}}
end


Test Cases

julia
using MultibodyComponents
using DyadInterface: TransientAnalysis, rebuild_sol, ODEAlg
using ModelingToolkit: toggle_namespacing, get_initial_conditions, @named
using CSV, DataFrames, Plots

snapshotsdir = joinpath(dirname(dirname(pathof(MultibodyComponents))), "test", "snapshots")
<< @setup-block not executed in draft mode >>

Test Case case1

julia
@named model_case1 = MultibodyComponents.PlanarMechanics.SensorsTest()
model_case1 = toggle_namespacing(model_case1, false)

model_case1 = toggle_namespacing(model_case1, true)
result_case1 = TransientAnalysis(; model = model_case1, alg = ODEAlg.Auto(), start = 0e+0, stop = 3e+0, abstol=1e-6, reltol=1e-6)
sol_case1 = rebuild_sol(result_case1)
<< @setup-block not executed in draft mode >>