PlanarMechanics.examples.two_joint_robot.TwoJointPlanarRobotPIDCartesian
Planar 2-joint serial robot tracking a jerk-bounded reference specified in Cartesian tool-flange space. A single inverse instance of the arm solves both the inverse kinematics and the inverse dynamics: its tool flange is constrained to the Cartesian reference without applying force there, while its joints are actuated by unknown torques (InverseKinematicsDriver). The unknown torques constitute the computed-torque feedforward, and joint angle and velocity references for the per-joint JointController feedback controllers are measured on the same inverse instance.
Usage
MultibodyComponents.PlanarMechanics.examples.two_joint_robot.TwoJointPlanarRobotPIDCartesian(phi1_0=1.5707963267948966, phi2_0=-1.5707963267948966, k1=100.0, k2=50.0, Ti1=0.1, Ti2=0.1, Td1=1.0, Td2=1.0, y_max=200.0, T=0.005)
Parameters:
| Name | Description | Units | Default value |
|---|---|---|---|
phi1_0 | Initial angle of joint 1, consistent with the Cartesian trajectory start point | rad | 1.5707963267948966 |
phi2_0 | Initial angle of joint 2, consistent with the Cartesian trajectory start point | rad | -1.5707963267948966 |
k1 | Proportional gain for joint 1 | – | 100.0 |
k2 | Proportional gain for joint 2 | – | 50.0 |
Ti1 | Integrator time constant for joint 1 | s | 0.1 |
Ti2 | Integrator time constant for joint 2 | s | 0.1 |
Td1 | Derivative gain for joint 1 | – | 1.0 |
Td2 | Derivative gain for joint 2 | – | 1.0 |
y_max | Maximum torque of both controllers | – | 200.0 |
T | Velocity filter time constant of both controllers | s | 0.005 |
Behavior
Source
"Planar 2-joint serial robot tracking a jerk-bounded reference specified in Cartesian tool-flange space. A single inverse instance of the arm solves both the inverse kinematics and the inverse dynamics: its tool flange is constrained to the Cartesian reference without applying force there, while its joints are actuated by unknown torques (InverseKinematicsDriver). The unknown torques constitute the computed-torque feedforward, and joint angle and velocity references for the per-joint JointController feedback controllers are measured on the same inverse instance."
example component TwoJointPlanarRobotPIDCartesian
world = MultibodyComponents.PlanarMechanics.World() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 1170, "y1": 220, "x2": 1070, "y2": 320, "rot": 0}
},
"tags": []
}
}
robot = TwoJointPlanarArm(final phi1_start = phi1_0, final phi2_start = phi2_0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 930, "y1": 220, "x2": 1030, "y2": 320, "rot": 0}
},
"tags": []
}
}
"Inverse model of the robot used to solve the inverse kinematics and compute torque feedforward"
inverse_robot = TwoJointPlanarArm(render = false, final phi1_start = phi1_0, final phi2_start = phi2_0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 930, "y1": 30, "x2": 1030, "y2": -70, "rot": 0}
},
"tags": []
}
}
"Cartesian tool-position reference trajectory; phase synchronization makes the path an exact straight line"
ptp = MultibodyComponents.KinematicPTPBoundedJerk(nout = 2, q0 = [1.0, 1.0], q1 = [0.5, 1.3], qd_max = [1.0, 1.0], qdd_max = [2.0, 2.0], qddd_max = [10.0, 10.0], phase_synchronization = true) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 110, "y1": -40, "x2": 210, "y2": 60, "rot": 0}
},
"tags": []
}
}
ctrl1 = JointController(index = 1, final k = k1, final Ti = Ti1, final Td = Td1, y_max = y_max, T = T) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 470, "y1": 150, "x2": 570, "y2": 250, "rot": 0}
},
"tags": []
}
}
ctrl2 = JointController(index = 2, final k = k2, final Ti = Ti2, final Td = Td2, y_max = y_max, T = T) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 530, "y1": 610, "x2": 630, "y2": 710, "rot": 0}
},
"tags": []
}
}
ik = InverseKinematicsDriver() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 490, "y1": 30, "x2": 590, "y2": -70, "rot": 0}
},
"tags": []
}
}
torque1 = RotationalComponents.Sources.TorqueSource() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 730, "y1": 150, "x2": 830, "y2": 250, "rot": 0}
},
"tags": []
}
}
torque2 = RotationalComponents.Sources.TorqueSource() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 730, "y1": 710, "x2": 830, "y2": 610, "rot": 0}
},
"tags": []
}
}
angle1 = RotationalComponents.Sensors.AngleSensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 230, "x2": 360, "y2": 330, "rot": 0}
},
"tags": []
}
}
angle2 = RotationalComponents.Sensors.AngleSensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 700, "x2": 360, "y2": 800, "rot": 0}
},
"tags": []
}
}
vel1 = RotationalComponents.Sensors.VelocitySensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 360, "x2": 360, "y2": 460, "rot": 0}
},
"tags": []
}
}
vel2 = RotationalComponents.Sensors.VelocitySensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 820, "x2": 360, "y2": 920, "rot": 0}
},
"tags": []
}
}
fixed = RotationalComponents.Components.Fixed() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 800, "y1": 430, "x2": 900, "y2": 530, "rot": 270}
},
"tags": []
}
}
fixed1 = RotationalComponents.Components.Fixed() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 520, "y1": -130, "x2": 560, "y2": -170, "rot": 0}
},
"tags": []
}
}
"Initial angle of joint 1, consistent with the Cartesian trajectory start point"
parameter phi1_0::Angle = 1.5707963267948966
"Initial angle of joint 2, consistent with the Cartesian trajectory start point"
parameter phi2_0::Angle = -1.5707963267948966
"Proportional gain for joint 1"
parameter k1::Real = 100.0
"Proportional gain for joint 2"
parameter k2::Real = 50.0
"Integrator time constant for joint 1"
parameter Ti1::Time = 0.1
"Integrator time constant for joint 2"
parameter Ti2::Time = 0.1
"Derivative gain for joint 1"
parameter Td1::Real = 1.0
"Derivative gain for joint 2"
parameter Td2::Real = 1.0
"Maximum torque of both controllers"
parameter y_max::Real = 200.0
"Velocity filter time constant of both controllers"
parameter T::Time = 0.005
relations
connect(robot.flange1, torque1.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 900, "y": 244}, {"x": 900, "y": 200}], "E": 2}],
"renderStyle": "standard"
}
}
connect(torque1.support, fixed.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 780, "y": 480}], "E": 2}],
"renderStyle": "standard"
}
}
connect(angle1.phi, ctrl1.phi) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 310, "y": 280}, {"x": 310, "y": 220}], "E": 2}],
"renderStyle": "standard"
}
}
connect(vel1.w, ctrl1.w) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 290, "y": 410}, {"x": 290, "y": 200}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ctrl1.y, torque1.tau) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(robot.flange2, torque2.spline, vel2.spline, angle2.spline) {
"Dyad": {
"edges": [
{"S": -1, "M": [{"x": 920, "y": 295}], "E": 1},
{"S": 2, "M": [], "E": -1},
{"S": 3, "M": [], "E": -2},
{"S": -2, "M": [{"x": 920, "y": 870}], "E": -1},
{"S": 4, "M": [{"x": 500, "y": 750}], "E": -2}
],
"junctions": [{"x": 920, "y": 660}, {"x": 500, "y": 870}],
"renderStyle": "standard"
}
}
connect(torque2.support, fixed.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 780, "y": 480}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.q, ctrl2.q, ctrl1.q) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 504, "y": 90}, {"x": 270, "y": 90}], "E": -1},
{"S": -1, "M": [{"x": 270, "y": 620}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 270, "y": 160}],
"renderStyle": "standard"
}
}
connect(ik.qd, ctrl2.qd, ctrl1.qd) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 528, "y": 71}, {"x": 250, "y": 71}], "E": -1},
{"S": -1, "M": [{"x": 250, "y": 640}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 250, "y": 180}],
"renderStyle": "standard"
}
}
connect(angle2.phi, ctrl2.phi) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 270, "y": 750}, {"x": 270, "y": 680}], "E": 2}],
"renderStyle": "standard"
}
}
connect(vel2.w, ctrl2.w) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 250, "y": 870}, {"x": 250, "y": 660}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ctrl2.y, torque2.tau) {}
connect(angle1.spline, robot.flange1, vel1.spline) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 900, "y": 280}, {"x": 900, "y": 244}], "E": 2},
{"S": 3, "M": [{"x": 500, "y": 410}], "E": -1}
],
"junctions": [{"x": 500, "y": 280}],
"renderStyle": "standard"
}
}
connect(ptp.q, ik.r_ref) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 350, "y": -25}, {"x": 350, "y": -21}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.frame, inverse_robot.frame_tool) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(ik.flange1, inverse_robot.flange1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 640, "y": 8}, {"x": 640, "y": 6}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.flange2, inverse_robot.flange2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 640, "y": -46}, {"x": 640, "y": -45}], "E": 2}],
"renderStyle": "standard"
}
}
connect(fixed1.spline, ik.support) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(ik.tau1, ctrl1.u_ff) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 552, "y": 108}, {"x": 520, "y": 108}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.tau2, ctrl2.u_ff) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 576, "y": 570}, {"x": 580, "y": 570}], "E": 2}],
"renderStyle": "standard"
}
}
connect(robot.frame_a, world.frame_b, inverse_robot.frame_a) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": 2}, {"S": 2, "M": [{"x": 1070, "y": -20}], "E": 3}],
"renderStyle": "standard"
}
}
u1: analysis_point(ctrl1.y, torque1.tau)
u2: analysis_point(ctrl2.y, torque2.tau)
ya1: analysis_point(angle1.phi, ctrl1.phi)
yv1: analysis_point(vel1.w, ctrl1.w)
ya2: analysis_point(angle2.phi, ctrl2.phi)
yv2: analysis_point(vel2.w, ctrl2.w)
uff1: analysis_point(ik.tau1, ctrl1.u_ff)
uff2: analysis_point(ik.tau2, ctrl2.u_ff)
endFlattened Source
"Planar 2-joint serial robot tracking a jerk-bounded reference specified in Cartesian tool-flange space. A single inverse instance of the arm solves both the inverse kinematics and the inverse dynamics: its tool flange is constrained to the Cartesian reference without applying force there, while its joints are actuated by unknown torques (InverseKinematicsDriver). The unknown torques constitute the computed-torque feedforward, and joint angle and velocity references for the per-joint JointController feedback controllers are measured on the same inverse instance."
example component TwoJointPlanarRobotPIDCartesian
world = MultibodyComponents.PlanarMechanics.World() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 1170, "y1": 220, "x2": 1070, "y2": 320, "rot": 0}
},
"tags": []
}
}
robot = TwoJointPlanarArm(final phi1_start = phi1_0, final phi2_start = phi2_0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 930, "y1": 220, "x2": 1030, "y2": 320, "rot": 0}
},
"tags": []
}
}
"Inverse model of the robot used to solve the inverse kinematics and compute torque feedforward"
inverse_robot = TwoJointPlanarArm(render = false, final phi1_start = phi1_0, final phi2_start = phi2_0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 930, "y1": 30, "x2": 1030, "y2": -70, "rot": 0}
},
"tags": []
}
}
"Cartesian tool-position reference trajectory; phase synchronization makes the path an exact straight line"
ptp = MultibodyComponents.KinematicPTPBoundedJerk(nout = 2, q0 = [1.0, 1.0], q1 = [0.5, 1.3], qd_max = [1.0, 1.0], qdd_max = [2.0, 2.0], qddd_max = [10.0, 10.0], phase_synchronization = true) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 110, "y1": -40, "x2": 210, "y2": 60, "rot": 0}
},
"tags": []
}
}
ctrl1 = JointController(index = 1, final k = k1, final Ti = Ti1, final Td = Td1, y_max = y_max, T = T) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 470, "y1": 150, "x2": 570, "y2": 250, "rot": 0}
},
"tags": []
}
}
ctrl2 = JointController(index = 2, final k = k2, final Ti = Ti2, final Td = Td2, y_max = y_max, T = T) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 530, "y1": 610, "x2": 630, "y2": 710, "rot": 0}
},
"tags": []
}
}
ik = InverseKinematicsDriver() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 490, "y1": 30, "x2": 590, "y2": -70, "rot": 0}
},
"tags": []
}
}
torque1 = RotationalComponents.Sources.TorqueSource() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 730, "y1": 150, "x2": 830, "y2": 250, "rot": 0}
},
"tags": []
}
}
torque2 = RotationalComponents.Sources.TorqueSource() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 730, "y1": 710, "x2": 830, "y2": 610, "rot": 0}
},
"tags": []
}
}
angle1 = RotationalComponents.Sensors.AngleSensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 230, "x2": 360, "y2": 330, "rot": 0}
},
"tags": []
}
}
angle2 = RotationalComponents.Sensors.AngleSensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 700, "x2": 360, "y2": 800, "rot": 0}
},
"tags": []
}
}
vel1 = RotationalComponents.Sensors.VelocitySensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 360, "x2": 360, "y2": 460, "rot": 0}
},
"tags": []
}
}
vel2 = RotationalComponents.Sensors.VelocitySensor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 460, "y1": 820, "x2": 360, "y2": 920, "rot": 0}
},
"tags": []
}
}
fixed = RotationalComponents.Components.Fixed() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 800, "y1": 430, "x2": 900, "y2": 530, "rot": 270}
},
"tags": []
}
}
fixed1 = RotationalComponents.Components.Fixed() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 520, "y1": -130, "x2": 560, "y2": -170, "rot": 0}
},
"tags": []
}
}
"Initial angle of joint 1, consistent with the Cartesian trajectory start point"
parameter phi1_0::Angle = 1.5707963267948966
"Initial angle of joint 2, consistent with the Cartesian trajectory start point"
parameter phi2_0::Angle = -1.5707963267948966
"Proportional gain for joint 1"
parameter k1::Real = 100.0
"Proportional gain for joint 2"
parameter k2::Real = 50.0
"Integrator time constant for joint 1"
parameter Ti1::Time = 0.1
"Integrator time constant for joint 2"
parameter Ti2::Time = 0.1
"Derivative gain for joint 1"
parameter Td1::Real = 1.0
"Derivative gain for joint 2"
parameter Td2::Real = 1.0
"Maximum torque of both controllers"
parameter y_max::Real = 200.0
"Velocity filter time constant of both controllers"
parameter T::Time = 0.005
relations
connect(robot.flange1, torque1.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 900, "y": 244}, {"x": 900, "y": 200}], "E": 2}],
"renderStyle": "standard"
}
}
connect(torque1.support, fixed.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 780, "y": 480}], "E": 2}],
"renderStyle": "standard"
}
}
connect(angle1.phi, ctrl1.phi) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 310, "y": 280}, {"x": 310, "y": 220}], "E": 2}],
"renderStyle": "standard"
}
}
connect(vel1.w, ctrl1.w) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 290, "y": 410}, {"x": 290, "y": 200}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ctrl1.y, torque1.tau) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(robot.flange2, torque2.spline, vel2.spline, angle2.spline) {
"Dyad": {
"edges": [
{"S": -1, "M": [{"x": 920, "y": 295}], "E": 1},
{"S": 2, "M": [], "E": -1},
{"S": 3, "M": [], "E": -2},
{"S": -2, "M": [{"x": 920, "y": 870}], "E": -1},
{"S": 4, "M": [{"x": 500, "y": 750}], "E": -2}
],
"junctions": [{"x": 920, "y": 660}, {"x": 500, "y": 870}],
"renderStyle": "standard"
}
}
connect(torque2.support, fixed.spline) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 780, "y": 480}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.q, ctrl2.q, ctrl1.q) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 504, "y": 90}, {"x": 270, "y": 90}], "E": -1},
{"S": -1, "M": [{"x": 270, "y": 620}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 270, "y": 160}],
"renderStyle": "standard"
}
}
connect(ik.qd, ctrl2.qd, ctrl1.qd) {
"Dyad": {
"edges": [
{"S": 1, "M": [{"x": 528, "y": 71}, {"x": 250, "y": 71}], "E": -1},
{"S": -1, "M": [{"x": 250, "y": 640}], "E": 2},
{"S": 3, "M": [], "E": -1}
],
"junctions": [{"x": 250, "y": 180}],
"renderStyle": "standard"
}
}
connect(angle2.phi, ctrl2.phi) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 270, "y": 750}, {"x": 270, "y": 680}], "E": 2}],
"renderStyle": "standard"
}
}
connect(vel2.w, ctrl2.w) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 250, "y": 870}, {"x": 250, "y": 660}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ctrl2.y, torque2.tau) {}
connect(angle1.spline, robot.flange1, vel1.spline) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [{"x": 900, "y": 280}, {"x": 900, "y": 244}], "E": 2},
{"S": 3, "M": [{"x": 500, "y": 410}], "E": -1}
],
"junctions": [{"x": 500, "y": 280}],
"renderStyle": "standard"
}
}
connect(ptp.q, ik.r_ref) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 350, "y": -25}, {"x": 350, "y": -21}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.frame, inverse_robot.frame_tool) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(ik.flange1, inverse_robot.flange1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 640, "y": 8}, {"x": 640, "y": 6}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.flange2, inverse_robot.flange2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 640, "y": -46}, {"x": 640, "y": -45}], "E": 2}],
"renderStyle": "standard"
}
}
connect(fixed1.spline, ik.support) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(ik.tau1, ctrl1.u_ff) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 552, "y": 108}, {"x": 520, "y": 108}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ik.tau2, ctrl2.u_ff) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 576, "y": 570}, {"x": 580, "y": 570}], "E": 2}],
"renderStyle": "standard"
}
}
connect(robot.frame_a, world.frame_b, inverse_robot.frame_a) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": 2}, {"S": 2, "M": [{"x": 1070, "y": -20}], "E": 3}],
"renderStyle": "standard"
}
}
u1: analysis_point(ctrl1.y, torque1.tau)
u2: analysis_point(ctrl2.y, torque2.tau)
ya1: analysis_point(angle1.phi, ctrl1.phi)
yv1: analysis_point(vel1.w, ctrl1.w)
ya2: analysis_point(angle2.phi, ctrl2.phi)
yv2: analysis_point(vel2.w, ctrl2.w)
uff1: analysis_point(ik.tau1, ctrl1.u_ff)
uff2: analysis_point(ik.tau2, ctrl2.u_ff)
metadata {}
endTest Cases
No test cases defined.
Related
Examples
Experiments
Analyses