Block Diagrams
The Control Systems functions and classes allow users to create and animate feedback loops.
Static Block Diagrams
Example: FeedbackLoop
from manim import *
from controltheorylib import *
config.background_color = "#3d3d3d"
class FeedbackLoop(Scene):
def construct(self):
#====== code related to creating feedback loop diagram =======
# Initiate controlsystem
cs = ControlSystem()
# Create blocks
sum_block1 = cs.add_block("", "summing_junction", 4*LEFT, params={"input1_dir": LEFT, "input2_dir": DOWN,"fill_opacity": 0})
ref = cs.add_input(sum_block1, "in_left", label=r"r(s)")
controller = cs.add_block(r"K_p(1+Ds)", "transfer_function", 1.5*LEFT, {"use_mathtex":True,"font_size":50,"label":r"K_p(1+Ds)"})
sum_block2 = cs.add_block("", "summing_junction", RIGHT, params={"input1_dir": LEFT, "input2_dir": UP, "output1_dir": RIGHT, "output2_dir":DOWN, "fill_opacity":0})
plant = cs.add_block("Plant", "transfer_function", RIGHT*3.5, {"text_font_size":40, "label":"Plant"})
output = cs.add_output(plant, "out_right", label=r"y(s)")
feedback = cs.add_feedback_path(plant, "out_right", sum_block1, "in_bottom", rel_start_offset=RIGHT)
#Connect
conn1 = cs.connect(sum_block1, "out_right", controller, "in_left", label=r"e(s)")
conn2 = cs.connect(controller, "out_right", sum_block2, "in_left")
conn3 = cs.connect(sum_block2, "out_right", plant, "in_left")
# Add disturbance
disturbance = cs.add_input(sum_block2, "in_top", label=r"d(s)")
# get all components
diagram = cs.get_all_components()
# add diagram to scene
self.add(diagram)
#======== code related to create overview of class hierarchy =======
surrounding = DashedVMobject(SurroundingRectangle(diagram, buff=0.5, color=WHITE, stroke_width=2), num_dashes=40).shift(0.1*LEFT)
controlsystext = Text("ControlSystem", font="Courier", font_size=30).next_to(surrounding,UP, buff=0.3)
controlblocktext = Text(".add_block()", font="Courier", font_size=25).next_to(conn1,UP, buff=0.8)
blockline1 = DashedLine(sum_block1, controlblocktext.get_bottom(), dash_length=0.1, stroke_width=1.5, buff=0.15)
blockline2 = DashedLine(controller, controlblocktext.get_bottom(), dash_length=0.1, stroke_width=1.5, buff=0.15)
conntext = Text(".connect()", font="Courier", font_size=25).next_to(sum_block2, DOWN, buff=0.6)
connline1 = DashedLine(conntext.get_top(), conn2.get_center()+0.2*LEFT, dash_length=0.1, stroke_width=1.5, buff=0.15)
connline2 = DashedLine(conntext.get_top(), conn3.get_center(), dash_length=0.1, stroke_width=1.5, buff=0.15)
self.add(surrounding,controlsystext, controlblocktext, blockline1, blockline2, conntext, connline1,connline2)
Example: FeedbackLoop2
from manim import *
from controltheorylib import *
config.background_color = "#3d3d3d"
class FeedbackLoop2(MovingCameraScene):
def construct(self):
self.camera.frame.scale(1.2)
cs = ControlSystem()
# Create blocks
setpoint = cs.add_block("setpoint", "transfer_function", 6*LEFT,{"color":BLUE, "label":"Setpoint Generator", "block_width":1.7,"block_height":0.8,"font_size":40, "output_dirs":[RIGHT,UP], "output_names": ["out_r","out_up"]})
input = cs.add_input(setpoint, "in_left", length=1)
sum1 = cs.add_block("sum1", "summing_junction", 4.3*LEFT)
fbcontroller = cs.add_block("fbcontroller", "transfer_function", 2.5*LEFT,{"color":YELLOW,"label": "FB controller", "block_height":0.8,"block_width":1.6, "font_size":40})
sum2 = cs.add_block("sum2", "summing_junction", 0.7*LEFT, params={"input1_dir": LEFT, "input2_dir": UP})
ffcontroller = cs.add_block("ffcontroller", "transfer_function", 2.5*LEFT+1.5*UP,{"color":YELLOW,"label": "FF controller", "block_height":0.8,"block_width":1.6, "font_size":40})
feedforward = cs.add_feedforward_path(setpoint, "out_up", ffcontroller, "in_left")
feedforward2 = cs.add_feedforward_path(ffcontroller, "out_right", sum2, "in_top")
amp = cs.add_block("amp", "transfer_function", 1.0*RIGHT,{"color":RED,"label":"Amplifier", "block_width":1.6,"block_height":0.8,"font_size":40})
act = cs.add_block("act", "transfer_function", 3.4*RIGHT,{"color": GREEN,"label":"Actuator", "block_width":1.6,"block_height":0.8,"font_size":40})
mech = cs.add_block("mech", "transfer_function", 5.7*RIGHT,{"color":PINK,"label":"Mechanism", "block_width":1.6,"block_height":0.8,"font_size":40,"output_dirs":[RIGHT,DOWN], "input_dirs":[LEFT,UP], "input_names":["in_l", "in_top"], "output_names": ["out_r","out_down"]})
output = cs.add_output(mech, "out_r", length=1.5, label="Position", use_math_tex=False, font_size=20, rel_label_pos=0.5*UP)
sens = cs.add_block("sens", "transfer_function", 5.7*RIGHT+1.5*DOWN,{"color":ORANGE,"label":"Sensor", "block_width":1.6,"block_height":0.8,"font_size":40, "output_dirs":[LEFT,DOWN], "input_dirs":[RIGHT,UP], "input_names":["in_r", "in_top"], "output_names": ["out_l","out_down"]})
feedback = cs.add_feedback_path(sens,"out_l", sum1, "in_bottom")
# Connections
conn1 = cs.connect(setpoint, "out_r", sum1, "in_left")
conn2 = cs.connect(sum1, "out_right", fbcontroller, "in_left",label="e")
conn3 = cs.connect(fbcontroller, "out_right", sum2, "in_left")
conn4 = cs.connect(sum2, "out_right", amp, "in_left")
conn5 = cs.connect(amp, "out_right", act, "in_left")
conn6 = cs.connect(act, "out_right", mech, "in_l")
conn7 = cs.connect(mech, "out_down", sens, "in_top")
surrounding1 = DashedVMobject(Rectangle(width=7.3,height=5, color=WHITE, stroke_width=1.5), num_dashes=70).shift(4*LEFT)
surrounding2 = DashedVMobject(Rectangle(width=8.3,height=5, color=WHITE, stroke_width=1.5), num_dashes=70).shift(3.97*RIGHT)
lefttext = Text("Software/ Electronics", font_size=25).next_to(surrounding1,DOWN, buff=0.3)
righttext = Text("Hardware", font_size=25).next_to(surrounding2,DOWN, buff=0.3)
Header = Text("Mechatronic Design", font_size=30).move_to(4*UP+5*LEFT)
diagram = cs.get_all_components()
min = MathTex("-", font_size=25).next_to(sum1,RIGHT+DOWN,buff=0.03)
plus = MathTex("+", font_size=25).next_to(sum1,LEFT+UP,buff=0.03)
plus2 = MathTex("+", font_size=25).next_to(sum2,LEFT+UP,buff=0.03)
self.add(diagram, surrounding1, surrounding2, lefttext, righttext, Header, min, plus,plus2)
Feedback Loop Animation
Example: BlockDiagramAnimation
from manim import *
from controltheorylib import *
config.background_color = "#3d3d3d"
class Animation_Example2(MovingCameraScene):
def construct(self):
self.camera.frame.scale(1.2)
cs = ControlSystem()
# Create blocks
setpoint = cs.add_block("setpoint", "transfer_function", 6*LEFT,{"color":BLUE, "label":"Setpoint Generator", "block_width":1.7,"block_height":0.8,"font_size":40, "output_dirs":[RIGHT,UP], "output_names": ["out_r","out_up"]})
input = cs.add_input(setpoint, "in_left", length=1)
sum1 = cs.add_block("sum1", "summing_junction", 4.3*LEFT)
fbcontroller = cs.add_block("fbcontroller", "transfer_function", 2.5*LEFT,{"color":YELLOW,"label": "FB controller", "block_height":0.8,"block_width":1.6, "font_size":40})
sum2 = cs.add_block("sum2", "summing_junction", 0.7*LEFT, params={"input1_dir": LEFT, "input2_dir": UP, "input2_sign": "-", "input1_sign": "+"})
ffcontroller = cs.add_block("ffcontroller", "transfer_function", 2.5*LEFT+1.5*UP,{"color":YELLOW,"label": "FF controller", "block_height":0.8,"block_width":1.6, "font_size":40})
feedforward = cs.add_feedforward_path(setpoint, "out_up", ffcontroller, "in_left")
feedforward2 = cs.add_feedforward_path(ffcontroller, "out_right", sum2, "in_top")
amp = cs.add_block("amp", "transfer_function", 1.0*RIGHT,{"color":RED,"label":"Amplifier", "block_width":1.6,"block_height":0.8,"font_size":40})
act = cs.add_block("act", "transfer_function", 3.4*RIGHT,{"color": GREEN,"label":"Actuator", "block_width":1.6,"block_height":0.8,"font_size":40})
mech = cs.add_block("mech", "transfer_function", 5.7*RIGHT,{"color":PINK,"label":"Mechanism", "block_width":1.6,"block_height":0.8,"font_size":40,"output_dirs":[RIGHT,DOWN], "input_dirs":[LEFT,UP], "input_names":["in_l", "in_top"], "output_names": ["out_r","out_down"]})
output = cs.add_output(mech, "out_r", length=1.5, label="Position", use_math_tex=False, font_size=20, rel_label_pos=0.5*UP)
sens = cs.add_block("sens", "transfer_function", 5.7*RIGHT+1.5*DOWN,{"color":ORANGE,"label":"Sensor", "block_width":1.6,"block_height":0.8,"font_size":40, "output_dirs":[LEFT,DOWN], "input_dirs":[RIGHT,UP], "input_names":["in_r", "in_top"], "output_names": ["out_l","out_down"]})
feedback = cs.add_feedback_path(sens,"out_l", sum1, "in_bottom")
# Connections
conn1 = cs.connect(setpoint, "out_r", sum1, "in_left")
conn2 = cs.connect(sum1, "out_right", fbcontroller, "in_left",label="e")
conn3 = cs.connect(fbcontroller, "out_right", sum2, "in_left")
conn4 = cs.connect(sum2, "out_right", amp, "in_left")
conn5 = cs.connect(amp, "out_right", act, "in_left")
conn6 = cs.connect(act, "out_right", mech, "in_l")
conn7 = cs.connect(mech, "out_down", sens, "in_top")
surrounding1 = DashedVMobject(Rectangle(width=7.3,height=5, color=WHITE, stroke_width=1.5), num_dashes=70).shift(4*LEFT)
surrounding2 = DashedVMobject(Rectangle(width=8.3,height=5, color=WHITE, stroke_width=1.5), num_dashes=70).shift(3.97*RIGHT)
lefttext = Text("Software/ Electronics", font_size=25).next_to(surrounding1,DOWN, buff=0.3)
righttext = Text("Hardware", font_size=25).next_to(surrounding2,DOWN, buff=0.3)
Header = Text("Mechatronic Design", font_size=30).move_to(4*UP+5*LEFT)
self.play(Create(input), run_time=0.5)
self.wait(0.2)
self.play(Create(setpoint), run_time=0.5)
self.wait(0.2)
self.play(Create(feedforward),Create(conn1), Create(sum1), Create(conn2), run_time=0.5)
self.wait(0.2)
self.play(Create(ffcontroller), Create(fbcontroller), run_time=0.5)
self.wait(0.2)
self.play(Create(feedforward2), Create(conn3), run_time=0.5)
self.wait(0.2)
self.play(Create(sum2), run_time=0.5)
self.wait(0.2)
self.play(Create(conn4), Create(surrounding1), Write(lefttext), run_time=0.5)
self.wait(0.2)
self.play(Create(amp), run_time=0.2)
self.wait(0.2)
self.play(Create(conn5), run_time=0.2)
self.wait(0.2)
self.play(Create(act), run_time=0.2)
self.wait(0.2)
self.play(Create(conn6), run_time=0.2)
self.wait(0.2)
self.play(Create(mech), run_time=0.2)
self.wait(0.2)
self.play(Create(output), Create(conn7), run_time=0.52)
self.wait(0.2)
self.play(Create(sens), run_time=0.2)
self.wait(0.2)
self.play(Create(feedback), Create(surrounding2), Write(righttext), run_time=0.2)
self.wait(0.2)
self.play(Write(Header), run_time=0.5)
self.wait(1.5)
Signal flow Animation
Example: SignalFlow
from manim import *
from controltheorylib import *
class SignalFlow(Scene):
def construct(self):
cs = ControlSystem()
# Create blocks
sum_block1 = cs.add_block("", "summing_junction", 3.5*LEFT, params={"input1_dir": LEFT,"stroke_width":1, "input2_dir": DOWN, "input2_sign": "-", "input1_sign": "+","fill_opacity": 0})
ref = cs.add_input(sum_block1, "in_left", label=r"r(t)")
controller = cs.add_block(r"K_p(1+Ds)", "transfer_function", LEFT, {"use_mathtex":False, "color":WHITE, "font_size":50,"label":"Controller"})
plant = cs.add_block("Plant", "transfer_function", RIGHT*2, {"color":WHITE,"text_font_size":50, "label":"Plant"})
output = cs.add_output(plant, "out_right", label=r"y(t)", color=WHITE)
feedback = cs.add_feedback_path(plant, "out_right", sum_block1, "in_bottom", rel_start_offset=RIGHT)
#Connect
conn1 = cs.connect(sum_block1, "out_right", controller, "in_left", label=r"e(t)", color=WHITE)
conn2 = cs.connect(controller, "out_right", plant, "in_left", color=WHITE)
#conn5 = cs.connect(sum_block3, "out2", feedforward_block,"in")
# Add disturbance
diagram = cs.get_all_components()
self.add(diagram)
cs.animate_signals(self, sum_block1, controller, plant, color=YELLOW, signal_speed=3, spawn_interval=0.8, feedback_color=YELLOW, duration=12)