Skip to content
LIBRARY
PlanarMechanics.examples.dyad_balans.DyadBalans2DFF.md

PlanarMechanics.examples.dyad_balans.DyadBalans2DFF

This is a continuation of the DyadBalans2D example, adding a feedforward generator computed using DyadControlSystems.feedforward_generator in the script compute_feedforward.jl. The feedforward generator provides a filtered position reference to the outer controller as well as an angle feedforward reference for the inner controller and a direct torque feedforward signal to be applied directly to the torque input. Both inner feedforward signals are connected to the u_ff port on the LimPID components.

Usage

MultibodyComponents.PlanarMechanics.examples.dyad_balans.DyadBalans2DFF(M=0.1, m=0.05, R=0.04, L=0.08, Ib=2.5e-4, Iw=1e-4)

Parameters:

NameDescriptionUnitsDefault value
MBody masskg0.1
mWheel masskg0.05
RWheel radiusm0.04
LDistance from wheel axis to body center of massm0.08
IbBody moment of inertiakg.m20.00025
IwWheel + motor moment of inertiakg.m20.0001

Behavior

[AnalysisPoint(IMU.phi(t),y,[angle_controller.u_m(t)])AnalysisPoint(IMU.x(t),y2,[pos_controller.u_m(t)])AnalysisPoint(angle_controller.y(t),u,[gain.u(t)])AnalysisPoint(pos_controller.y(t),u2,[gain1.u(t)])connect(wheelinertia+framea,wheelJoint+framea)connect(simplemotor+frameb,wheelJoint+framea)connect(IMU+phi,anglecontroller+u_m)connect(IMU+x,poscontroller+u_m)connect(poscontroller+y,gain1+u)connect(gain1+y,anglecontroller+u_s)connect(gain+y,simplemotor+torqueinput)connect(anglecontroller+y,gain+u)connect(posref+y,poscontroller+u_s)connect(refgen+y,posref+u)connect(refgen+y,torqueff+u,angleff+u)connect(bodymass+frame_a,simplemotor+framea,IMU+framea)connect(square+y,mux1+u)connect(mux1+y,refgen+u)connect(angleff+y,gain2+u)connect(gain2+y,poscontroller+u_ff)connect(torqueff+y,anglecontroller+u_ff)world.frameb.x(t)=0world.frameb.y(t)=0world.frameb.phi(t)=0connect(frameb,framevis+frame_a)world.framevis.phi(t)=world.framevis.frame_a.phi(t)world.framevis.x_shape.r(t)=arrayliteral([3],world.framevis.frame_a.x(t),world.framevis.frame_a.y(t),world.framevis.z_position)world.framevis.x_shape.R(t)=arrayliteral([33],cos(world.framevis.phi(t)),sin(world.framevis.phi(t)),0,sin(world.framevis.phi(t)),cos(world.framevis.phi(t)),0,0,0,1)world.framevis.x_shape.r_shape(t)=[000]world.framevis.x_shape.length_direction(t)=[100]world.framevis.x_shape.width_direction(t)=[001]world.framevis.x_shape.length(t)=world.framevis.axis_lengthworld.framevis.x_shape.width(t)=2world.framevis.axis_radiusworld.framevis.x_shape.height(t)=2world.framevis.axis_radiusworld.framevis.y_shape.r(t)=arrayliteral([3],world.framevis.frame_a.x(t),world.framevis.frame_a.y(t),world.framevis.z_position)world.framevis.y_shape.R(t)=arrayliteral([33],cos(world.framevis.phi(t)),sin(world.framevis.phi(t)),0,sin(world.framevis.phi(t)),cos(world.framevis.phi(t)),0,0,0,1)world.framevis.y_shape.r_shape(t)=[000]world.framevis.y_shape.length_direction(t)=[010]world.framevis.y_shape.width_direction(t)=[001]world.framevis.y_shape.length(t)=world.framevis.axis_lengthworld.framevis.y_shape.width(t)=2world.framevis.axis_radiusworld.framevis.y_shape.height(t)=2world.framevis.axis_radiusworld.framevis.z_shape.r(t)=arrayliteral([3],world.framevis.frame_a.x(t),world.framevis.frame_a.y(t),world.framevis.z_position)world.framevis.z_shape.R(t)=arrayliteral([33],cos(world.framevis.phi(t)),sin(world.framevis.phi(t)),0,sin(world.framevis.phi(t)),cos(world.framevis.phi(t)),0,0,0,1)world.framevis.z_shape.r_shape(t)=[000]world.framevis.z_shape.length_direction(t)=[001]world.framevis.z_shape.width_direction(t)=[100]world.framevis.z_shape.length(t)=world.framevis.axis_lengthworld.framevis.z_shape.width(t)=2world.framevis.axis_radiusworld.framevis.z_shape.height(t)=2world.framevis.axis_radiuswheelJoint.phiroll(t)=wheelJoint.framea.phi(t)wheelJoint.wroll(t)=dwheelJoint.phiroll(t)dtwheelJoint.x(t)=wheelJoint.framea.x(t)wheelJoint.v(t)=dwheelJoint.x(t)dtwheelJoint.x(t)=wheelJoint.x0+wheelJoint.radiuswheelJoint.phiroll(t)wheelJoint.framea.tau(t)=wheelJoint.radiuswheelJoint.framea.fx(t)wheelJoint.framea.y(t)=wheelJoint.radiuswheelJoint.tireshape.r(t)=arrayliteral([3],wheelJoint.framea.x(t),wheelJoint.framea.y(t),wheelJoint.zposition)wheelJoint.tireshape.R(t)=arrayliteral([33],1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)wheelJoint.tireshape.r_shape(t)=arrayliteral([3],0,0,12wheelJoint.wheelwidth)wheelJoint.tireshape.length_direction(t)=[001]wheelJoint.tireshape.width_direction(t)=[010]wheelJoint.tireshape.length(t)=wheelJoint.wheelwidthwheelJoint.tireshape.width(t)=2wheelJoint.radiuswheelJoint.tireshape.height(t)=2wheelJoint.radiuswheelJoint.rim1shape.r(t)=arrayliteral([3],wheelJoint.framea.x(t),wheelJoint.framea.y(t),wheelJoint.zposition)wheelJoint.rim1shape.R(t)=arrayliteral([33],cos(wheelJoint.phiroll(t)),sin(wheelJoint.phiroll(t)),0,sin(wheelJoint.phiroll(t)),cos(wheelJoint.phiroll(t)),0,0,0,1)wheelJoint.rim1shape.r_shape(t)=arrayliteral([3],wheelJoint.radius,0,0)wheelJoint.rim1shape.length_direction(t)=[100]wheelJoint.rim1shape.width_direction(t)=[010]wheelJoint.rim1shape.length(t)=2wheelJoint.radiuswheelJoint.rim1shape.width(t)=wheelJoint.rimdiameterwheelJoint.rim1shape.height(t)=wheelJoint.rimdiameterwheelJoint.rim2shape.r(t)=arrayliteral([3],wheelJoint.framea.x(t),wheelJoint.framea.y(t),wheelJoint.zposition)wheelJoint.rim2shape.R(t)=arrayliteral([33],cos(1.570796326794895+wheelJoint.phiroll(t)),sin(1.570796326794895+wheelJoint.phiroll(t)),0,sin(1.570796326794895+wheelJoint.phiroll(t)),cos(1.570796326794895+wheelJoint.phiroll(t)),0,0,0,1)wheelJoint.rim2shape.r_shape(t)=arrayliteral([3],wheelJoint.radius,0,0)wheelJoint.rim2shape.length_direction(t)=[100]wheelJoint.rim2shape.width_direction(t)=[010]wheelJoint.rim2shape.length(t)=2wheelJoint.radiuswheelJoint.rim2shape.width(t)=wheelJoint.rimdiameterwheelJoint.rim2shape.height(t)=wheelJoint.rimdiameterIMU.x(t)=IMU.pos.x(t)IMU.y(t)=IMU.pos.y(t)IMU.phi(t)=IMU.pos.phi(t)connect(framea,pos+framea)IMU.pos.framea.fx(t)=0IMU.pos.framea.fy(t)=0IMU.pos.framea.tau(t)=0arrayliteral([3],IMU.pos.x(t),IMU.pos.y(t),IMU.pos.phi(t))=IMU.pos.r(t)IMU.pos.r(t)=arrayliteral([3],IMU.pos.framea.x(t),IMU.pos.framea.y(t),IMU.pos.framea.phi(t))square.y(t)=ifelse(square.starttime<t,square.offset+square.amplitude(122square.frequency(square.starttime+t)+4square.frequency(square.starttime+t)),square.offset)wheelinertia.r(t)=arrayliteral([2],wheelinertia.framea.x(t),wheelinertia.framea.y(t))wheelinertia.v(t)=dwheelinertia.r(t)dtwheelinertia.phi(t)=wheelinertia.framea.phi(t)wheelinertia.w(t)=dwheelinertia.phi(t)dtwheelinertia.a(t)=dwheelinertia.v(t)dtwheelinertia.alpha(t)=dwheelinertia.w(t)dtwheelinertia.f(t)=arrayliteral([2],wheelinertia.framea.fx(t),wheelinertia.framea.fy(t))wheelinertia.f(t)+arrayliteral([2],gn2d1wheelinertia.m,gn2d2wheelinertia.m)=wheelinertia.mwheelinertia.a(t)wheelinertia.Iwheelinertia.alpha(t)=wheelinertia.framea.tau(t)wheelinertia.shape.r(t)=arrayliteral([3],wheelinertia.framea.x(t),wheelinertia.framea.y(t),wheelinertia.zposition)wheelinertia.shape.R(t)=arrayliteral([33],cos(wheelinertia.phi(t)),sin(wheelinertia.phi(t)),0,sin(wheelinertia.phi(t)),cos(wheelinertia.phi(t)),0,0,0,1)wheelinertia.shape.rshape(t)=[000]wheelinertia.shape.lengthdirection(t)=[001]wheelinertia.shape.widthdirection(t)=[100]wheelinertia.shape.length(t)=2wheelinertia.radiuswheelinertia.shape.width(t)=2wheelinertia.radiuswheelinertia.shape.height(t)=2wheelinertia.radiusconnect(torquesource+spline,motorrotation+flange_a,damper1+splinea)connect(framea,motorrotation+frame_a)connect(motorrotation+frame_b,frameb)connect(torquesource+support,damper1+splineb,fixed+spline)connect(torqueinput,torquesource+tau)simplemotor.torquesource.support.phi(t)=simplemotor.torquesource.phisupport(t)simplemotor.torquesource.support.tau(t)=simplemotor.torquesource.spline.tau(t)simplemotor.torquesource.phi(t)=simplemotor.torquesource.phisupport(t)+simplemotor.torquesource.spline.phi(t)simplemotor.torquesource.spline.tau(t)=simplemotor.torquesource.tau(t)simplemotor.motorrotation.w(t)=dsimplemotor.motorrotation.phi(t)dtsimplemotor.motorrotation.alpha(t)=dsimplemotor.motorrotation.w(t)dtsimplemotor.motorrotation.frame_a.x(t)=simplemotor.motorrotation.frame_b.x(t)simplemotor.motorrotation.frame_a.y(t)=simplemotor.motorrotation.frame_b.y(t)simplemotor.motorrotation.phi(t)+simplemotor.motorrotation.frame_a.phi(t)=simplemotor.motorrotation.frame_b.phi(t)simplemotor.motorrotation.frame_a.fx(t)+simplemotor.motorrotation.frame_b.fx(t)=0simplemotor.motorrotation.frame_b.fy(t)+simplemotor.motorrotation.frame_a.fy(t)=0simplemotor.motorrotation.frame_a.tau(t)+simplemotor.motorrotation.frame_b.tau(t)=0simplemotor.motorrotation.frame_a.tau(t)=simplemotor.motorrotation.tau(t)simplemotor.motorrotation.flange_a.phi(t)=simplemotor.motorrotation.phi(t)simplemotor.motorrotation.flange_a.tau(t)=simplemotor.motorrotation.tau(t)simplemotor.motorrotation.shape.r(t)=arrayliteral([3],simplemotor.motorrotation.frame_a.x(t),simplemotor.motorrotation.frame_a.y(t),simplemotor.motorrotation.z_position)simplemotor.motorrotation.shape.R(t)=arrayliteral([33],cos(simplemotor.motorrotation.frame_a.phi(t)),sin(simplemotor.motorrotation.frame_a.phi(t)),0,sin(simplemotor.motorrotation.frame_a.phi(t)),cos(simplemotor.motorrotation.frame_a.phi(t)),0,0,0,1)simplemotor.motorrotation.shape.r_shape(t)=arrayliteral([3],0,0,12simplemotor.motorrotation.cylinder_length)simplemotor.motorrotation.shape.length_direction(t)=[001]simplemotor.motorrotation.shape.width_direction(t)=[100]simplemotor.motorrotation.shape.length(t)=simplemotor.motorrotation.cylinder_lengthsimplemotor.motorrotation.shape.width(t)=2simplemotor.motorrotation.radiussimplemotor.motorrotation.shape.height(t)=2simplemotor.motorrotation.radiussimplemotor.damper1.phirel(t)=simplemotor.damper1.splineb.phi(t)simplemotor.damper1.splinea.phi(t)simplemotor.damper1.splineb.tau(t)=simplemotor.damper1.tau(t)simplemotor.damper1.splinea.tau(t)=simplemotor.damper1.tau(t)dsimplemotor.damper1.phirel(t)dt=simplemotor.damper1.wrel(t)dsimplemotor.damper1.wrel(t)dt=simplemotor.damper1.arel(t)simplemotor.damper1.tau(t)=simplemotor.damper1.dsimplemotor.damper1.wrel(t)simplemotor.fixed.spline.phi(t)=simplemotor.fixed.phi0anglecontroller.control_error(t)=anglecontroller.u_s(t)anglecontroller.u_m(t)connect(addp+y,proportional+u)connect(addd+y,derivative+u)connect(addi+y,integrator+u)connect(proportional+y,addpid+u1)connect(derivative+y,addpid+u2)connect(integrator+y,addpid+u3)connect(addpid+y,gainpid+u)connect(gainpid+y,addff+u1)connect(addff+y,limiter+u)connect(limiter+y,addsat+u1)connect(addsat+y,gaintrack+u)connect(gaintrack+y,addi+u3)connect(limiter+y,y)connect(addff+u2,uff)connect(addff+y,addsat+u2)connect(addp+u1,us)connect(addd+u1,us)connect(addi+u1,us)connect(um,addp+u2)connect(addi+u2,um)connect(addd+u2,um)anglecontroller.add_p.y(t)=anglecontroller.add_p.k1anglecontroller.add_p.u1(t)+anglecontroller.add_p.k2anglecontroller.add_p.u2(t)anglecontroller.add_d.y(t)=anglecontroller.add_d.k1anglecontroller.add_d.u1(t)+anglecontroller.add_d.k2anglecontroller.add_d.u2(t)anglecontroller.add_i.y(t)=anglecontroller.add_i.k1anglecontroller.add_i.u1(t)+anglecontroller.add_i.k2anglecontroller.add_i.u2(t)+anglecontroller.add_i.k3anglecontroller.add_i.u3(t)anglecontroller.proportional.y(t)=anglecontroller.proportional.kanglecontroller.proportional.u(t)danglecontroller.derivative.x(t)dt=anglecontroller.derivative.x(t)+anglecontroller.derivative.u(t)anglecontroller.derivative.Tanglecontroller.derivative.y(t)=anglecontroller.derivative.k(anglecontroller.derivative.x(t)+anglecontroller.derivative.u(t))anglecontroller.derivative.Tdanglecontroller.integrator.x(t)dt=anglecontroller.integrator.kanglecontroller.integrator.u(t)anglecontroller.integrator.y(t)=anglecontroller.integrator.x(t)anglecontroller.add_pid.y(t)=anglecontroller.add_pid.k1anglecontroller.add_pid.u1(t)+anglecontroller.add_pid.k2anglecontroller.add_pid.u2(t)+anglecontroller.add_pid.k3anglecontroller.add_pid.u3(t)anglecontroller.gain_pid.y(t)=anglecontroller.gain_pid.kanglecontroller.gain_pid.u(t)anglecontroller.add_ff.y(t)=anglecontroller.add_ff.k1anglecontroller.add_ff.u1(t)+anglecontroller.add_ff.k2anglecontroller.add_ff.u2(t)anglecontroller.limiter.y(t)=clamp(anglecontroller.limiter.u(t),anglecontroller.limiter.y_min,anglecontroller.limiter.y_max)anglecontroller.add_sat.y(t)=anglecontroller.add_sat.k1anglecontroller.add_sat.u1(t)+anglecontroller.add_sat.k2anglecontroller.add_sat.u2(t)anglecontroller.gain_track.y(t)=anglecontroller.gain_track.kanglecontroller.gain_track.u(t)poscontroller.control_error(t)=poscontroller.u_s(t)poscontroller.u_m(t)connect(addp+y,proportional+u)connect(addd+y,derivative+u)connect(addi+y,integrator+u)connect(proportional+y,addpid+u1)connect(derivative+y,addpid+u2)connect(integrator+y,addpid+u3)connect(addpid+y,gainpid+u)connect(gainpid+y,addff+u1)connect(addff+y,limiter+u)connect(limiter+y,addsat+u1)connect(addsat+y,gaintrack+u)connect(gaintrack+y,addi+u3)connect(limiter+y,y)connect(addff+u2,uff)connect(addff+y,addsat+u2)connect(addp+u1,us)connect(addd+u1,us)connect(addi+u1,us)connect(um,addp+u2)connect(addi+u2,um)connect(addd+u2,um)poscontroller.add_p.y(t)=poscontroller.add_p.k1poscontroller.add_p.u1(t)+poscontroller.add_p.k2poscontroller.add_p.u2(t)poscontroller.add_d.y(t)=poscontroller.add_d.k1poscontroller.add_d.u1(t)+poscontroller.add_d.k2poscontroller.add_d.u2(t)poscontroller.add_i.y(t)=poscontroller.add_i.k1poscontroller.add_i.u1(t)+poscontroller.add_i.k2poscontroller.add_i.u2(t)+poscontroller.add_i.k3poscontroller.add_i.u3(t)poscontroller.proportional.y(t)=poscontroller.proportional.kposcontroller.proportional.u(t)dposcontroller.derivative.x(t)dt=poscontroller.derivative.x(t)+poscontroller.derivative.u(t)poscontroller.derivative.Tposcontroller.derivative.y(t)=poscontroller.derivative.k(poscontroller.derivative.x(t)+poscontroller.derivative.u(t))poscontroller.derivative.Tdposcontroller.integrator.x(t)dt=poscontroller.integrator.kposcontroller.integrator.u(t)poscontroller.integrator.y(t)=poscontroller.integrator.x(t)poscontroller.add_pid.y(t)=poscontroller.add_pid.k1poscontroller.add_pid.u1(t)+poscontroller.add_pid.k2poscontroller.add_pid.u2(t)+poscontroller.add_pid.k3poscontroller.add_pid.u3(t)poscontroller.gain_pid.y(t)=poscontroller.gain_pid.kposcontroller.gain_pid.u(t)poscontroller.add_ff.y(t)=poscontroller.add_ff.k1poscontroller.add_ff.u1(t)+poscontroller.add_ff.k2poscontroller.add_ff.u2(t)poscontroller.limiter.y(t)=clamp(poscontroller.limiter.u(t),poscontroller.limiter.y_min,poscontroller.limiter.y_max)poscontroller.add_sat.y(t)=poscontroller.add_sat.k1poscontroller.add_sat.u1(t)+poscontroller.add_sat.k2poscontroller.add_sat.u2(t)poscontroller.gain_track.y(t)=poscontroller.gain_track.kposcontroller.gain_track.u(t)gain.y(t)=gain.kgain.u(t)gain1.y(t)=gain1.kgain1.u(t)refgen.Δu(t)=broadcast(,refgen.u(t),refgen.u0)refgen.Δy(t)=broadcast(,refgen.y(t),refgen.y0)drefgen.x(t)dt=refgen.Brefgen.Δu(t)+refgen.Arefgen.x(t)refgen.Δy(t)=refgen.Crefgen.x(t)+refgen.Drefgen.Δu(t)angleff.y(t)=angleff.u_1(t)posref.y(t)=posref.u_2(t)torqueff.y(t)=torqueff.u_3(t)gain2.y(t)=gain2.kgain2.u(t)bodymass.rod_shape.r(t)=arrayliteral([3],bodymass.frame_a.x(t),bodymass.frame_a.y(t),bodymass.z_position)bodymass.rod_shape.R(t)=arrayliteral([33],1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)bodymass.rod_shape.r_shape(t)=[000]bodymass.rod_shape.length_direction(t)=arrayliteral([3],bodymass.translation.r0_1(t)bodymass.l,bodymass.translation.r0_2(t)bodymass.l,0)bodymass.rod_shape.width_direction(t)=[001]bodymass.rod_shape.length(t)=bodymass.lbodymass.rod_shape.width(t)=2bodymass.radiusbodymass.rod_shape.height(t)=2bodymass.radiusconnect(framea,translation+framea,translationcm+frame_a)connect(frameb,translation+frameb)connect(translationcm+frame_b,body+framea)bodymass.translation.phi(t)=bodymass.translation.frame_a.phi(t)bodymass.translation.w(t)=dbodymass.translation.phi(t)dtbodymass.translation.r0(t)=arrayliteral([22],cos(bodymass.translation.phi(t)),sin(bodymass.translation.phi(t)),sin(bodymass.translation.phi(t)),cos(bodymass.translation.phi(t)))bodymass.translation.rbodymass.translation.r0(t)=arrayliteral([2],bodymass.translation.frame_a.x(t)+bodymass.translation.frame_b.x(t),bodymass.translation.frame_b.y(t)bodymass.translation.frame_a.y(t))bodymass.translation.frame_a.phi(t)=bodymass.translation.frame_b.phi(t)bodymass.translation.frame_b.fx(t)+bodymass.translation.frame_a.fx(t)=0bodymass.translation.frame_b.fy(t)+bodymass.translation.frame_a.fy(t)=0bodymass.translation.frame_b.tau(t)+bodymass.translation.frame_a.tau(t)bodymass.translation.frame_b.fx(t)bodymass.translation.r0_2(t)+bodymass.translation.r0_1(t)bodymass.translation.frame_b.fy(t)=0bodymass.translation.shape.r(t)=arrayliteral([3],bodymass.translation.frame_a.x(t),bodymass.translation.frame_a.y(t),bodymass.translation.z_position)bodymass.translation.shape.R(t)=arrayliteral([33],1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)bodymass.translation.shape.r_shape(t)=[000]bodymass.translation.shape.length_direction(t)=arrayliteral([3],bodymass.translation.r0_1(t)bodymass.translation.l,bodymass.translation.r0_2(t)bodymass.translation.l,0)bodymass.translation.shape.width_direction(t)=[001]bodymass.translation.shape.length(t)=bodymass.translation.lbodymass.translation.shape.width(t)=2bodymass.translation.radiusbodymass.translation.shape.height(t)=2bodymass.translation.radiusbodymass.translation_cm.phi(t)=bodymass.translation_cm.frame_a.phi(t)bodymass.translation_cm.w(t)=dbodymass.translation_cm.phi(t)dtbodymass.translation_cm.r0(t)=arrayliteral([22],cos(bodymass.translation_cm.phi(t)),sin(bodymass.translation_cm.phi(t)),sin(bodymass.translation_cm.phi(t)),cos(bodymass.translation_cm.phi(t)))bodymass.translation_cm.rbodymass.translation_cm.r0(t)=arrayliteral([2],bodymass.translation_cm.frame_a.x(t)+bodymass.translation_cm.frame_b.x(t),bodymass.translation_cm.frame_b.y(t)bodymass.translation_cm.frame_a.y(t))bodymass.translation_cm.frame_a.phi(t)=bodymass.translation_cm.frame_b.phi(t)bodymass.translation_cm.frame_b.fx(t)+bodymass.translation_cm.frame_a.fx(t)=0bodymass.translation_cm.frame_b.fy(t)+bodymass.translation_cm.frame_a.fy(t)=0bodymass.translation_cm.frame_a.tau(t)+bodymass.translation_cm.frame_b.tau(t)bodymass.translation_cm.frame_b.fx(t)bodymass.translation_cm.r0_2(t)+bodymass.translation_cm.frame_b.fy(t)bodymass.translation_cm.r0_1(t)=0bodymass.translation_cm.shape.r(t)=arrayliteral([3],bodymass.translation_cm.frame_a.x(t),bodymass.translation_cm.frame_a.y(t),bodymass.translation_cm.z_position)bodymass.translation_cm.shape.R(t)=arrayliteral([33],1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)bodymass.translation_cm.shape.r_shape(t)=[000]bodymass.translation_cm.shape.length_direction(t)=arrayliteral([3],bodymass.translation_cm.r0_1(t)bodymass.translation_cm.l,bodymass.translation_cm.r0_2(t)bodymass.translation_cm.l,0)bodymass.translation_cm.shape.width_direction(t)=[001]bodymass.translation_cm.shape.length(t)=bodymass.translation_cm.lbodymass.translation_cm.shape.width(t)=2bodymass.translation_cm.radiusbodymass.translation_cm.shape.height(t)=2bodymass.translation_cm.radiusbodymass.body.r(t)=arrayliteral([2],bodymass.body.frame_a.x(t),bodymass.body.frame_a.y(t))bodymass.body.v(t)=dbodymass.body.r(t)dtbodymass.body.phi(t)=bodymass.body.frame_a.phi(t)bodymass.body.w(t)=dbodymass.body.phi(t)dtbodymass.body.a(t)=dbodymass.body.v(t)dtbodymass.body.alpha(t)=dbodymass.body.w(t)dtbodymass.body.f(t)=arrayliteral([2],bodymass.body.frame_a.fx(t),bodymass.body.frame_a.fy(t))arrayliteral([2],bodymass.body.mgn2d1,bodymass.body.mgn2d2)+bodymass.body.f(t)=bodymass.body.mbodymass.body.a(t)bodymass.body.Ibodymass.body.alpha(t)=bodymass.body.frame_a.tau(t)bodymass.body.shape.r(t)=arrayliteral([3],bodymass.body.frame_a.x(t),bodymass.body.frame_a.y(t),bodymass.body.z_position)bodymass.body.shape.R(t)=arrayliteral([33],cos(bodymass.body.phi(t)),sin(bodymass.body.phi(t)),0,sin(bodymass.body.phi(t)),cos(bodymass.body.phi(t)),0,0,0,1)bodymass.body.shape.r_shape(t)=[000]bodymass.body.shape.length_direction(t)=[001]bodymass.body.shape.width_direction(t)=[100]bodymass.body.shape.length(t)=2bodymass.body.radiusbodymass.body.shape.width(t)=2bodymass.body.radiusbodymass.body.shape.height(t)=2bodymass.body.radiusmux1.y(t)=arrayliteral([1],mux1.u(t))]

