LIBRARY
PlanarMechanics.examples.dyad_balans.DyadBalans2D
This example models a wheeled balancing robot with a cascade control system stabilizing the angle in the inner loop and the position in the outer loop.
See also DyadBalans2DFF for a version with a feedforward generator.
Usage
MultibodyComponents.PlanarMechanics.examples.dyad_balans.DyadBalans2D(M=0.1, m=0.05, R=0.04, L=0.08, Ib=2.5e-4, Iw=1e-4, d=0.0001)
Parameters:
| Name | Description | Units | Default value |
|---|---|---|---|
M | Body mass | kg | 0.1 |
m | Wheel mass | kg | 0.05 |
R | Wheel radius | m | 0.04 |
L | Distance from wheel axis to body center of mass | m | 0.08 |
Ib | Body moment of inertia | kg.m2 | 0.00025 |
Iw | Wheel + motor moment of inertia | kg.m2 | 0.0001 |
d | Motor viscous friction | N.m.s/rad | 0.0001 |
Behavior
Source
dyad
"""
This example models a wheeled balancing robot with a cascade control system stabilizing the angle in the inner loop and the position in the outer loop.
See also DyadBalans2DFF for a version with a feedforward generator.
"""
example component DyadBalans2D
world = MultibodyComponents.PlanarMechanics.World(g = 9.82) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 110, "y1": 420, "x2": 10, "y2": 320, "rot": 180}
},
"tags": []
}
}
firstorder = BlockComponents.Continuous.FirstOrder(T = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -570, "y1": 600, "x2": -470, "y2": 700, "rot": 0}
},
"tags": []
}
}
firstorder1 = BlockComponents.Continuous.FirstOrder(T = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -430, "y1": 600, "x2": -330, "y2": 700, "rot": 0}
},
"tags": []
}
}
wheelJoint = MultibodyComponents.PlanarMechanics.OneDOFRollingWheelJoint(radius = R) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 200, "y1": 700, "x2": 460, "y2": 960, "rot": 0}
},
"tags": []
}
}
constant2 = BlockComponents.Sources.Constant(k = 0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 50, "y1": 500, "x2": 110, "y2": 560, "rot": 90}
},
"tags": []
}
}
IMU = MultibodyComponents.PlanarMechanics.AbsolutePosition(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 560, "x2": 560, "y2": 660, "rot": 0}
},
"tags": []
}
}
square = BlockComponents.Sources.Square(amplitude = 0.05, frequency = 1 / 10) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -720, "y1": 600, "x2": -620, "y2": 700, "rot": 0}
},
"tags": []
}
}
wheelinertia = MultibodyComponents.PlanarMechanics.Body(I = Iw, radius = 0.01, m = m) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 390, "y1": 680, "x2": 530, "y2": 820, "rot": 0}
},
"tags": []
}
}
simplemotor = MultibodyComponents.PlanarMechanics.examples.dyad_balans.SimpleMotor(d = d) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 280, "y1": 620, "x2": 380, "y2": 720, "rot": 90}
},
"tags": []
}
}
constant1 = BlockComponents.Sources.Constant(k = 0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -240, "y1": 500, "x2": -180, "y2": 560, "rot": 90}
},
"tags": []
}
}
angle_controller = BlockComponents.Continuous.LimPID(k = 0.487401, Ti = 0.0587352, Td = 0.0420526, Nd = 119.368, y_max = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 30, "y1": 620, "x2": 130, "y2": 720, "rot": 0}
},
"tags": []
}
}
pos_controller = BlockComponents.Continuous.LimPID(k = 0.0666576, Ti = 5.25024, Td = 4.81393, Nd = 4.76616, wd = 1, wp = 1, y_max = deg2rad(25.0)) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -260, "y1": 620, "x2": -160, "y2": 720, "rot": 0}
},
"tags": []
}
}
gain = BlockComponents.Math.Gain(k = -1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 170, "y1": 630, "x2": 250, "y2": 710, "rot": 0}
},
"tags": []
}
}
gain1 = BlockComponents.Math.Gain(k = -1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -120, "y1": 630, "x2": -40, "y2": 710, "rot": 0}
},
"tags": []
}
}
body_mass = MultibodyComponents.PlanarMechanics.BodyShape(color = [0.2, 0.2, 0.2, 0.9], r = [0, 2 * L], r_cm = [0, L], m = M, I = Ib, radius = 0.03) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 180, "y1": 250, "x2": 480, "y2": 550, "rot": 270}
},
"tags": []
}
}
"Body mass"
parameter M::Mass = 0.1
"Wheel mass"
parameter m::Mass = 0.05
"Wheel radius"
parameter R::Length = 0.04
"Distance from wheel axis to body center of mass"
parameter L::Length = 0.08
"Body moment of inertia"
parameter Ib::Inertia = 2.5e-4
"Wheel + motor moment of inertia"
parameter Iw::Inertia = 1e-4
"Motor viscous friction"
parameter d::RotationalDampingConstant = 0.0001
relations
initial body_mass.body.phi = 0.1
initial body_mass.body.w = 0.0
initial wheelinertia.phi = 0.0
initial wheelinertia.w = 0.0
#initial wheelJoint.x = 0.0
#initial wheelJoint.v = 0.0
initial firstorder.x = 0.0
initial firstorder1.x = 0.0
connect(square.y, firstorder.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(firstorder.y, firstorder1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(wheelinertia.frame_a, wheelJoint.frame_a) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 330, "y": 750}], "E": 2}],
"renderStyle": "standard"
}
}
connect(simplemotor.frame_b, wheelJoint.frame_a) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
connect(simplemotor.frame_a, IMU.frame_a, body_mass.frame_a) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 330, "y": 610}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 330, "y": 610}],
"renderStyle": "standard"
}
}
y: analysis_point(IMU.phi, angle_controller.u_m)
y2: analysis_point(IMU.x, pos_controller.u_m)
u: analysis_point(angle_controller.y, gain.u)
u2: analysis_point(pos_controller.y, gain1.u)
connect(IMU.phi, angle_controller.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 660, "y": 641},
{"x": 660, "y": 1040},
{"x": 0, "y": 1040},
{"x": 0, "y": 693}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
connect(IMU.x, pos_controller.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 710, "y": 580},
{"x": 710, "y": 1090},
{"x": -300, "y": 1090},
{"x": -300, "y": 693}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
connect(pos_controller.y, gain1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -134.25, "y": 671}, {"x": -134.25, "y": 670}], "E": 2}],
"renderStyle": "standard"
}
}
connect(gain1.y, angle_controller.u_s) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -10, "y": 670}, {"x": -10, "y": 647}], "E": 2}],
"renderStyle": "standard"
}
}
connect(firstorder1.y, pos_controller.u_s) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -295, "y": 650}, {"x": -295, "y": 647}], "E": 2}],
"renderStyle": "standard"
}
}
connect(gain.y, simplemotor.torqueinput) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(angle_controller.y, gain.u) {
"Dyad": {
"renderStyle": "standard",
"edges": [{"S": 1, "M": [{"x": 150.5, "y": 671}, {"x": 150.5, "y": 670}], "E": 2}]
}
}
connect(constant1.y, pos_controller.u_ff) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(constant2.y, angle_controller.u_ff) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 50}}}}
endFlattened Source
dyad
"""
This example models a wheeled balancing robot with a cascade control system stabilizing the angle in the inner loop and the position in the outer loop.
See also DyadBalans2DFF for a version with a feedforward generator.
"""
example component DyadBalans2D
world = MultibodyComponents.PlanarMechanics.World(g = 9.82) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 110, "y1": 420, "x2": 10, "y2": 320, "rot": 180}
},
"tags": []
}
}
firstorder = BlockComponents.Continuous.FirstOrder(T = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -570, "y1": 600, "x2": -470, "y2": 700, "rot": 0}
},
"tags": []
}
}
firstorder1 = BlockComponents.Continuous.FirstOrder(T = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -430, "y1": 600, "x2": -330, "y2": 700, "rot": 0}
},
"tags": []
}
}
wheelJoint = MultibodyComponents.PlanarMechanics.OneDOFRollingWheelJoint(radius = R) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 200, "y1": 700, "x2": 460, "y2": 960, "rot": 0}
},
"tags": []
}
}
constant2 = BlockComponents.Sources.Constant(k = 0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 50, "y1": 500, "x2": 110, "y2": 560, "rot": 90}
},
"tags": []
}
}
IMU = MultibodyComponents.PlanarMechanics.AbsolutePosition(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 560, "x2": 560, "y2": 660, "rot": 0}
},
"tags": []
}
}
square = BlockComponents.Sources.Square(amplitude = 0.05, frequency = 1 / 10) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -720, "y1": 600, "x2": -620, "y2": 700, "rot": 0}
},
"tags": []
}
}
wheelinertia = MultibodyComponents.PlanarMechanics.Body(I = Iw, radius = 0.01, m = m) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 390, "y1": 680, "x2": 530, "y2": 820, "rot": 0}
},
"tags": []
}
}
simplemotor = MultibodyComponents.PlanarMechanics.examples.dyad_balans.SimpleMotor(d = d) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 280, "y1": 620, "x2": 380, "y2": 720, "rot": 90}
},
"tags": []
}
}
constant1 = BlockComponents.Sources.Constant(k = 0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -240, "y1": 500, "x2": -180, "y2": 560, "rot": 90}
},
"tags": []
}
}
angle_controller = BlockComponents.Continuous.LimPID(k = 0.487401, Ti = 0.0587352, Td = 0.0420526, Nd = 119.368, y_max = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 30, "y1": 620, "x2": 130, "y2": 720, "rot": 0}
},
"tags": []
}
}
pos_controller = BlockComponents.Continuous.LimPID(k = 0.0666576, Ti = 5.25024, Td = 4.81393, Nd = 4.76616, wd = 1, wp = 1, y_max = deg2rad(25.0)) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -260, "y1": 620, "x2": -160, "y2": 720, "rot": 0}
},
"tags": []
}
}
gain = BlockComponents.Math.Gain(k = -1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 170, "y1": 630, "x2": 250, "y2": 710, "rot": 0}
},
"tags": []
}
}
gain1 = BlockComponents.Math.Gain(k = -1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": -120, "y1": 630, "x2": -40, "y2": 710, "rot": 0}
},
"tags": []
}
}
body_mass = MultibodyComponents.PlanarMechanics.BodyShape(color = [0.2, 0.2, 0.2, 0.9], r = [0, 2 * L], r_cm = [0, L], m = M, I = Ib, radius = 0.03) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 180, "y1": 250, "x2": 480, "y2": 550, "rot": 270}
},
"tags": []
}
}
"Body mass"
parameter M::Mass = 0.1
"Wheel mass"
parameter m::Mass = 0.05
"Wheel radius"
parameter R::Length = 0.04
"Distance from wheel axis to body center of mass"
parameter L::Length = 0.08
"Body moment of inertia"
parameter Ib::Inertia = 2.5e-4
"Wheel + motor moment of inertia"
parameter Iw::Inertia = 1e-4
"Motor viscous friction"
parameter d::RotationalDampingConstant = 0.0001
relations
initial body_mass.body.phi = 0.1
initial body_mass.body.w = 0.0
initial wheelinertia.phi = 0.0
initial wheelinertia.w = 0.0
#initial wheelJoint.x = 0.0
#initial wheelJoint.v = 0.0
initial firstorder.x = 0.0
initial firstorder1.x = 0.0
connect(square.y, firstorder.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(firstorder.y, firstorder1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(wheelinertia.frame_a, wheelJoint.frame_a) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 330, "y": 750}], "E": 2}],
"renderStyle": "standard"
}
}
connect(simplemotor.frame_b, wheelJoint.frame_a) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
connect(simplemotor.frame_a, IMU.frame_a, body_mass.frame_a) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 330, "y": 610}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 330, "y": 610}],
"renderStyle": "standard"
}
}
y: analysis_point(IMU.phi, angle_controller.u_m)
y2: analysis_point(IMU.x, pos_controller.u_m)
u: analysis_point(angle_controller.y, gain.u)
u2: analysis_point(pos_controller.y, gain1.u)
connect(IMU.phi, angle_controller.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 660, "y": 641},
{"x": 660, "y": 1040},
{"x": 0, "y": 1040},
{"x": 0, "y": 693}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
connect(IMU.x, pos_controller.u_m) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 710, "y": 580},
{"x": 710, "y": 1090},
{"x": -300, "y": 1090},
{"x": -300, "y": 693}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
connect(pos_controller.y, gain1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -134.25, "y": 671}, {"x": -134.25, "y": 670}], "E": 2}],
"renderStyle": "standard"
}
}
connect(gain1.y, angle_controller.u_s) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -10, "y": 670}, {"x": -10, "y": 647}], "E": 2}],
"renderStyle": "standard"
}
}
connect(firstorder1.y, pos_controller.u_s) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": -295, "y": 650}, {"x": -295, "y": 647}], "E": 2}],
"renderStyle": "standard"
}
}
connect(gain.y, simplemotor.torqueinput) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(angle_controller.y, gain.u) {
"Dyad": {
"renderStyle": "standard",
"edges": [{"S": 1, "M": [{"x": 150.5, "y": 671}, {"x": 150.5, "y": 670}], "E": 2}]
}
}
connect(constant1.y, pos_controller.u_ff) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(constant2.y, angle_controller.u_ff) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 50}}}}
endTest Cases
Test Case case1
Related
Examples
Experiments
Analyses