Skip to content

Commit f73b29f

Browse files
baggepinnenclaude
andcommitted
Consolidate steerable interface into RollingDyadBot3D
Instead of a separate SteerableDyadBot3D, RollingDyadBot3D itself now has individual torque inputs for the two wheels; models that drive both wheels with the same torque (AngleControlledDyadBot3D) place the halving gain outside the plant and connect it to both inputs. The x/x_dot outputs are now odometric (average wheel arc length), which coincides with the world-frame x coordinate when driving straight, so the planar-equivalence test is unaffected. Also add icons for ControlMixer and YawController, and make all diagram placement boxes square since non-square components render poorly in the GUI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 16c4eec commit f73b29f

18 files changed

Lines changed: 187 additions & 501 deletions

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@ Plant models:
1414
- `DyadBot3D`: three-dimensional model with two individually spinning wheels
1515
with slip-based ground contact, allowing the body to tilt.
1616
- `RollingDyadBot3D`: three-dimensional model with an ideal no-slip rolling
17-
wheel axle (`MultibodyComponents.RollingWheelSet`). Same interface and
18-
parameters as `PlanarDyadBot`; for motion in the vertical plane it is
19-
dynamically equivalent to the planar model.
20-
- `SteerableDyadBot3D`: like `RollingDyadBot3D` but with individually driven
17+
wheel axle (`MultibodyComponents.RollingWheelSet`) and individually driven
2118
wheels, so the robot can be steered by differential torque. Outputs
2219
odometric path position/velocity, tilt angle/rate and yaw (heading)
23-
angle/rate.
20+
angle/rate. With equal torque on both wheels it is dynamically equivalent
21+
to `PlanarDyadBot`.
2422

2523
Closed-loop models around the planar plant:
2624

@@ -41,7 +39,7 @@ Closed-loop models around the 3D plant:
4139
`AngleController` as `AngleControlledDyadBot`; with in-plane motion the
4240
closed-loop response is identical to the planar model
4341
(`test/test_stabilization.jl` verifies this).
44-
- `YawControlledDyadBot3D`: `SteerableDyadBot3D` with the balance/position
42+
- `YawControlledDyadBot3D`: `RollingDyadBot3D` with the balance/position
4543
cascade (`CascadeController`, position reference held at zero) and a
4644
separate `YawController` tracking a heading reference that steps at t = 5;
4745
a `ControlMixer` combines drive and yaw torque into the two wheel torque

assets/ControlMixer.svg

Lines changed: 21 additions & 0 deletions
Loading

assets/YawController.svg

Lines changed: 18 additions & 0 deletions
Loading

dyad/cascade_ff.dyad

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ metadata {
275275
}
276276
},
277277
"zoh": {
278-
"Dyad": {"placement": {"diagram": {"x1": 840, "y1": 450, "x2": 920, "y2": 550}}}
278+
"Dyad": {"placement": {"diagram": {"x1": 830, "y1": 450, "x2": 930, "y2": 550}}}
279279
},
280280
"clock": {
281281
"Dyad": {"placement": {"diagram": {"x1": 410, "y1": 790, "x2": 510, "y2": 890}}}

dyad/closed_loop.dyad

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ component AngleControlledDyadBot3D
242242
world = MultibodyComponents.World(g = 9.82, nominal_length = 0.1) {^world}
243243
plant = RollingDyadBot3D(phi0 = phi0) {^plant}
244244
controller = AngleController(k_angle = k_angle, Ti_angle = Ti_angle, Td_angle = Td_angle) {^controller}
245+
"Splits the motor torque equally between the two wheels"
246+
gain_half = BlockComponents.Math.Gain(k = 0.5) {^gain_half}
245247
"Proportional gain of the angle controller"
246248
parameter k_angle::Real = 0.487401
247249
"Integrator time constant of the angle controller"
@@ -251,7 +253,8 @@ component AngleControlledDyadBot3D
251253
"Initial tilt angle of the body"
252254
parameter phi0::Angle = 0.1
253255
relations
254-
connect(controller.torque, plant.torque) {^id4}
256+
connect(controller.torque, gain_half.u) {^id4}
257+
connect(gain_half.y, plant.torque_left, plant.torque_right) {^id6}
255258
connect(plant.theta, controller.measurement) {^id5}
256259
metadata {
257260
"Dyad": {
@@ -278,9 +281,18 @@ metadata {
278281
"controller": {
279282
"Dyad": {"placement": {"diagram": {"x1": 270, "y1": 420, "x2": 420, "y2": 570}}}
280283
},
281-
"id4": {
284+
"gain_half": {
285+
"Dyad": {"placement": {"diagram": {"x1": 450, "y1": 455, "x2": 530, "y2": 535}}}
286+
},
287+
"id4": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}},
288+
"id6": {
282289
"Dyad": {
283-
"edges": [{"S": 1, "M": [{"x": 503.75, "y": 495}, {"x": 503.75, "y": 500}], "E": 2}]
290+
"edges": [
291+
{"S": 1, "M": [], "E": -1},
292+
{"S": -1, "M": [{"x": 545, "y": 460}], "E": 2},
293+
{"S": 3, "M": [{"x": 545, "y": 530}], "E": -1}
294+
],
295+
"junctions": [{"x": 545, "y": 495}]
284296
}
285297
},
286298
"id5": {
@@ -745,7 +757,7 @@ metadata {
745757
"Dyad": {"placement": {"diagram": {"x1": 60, "y1": 450, "x2": 160, "y2": 550}}}
746758
},
747759
"controller": {
748-
"Dyad": {"placement": {"diagram": {"x1": 240, "y1": 410, "x2": 480, "y2": 600}}}
760+
"Dyad": {"placement": {"diagram": {"x1": 265, "y1": 410, "x2": 455, "y2": 600}}}
749761
},
750762
"id7": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}},
751763
"id8": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}},
@@ -856,7 +868,7 @@ metadata {
856868
"Dyad": {"placement": {"diagram": {"x1": 120, "y1": 430, "x2": 220, "y2": 530}}}
857869
},
858870
"controller": {
859-
"Dyad": {"placement": {"diagram": {"x1": 280, "y1": 390, "x2": 510, "y2": 570}}}
871+
"Dyad": {"placement": {"diagram": {"x1": 305, "y1": 390, "x2": 485, "y2": 570}}}
860872
},
861873
"id7": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}},
862874
"id8": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}},

dyad/lqg.dyad

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ metadata {
288288
"mux": {
289289
"Dyad": {
290290
"placement": {
291-
"diagram": {"iconName": "default", "x1": 90, "y1": 450, "x2": 170, "y2": 550, "rot": 0}
291+
"diagram": {"iconName": "default", "x1": 80, "y1": 450, "x2": 180, "y2": 550, "rot": 0}
292292
},
293293
"tags": []
294294
}

0 commit comments

Comments
 (0)