Source

dyad
"This is a continuation of the DyadBalans2D example, adding a feedforward generator computed using `DyadControlSystems.feedforward_generator` in the script `compute_feedforward.jl`. The feedforward generator provides a filtered position reference to the outer controller as well as an angle feedforward reference for the inner controller and a direct torque feedforward signal to be applied directly to the torque input. Both inner feedforward signals are connected to the `u_ff` port on the `LimPID` components."
example component DyadBalans2DFF
  world = MultibodyComponents.PlanarMechanics.World(g = 9.82) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 890, "y1": 900, "x2": 790, "y2": 800, "rot": 180}
      },
      "tags": []
    }
  }
  wheelJoint = MultibodyComponents.PlanarMechanics.OneDOFRollingWheelJoint(radius = R) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 200, "y1": 700, "x2": 460, "y2": 960, "rot": 0}
      },
      "tags": []
    }
  }
  IMU = MultibodyComponents.PlanarMechanics.AbsolutePosition(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 460, "y1": 540, "x2": 560, "y2": 640, "rot": 0}
      },
      "tags": []
    }
  }
  square = BlockComponents.Sources.Square(amplitude = 0.05, frequency = 1 / 10) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -830, "y1": 600, "x2": -730, "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() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 620, "x2": 380, "y2": 720, "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": []
    }
  }
  """
  refgen has 1 input (position reference) and 3 outputs:
     y[1]: angle feedforward
     y[2]: filtered position reference
     y[3]: torque feedforward
  """
  refgen = BlockComponents.Continuous.StateSpace(nx = ff_nx(), nu = 1, ny = 3, A = ff_A(), B = ff_B(), C = ff_C(), D = ff_D()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -570, "y1": 600, "x2": -470, "y2": 700, "rot": 0}
      },
      "tags": []
    }
  }
  angle_ff = MultibodyComponents.Selector(nu = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 470, "x2": -310, "y2": 570, "rot": 0}
      },
      "tags": []
    }
  }
  pos_ref = MultibodyComponents.Selector(nu = 3, index = 2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 600, "x2": -310, "y2": 700, "rot": 0}
      },
      "tags": []
    }
  }
  torque_ff = MultibodyComponents.Selector(nu = 3, index = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 340, "x2": -310, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "This negative gain is required since we are providing the angle reference signal to the `u_ff` connector of the outer controller which sits behind the negative gain `gain1`. We are thus effectively negating `gain1` with this `gain2`."
  gain2 = BlockComponents.Math.Gain(k = -1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -240, "y1": 540, "x2": -180, "y2": 600, "rot": 90}
      },
      "tags": []
    }
  }
  body_mass = MultibodyComponents.PlanarMechanics.BodyShape(m = M, I = Ib, radius = 0.03, color = [0.2, 0.2, 0.2, 0.9], r = [0, 2 * L], r_cm = [0, L]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 130, "y1": 130, "x2": 530, "y2": 530, "rot": 270}
      },
      "tags": []
    }
  }
  mux1 = MultibodyComponents.PlanarMechanics.examples.dyad_balans.Mux1() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -700, "y1": 600, "x2": -600, "y2": 700, "rot": 0}
      },
      "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
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
  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": []}]}}
  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": 621},
            {"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": 560},
            {"x": 710, "y": 1090},
            {"x": -290, "y": 1090},
            {"x": -290, "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(gain.y, simplemotor.torqueinput) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  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(pos_ref.y, pos_controller.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -290, "y": 650}, {"x": -290, "y": 647}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(refgen.y, pos_ref.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(refgen.y, torque_ff.u, angle_ff.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": -445, "y": 650}], "E": -1},
        {"S": -1, "M": [{"x": -445, "y": 390}], "E": 2},
        {"S": 3, "M": [], "E": -1}
      ],
      "junctions": [{"x": -445, "y": 520}],
      "renderStyle": "standard"
    }
  }
  connect(body_mass.frame_a, simplemotor.frame_a, IMU.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 330.00000000000006, "y": 535}, {"x": 330, "y": 535}],
          "E": -1
        },
        {"S": -1, "M": [], "E": 2},
        {"S": 3, "M": [], "E": -1}
      ],
      "junctions": [{"x": 330, "y": 590}],
      "renderStyle": "standard"
    }
  }
  connect(square.y, mux1.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(mux1.y, refgen.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(angle_ff.y, gain2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -210, "y": 520}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(gain2.y, pos_controller.u_ff) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(torque_ff.y, angle_controller.u_ff) {
    "Dyad": {
      "renderStyle": "standard",
      "edges": [{"S": 1, "M": [{"x": 80, "y": 390}], "E": 2}]
    }
  }
metadata {"Dyad": {"tests": {"case1": {"stop": 50}}}}
end
Flattened Source
dyad
"This is a continuation of the DyadBalans2D example, adding a feedforward generator computed using `DyadControlSystems.feedforward_generator` in the script `compute_feedforward.jl`. The feedforward generator provides a filtered position reference to the outer controller as well as an angle feedforward reference for the inner controller and a direct torque feedforward signal to be applied directly to the torque input. Both inner feedforward signals are connected to the `u_ff` port on the `LimPID` components."
example component DyadBalans2DFF
  world = MultibodyComponents.PlanarMechanics.World(g = 9.82) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 890, "y1": 900, "x2": 790, "y2": 800, "rot": 180}
      },
      "tags": []
    }
  }
  wheelJoint = MultibodyComponents.PlanarMechanics.OneDOFRollingWheelJoint(radius = R) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 200, "y1": 700, "x2": 460, "y2": 960, "rot": 0}
      },
      "tags": []
    }
  }
  IMU = MultibodyComponents.PlanarMechanics.AbsolutePosition(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 460, "y1": 540, "x2": 560, "y2": 640, "rot": 0}
      },
      "tags": []
    }
  }
  square = BlockComponents.Sources.Square(amplitude = 0.05, frequency = 1 / 10) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -830, "y1": 600, "x2": -730, "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() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 620, "x2": 380, "y2": 720, "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": []
    }
  }
  """
  refgen has 1 input (position reference) and 3 outputs:
     y[1]: angle feedforward
     y[2]: filtered position reference
     y[3]: torque feedforward
  """
  refgen = BlockComponents.Continuous.StateSpace(nx = ff_nx(), nu = 1, ny = 3, A = ff_A(), B = ff_B(), C = ff_C(), D = ff_D()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -570, "y1": 600, "x2": -470, "y2": 700, "rot": 0}
      },
      "tags": []
    }
  }
  angle_ff = MultibodyComponents.Selector(nu = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 470, "x2": -310, "y2": 570, "rot": 0}
      },
      "tags": []
    }
  }
  pos_ref = MultibodyComponents.Selector(nu = 3, index = 2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 600, "x2": -310, "y2": 700, "rot": 0}
      },
      "tags": []
    }
  }
  torque_ff = MultibodyComponents.Selector(nu = 3, index = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -410, "y1": 340, "x2": -310, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "This negative gain is required since we are providing the angle reference signal to the `u_ff` connector of the outer controller which sits behind the negative gain `gain1`. We are thus effectively negating `gain1` with this `gain2`."
  gain2 = BlockComponents.Math.Gain(k = -1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -240, "y1": 540, "x2": -180, "y2": 600, "rot": 90}
      },
      "tags": []
    }
  }
  body_mass = MultibodyComponents.PlanarMechanics.BodyShape(m = M, I = Ib, radius = 0.03, color = [0.2, 0.2, 0.2, 0.9], r = [0, 2 * L], r_cm = [0, L]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 130, "y1": 130, "x2": 530, "y2": 530, "rot": 270}
      },
      "tags": []
    }
  }
  mux1 = MultibodyComponents.PlanarMechanics.examples.dyad_balans.Mux1() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -700, "y1": 600, "x2": -600, "y2": 700, "rot": 0}
      },
      "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
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
  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": []}]}}
  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": 621},
            {"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": 560},
            {"x": 710, "y": 1090},
            {"x": -290, "y": 1090},
            {"x": -290, "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(gain.y, simplemotor.torqueinput) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  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(pos_ref.y, pos_controller.u_s) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -290, "y": 650}, {"x": -290, "y": 647}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(refgen.y, pos_ref.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(refgen.y, torque_ff.u, angle_ff.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": -445, "y": 650}], "E": -1},
        {"S": -1, "M": [{"x": -445, "y": 390}], "E": 2},
        {"S": 3, "M": [], "E": -1}
      ],
      "junctions": [{"x": -445, "y": 520}],
      "renderStyle": "standard"
    }
  }
  connect(body_mass.frame_a, simplemotor.frame_a, IMU.frame_a) {
    "Dyad": {
      "edges": [
        {
          "S": 1,
          "M": [{"x": 330.00000000000006, "y": 535}, {"x": 330, "y": 535}],
          "E": -1
        },
        {"S": -1, "M": [], "E": 2},
        {"S": 3, "M": [], "E": -1}
      ],
      "junctions": [{"x": 330, "y": 590}],
      "renderStyle": "standard"
    }
  }
  connect(square.y, mux1.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(mux1.y, refgen.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(angle_ff.y, gain2.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -210, "y": 520}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(gain2.y, pos_controller.u_ff) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(torque_ff.y, angle_controller.u_ff) {
    "Dyad": {
      "renderStyle": "standard",
      "edges": [{"S": 1, "M": [{"x": 80, "y": 390}], "E": 2}]
    }
  }
metadata {"Dyad": {"tests": {"case1": {"stop": 50}}}}
end


Test Cases

Test Case case1

  • Examples

  • Experiments

  • Analyses