Block Diagrams

The Control Systems functions and classes allow users to create and animate feedback loops.

Static Block Diagrams

Example: FeedbackLoop

FeedbackLoop Manim output
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

FeedbackLoop2 Manim output
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)