rparedis 5 лет назад
Родитель
Сommit
17ddfc5891

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
examples/AGV/AGV.drawio


+ 161 - 18
examples/AGV/AGV.py

@@ -1,38 +1,134 @@
 #!/usr/bin/python3
 #!/usr/bin/python3
 # This file was automatically generated from drawio2cbd with the command:
 # This file was automatically generated from drawio2cbd with the command:
-#   /home/red/git/DrawioConvert/__main__.py AGV.drawio -Sargv -e AGV -F CBD -E delta=0.1 -t 100
+#   /home/red/git/DrawioConvert/__main__.py -F CBD -E delta=0.001 -aSgrvs -t 100 AGV.drawio -e AGVVirtual
 
 
 from CBD.CBD import *
 from CBD.CBD import *
 from CBD.lib.std import *
 from CBD.lib.std import *
 from ev3 import _PI as PI
 from ev3 import _PI as PI
 from ev3 import *
 from ev3 import *
+from env import PathBlock
+from CBD.lib.endpoints import *
+
+DELTA_T = 0.001
+
+class OdometryBase(CBD):
+    def __init__(self, block_name, r=(0.03), d=(0.18)):
+        CBD.__init__(self, block_name, input_ports=['phiLdot', 'heading', 'phiRdot'], output_ports=['ydot', 'xdot'])
+
+        # Create the Blocks
+        self.addBlock(ConstantBlock("r", value=(r)))
+        self.addBlock(GenericBlock("sin", block_operator=("sin")))
+        self.addBlock(GenericBlock("cos", block_operator=("cos")))
+        self.addBlock(AdderBlock("B-OUZVyqj9DiQNmVnVL9-16"))
+        self.addBlock(ConstantBlock("half", value=(0.5)))
+        self.addBlock(ProductBlock("div2"))
+        self.addBlock(ProductBlock("mulX"))
+        self.addBlock(ProductBlock("mulY"))
+        self.addBlock(ProductBlock("mulSin"))
+        self.addBlock(ProductBlock("mulCos"))
+
+        # Create the Connections
+        self.addConnection("phiLdot", "B-OUZVyqj9DiQNmVnVL9-16", input_port_name='IN1')
+        self.addConnection("phiRdot", "B-OUZVyqj9DiQNmVnVL9-16", input_port_name='IN2')
+        self.addConnection("half", "div2", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("B-OUZVyqj9DiQNmVnVL9-16", "div2", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("mulX", "ydot", output_port_name='OUT1')
+        self.addConnection("div2", "mulX", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("div2", "mulY", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("mulY", "xdot", output_port_name='OUT1')
+        self.addConnection("heading", "sin", input_port_name='IN1')
+        self.addConnection("heading", "cos", input_port_name='IN1')
+        self.addConnection("mulSin", "mulX", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("sin", "mulSin", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("r", "mulSin", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("r", "mulCos", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("mulCos", "mulY", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("cos", "mulCos", output_port_name='OUT1', input_port_name='IN2')
+
+
+class Odometry(CBD):
+    def __init__(self, block_name, initial=(0,0,0), wheel_radius=(0.03), wheel_axis=(0.18)):
+        CBD.__init__(self, block_name, input_ports=['phiLdot', 'phiRdot'], output_ports=['y', 'heading', 'x'])
+
+        # Create the Blocks
+        self.addBlock(IntegratorBlock("int_w"))
+        self.addBlock(ConstantBlock("delta", value=(DELTA_T)))
+        self.addBlock(OdometryBase("base", r=(wheel_radius)))
+        self.addBlock(IntegratorBlock("int_x"))
+        self.addBlock(IntegratorBlock("int_y"))
+        self.addBlock(ConstantBlock("init_x", value=(initial[0])))
+        self.addBlock(ConstantBlock("init_y", value=(initial[1])))
+        self.addBlock(OdometryW("OCegTr4naEkMIVMNTQIZ-73", r=(wheel_radius), d=(wheel_axis)))
+        self.addBlock(ConstantBlock("init_w", value=(initial[2])))
+
+        # Create the Connections
+        self.addConnection("delta", "int_w", output_port_name='OUT1', input_port_name='delta_t')
+        self.addConnection("delta", "int_y", output_port_name='OUT1', input_port_name='delta_t')
+        self.addConnection("delta", "int_x", output_port_name='OUT1', input_port_name='delta_t')
+        self.addConnection("int_w", "heading", output_port_name='OUT1')
+        self.addConnection("int_w", "base", output_port_name='OUT1', input_port_name='heading')
+        self.addConnection("phiLdot", "base", input_port_name='phiLdot')
+        self.addConnection("phiLdot", "OCegTr4naEkMIVMNTQIZ-73", input_port_name='phiLdot')
+        self.addConnection("phiRdot", "base", input_port_name='phiRdot')
+        self.addConnection("phiRdot", "OCegTr4naEkMIVMNTQIZ-73", input_port_name='phiRdot')
+        self.addConnection("base", "int_x", output_port_name='xdot', input_port_name='IN1')
+        self.addConnection("base", "int_y", output_port_name='ydot', input_port_name='IN1')
+        self.addConnection("int_x", "x", output_port_name='OUT1')
+        self.addConnection("int_y", "y", output_port_name='OUT1')
+        self.addConnection("OCegTr4naEkMIVMNTQIZ-73", "int_w", output_port_name='wdot', input_port_name='IN1')
+        self.addConnection("init_w", "int_w", output_port_name='OUT1', input_port_name='IC')
+        self.addConnection("init_y", "int_y", output_port_name='OUT1', input_port_name='IC')
+        self.addConnection("init_x", "int_x", output_port_name='OUT1', input_port_name='IC')
+
+
+class OdometryW(CBD):
+    def __init__(self, block_name, d=(0.18), r=(0.03)):
+        CBD.__init__(self, block_name, input_ports=['phiLdot', 'phiRdot'], output_ports=['wdot'])
+
+        # Create the Blocks
+        self.addBlock(ConstantBlock("rneg", value=(-r)))
+        self.addBlock(NegatorBlock("OCegTr4naEkMIVMNTQIZ-39"))
+        self.addBlock(AdderBlock("OCegTr4naEkMIVMNTQIZ-43"))
+        self.addBlock(ProductBlock("multr"))
+        self.addBlock(ConstantBlock("invd", value=(1.0/d)))
+        self.addBlock(ProductBlock("multd"))
+
+        # Create the Connections
+        self.addConnection("phiRdot", "OCegTr4naEkMIVMNTQIZ-39", input_port_name='IN1')
+        self.addConnection("phiLdot", "OCegTr4naEkMIVMNTQIZ-43", input_port_name='IN1')
+        self.addConnection("OCegTr4naEkMIVMNTQIZ-39", "OCegTr4naEkMIVMNTQIZ-43", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("OCegTr4naEkMIVMNTQIZ-43", "multr", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("rneg", "multr", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("invd", "multd", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("multr", "multd", output_port_name='OUT1', input_port_name='IN2')
+        self.addConnection("multd", "wdot", output_port_name='OUT1')
 
 
-DELTA_T = 0.1
 
 
 class Controller(CBD):
 class Controller(CBD):
     def __init__(self, block_name, T=(50)):
     def __init__(self, block_name, T=(50)):
-        super().__init__(block_name, input_ports=['color'], output_ports=['velocity', 'heading'])
+        CBD.__init__(self, block_name, input_ports=['color'], output_ports=['velocity', 'heading'])
 
 
         # Create the Blocks
         # Create the Blocks
         self.addBlock(ConstantBlock("v", value=(0.1)))
         self.addBlock(ConstantBlock("v", value=(0.1)))
         self.addBlock(ConstantBlock("threshold", value=(-T)))
         self.addBlock(ConstantBlock("threshold", value=(-T)))
         self.addBlock(AdderBlock("sum"))
         self.addBlock(AdderBlock("sum"))
-        self.addBlock(ProductBlock("mult"))
-        self.addBlock(PID("PID"))
+        self.addBlock(ClampBlock("limit", min=(-1), max=(1)))
+        self.addBlock(ProductBlock("gOsFvuXVd93uCKtzi7wK-4"))
+        self.addBlock(ConstantBlock("gamma", value=(PI/2)))
 
 
         # Create the Connections
         # Create the Connections
         self.addConnection("v", "velocity", output_port_name='OUT1')
         self.addConnection("v", "velocity", output_port_name='OUT1')
-        self.addConnection("v", "mult", output_port_name='OUT1', input_port_name='IN1')
         self.addConnection("color", "sum", input_port_name='IN1')
         self.addConnection("color", "sum", input_port_name='IN1')
         self.addConnection("threshold", "sum", output_port_name='OUT1', input_port_name='IN2')
         self.addConnection("threshold", "sum", output_port_name='OUT1', input_port_name='IN2')
-        self.addConnection("mult", "PID", output_port_name='OUT1', input_port_name='IN1')
-        self.addConnection("sum", "mult", output_port_name='OUT1', input_port_name='IN2')
-        self.addConnection("PID", "heading", output_port_name='OUT1')
+        self.addConnection("sum", "limit", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("limit", "gOsFvuXVd93uCKtzi7wK-4", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("gOsFvuXVd93uCKtzi7wK-4", "heading", output_port_name='OUT1')
+        self.addConnection("gamma", "gOsFvuXVd93uCKtzi7wK-4", output_port_name='OUT1', input_port_name='IN2')
 
 
 
 
 class PID(CBD):
 class PID(CBD):
-    def __init__(self, block_name, Kp=(0.3), Ki=(0.0), Kd=(0.5)):
-        super().__init__(block_name, input_ports=['IN1'], output_ports=['OUT1'])
+    def __init__(self, block_name, Kp=(0.03), Ki=(0.0), Kd=(0.05)):
+        CBD.__init__(self, block_name, input_ports=['IN1'], output_ports=['OUT1'])
 
 
         # Create the Blocks
         # Create the Blocks
         self.addBlock(ConstantBlock("Kp", value=(Kp)))
         self.addBlock(ConstantBlock("Kp", value=(Kp)))
@@ -40,7 +136,7 @@ class PID(CBD):
         self.addBlock(ConstantBlock("Kd", value=(Kd)))
         self.addBlock(ConstantBlock("Kd", value=(Kd)))
         self.addBlock(IntegratorBlock("int"))
         self.addBlock(IntegratorBlock("int"))
         self.addBlock(DerivatorBlock("deriv"))
         self.addBlock(DerivatorBlock("deriv"))
-        self.addBlock(ConstantBlock("delta_t", value=(0.01)))
+        self.addBlock(ConstantBlock("delta_t", value=(DELTA_T)))
         self.addBlock(ProductBlock("prod"))
         self.addBlock(ProductBlock("prod"))
         self.addBlock(ProductBlock("imult"))
         self.addBlock(ProductBlock("imult"))
         self.addBlock(ProductBlock("dmult"))
         self.addBlock(ProductBlock("dmult"))
@@ -69,8 +165,8 @@ class PID(CBD):
 
 
 
 
 class DifferentialDrive(CBD):
 class DifferentialDrive(CBD):
-    def __init__(self, block_name, wheel_axis=(0.18), wheel_radius=(0.03), max_wheel_speed=(170/60*2*PI), p=(0.82)):
-        super().__init__(block_name, input_ports=['velocity', 'steering'], output_ports=['phiLdot', 'phiRdot'])
+    def __init__(self, block_name, wheel_axis=(0.18), wheel_radius=(0.03), max_wheel_speed=(170/60*2*PI), p=(0.85)):
+        CBD.__init__(self, block_name, input_ports=['velocity', 'steering'], output_ports=['phiLdot', 'phiRdot'])
 
 
         # Create the Blocks
         # Create the Blocks
         self.addBlock(ProductBlock("steeringMult"))
         self.addBlock(ProductBlock("steeringMult"))
@@ -82,7 +178,7 @@ class DifferentialDrive(CBD):
         self.addBlock(NegatorBlock("neg"))
         self.addBlock(NegatorBlock("neg"))
         self.addBlock(ProductBlock("leftMult"))
         self.addBlock(ProductBlock("leftMult"))
         self.addBlock(ProductBlock("rightMult"))
         self.addBlock(ProductBlock("rightMult"))
-        self.addBlock(ConstantBlock("WheelRadius", value=(wheel_radius)))
+        self.addBlock(ConstantBlock("WheelRadius", value=(1.0/wheel_radius)))
         self.addBlock(ClampBlock("leftClamp", min=(-p*max_wheel_speed), max=(p*max_wheel_speed)))
         self.addBlock(ClampBlock("leftClamp", min=(-p*max_wheel_speed), max=(p*max_wheel_speed)))
         self.addBlock(ClampBlock("rightClamp", min=(-p*max_wheel_speed), max=(p*max_wheel_speed)))
         self.addBlock(ClampBlock("rightClamp", min=(-p*max_wheel_speed), max=(p*max_wheel_speed)))
 
 
@@ -107,12 +203,12 @@ class DifferentialDrive(CBD):
 
 
 
 
 class AGV(CBD):
 class AGV(CBD):
-    def __init__(self, block_name):
-        super().__init__(block_name, input_ports=[], output_ports=[])
+    def __init__(self, block_name, wheel_radius=(0.03), wheel_axis=(0.18)):
+        CBD.__init__(self, block_name, input_ports=[], output_ports=[])
 
 
         # Create the Blocks
         # Create the Blocks
         self.addBlock(Controller("Controller", T=(50)))
         self.addBlock(Controller("Controller", T=(50)))
-        self.addBlock(DifferentialDrive("StaticPlant"))
+        self.addBlock(DifferentialDrive("StaticPlant", wheel_axis=(wheel_axis), wheel_radius=(wheel_radius)))
         self.addBlock(ReflectionSensorBlock("colorSensor", port_name=('1')))
         self.addBlock(ReflectionSensorBlock("colorSensor", port_name=('1')))
         self.addBlock(MotorActuatorBlock("LeftMotor", port_name=('B')))
         self.addBlock(MotorActuatorBlock("LeftMotor", port_name=('B')))
         self.addBlock(MotorActuatorBlock("RightMotor", port_name=('C')))
         self.addBlock(MotorActuatorBlock("RightMotor", port_name=('C')))
@@ -125,3 +221,50 @@ class AGV(CBD):
         self.addConnection("StaticPlant", "LeftMotor", output_port_name='phiLdot', input_port_name='IN1')
         self.addConnection("StaticPlant", "LeftMotor", output_port_name='phiLdot', input_port_name='IN1')
 
 
 
 
+class AGVVirtual(CBD):
+    def __init__(self, block_name, wheel_radius=(0.03), wheel_axis=(0.18), path_file=("paths/falcon.csv"), initial=(0, 0, 0)):
+        CBD.__init__(self, block_name, input_ports=[], output_ports=[])
+
+        # Create the Blocks
+        self.addBlock(Controller("Controller", T=(20)))
+        self.addBlock(DifferentialDrive("StaticPlant", wheel_axis=(wheel_axis), wheel_radius=(wheel_radius)))
+        self.addBlock(PathBlock("environment", path_file=(path_file)))
+        self.addBlock(PositionCollectorBlock("plot"))
+        self.addBlock(Odometry("vKpeSHbH8nwqUBcX28lP-0", initial=(initial)))
+        self.addBlock(SignalCollectorBlock("headingPlot"))
+
+        # Create the Connections
+        self.addConnection("Controller", "StaticPlant", output_port_name='heading', input_port_name='steering')
+        self.addConnection("Controller", "StaticPlant", output_port_name='velocity', input_port_name='velocity')
+        self.addConnection("environment", "Controller", output_port_name='color', input_port_name='color')
+        self.addConnection("StaticPlant", "vKpeSHbH8nwqUBcX28lP-0", output_port_name='phiLdot', input_port_name='phiLdot')
+        self.addConnection("StaticPlant", "vKpeSHbH8nwqUBcX28lP-0", output_port_name='phiRdot', input_port_name='phiRdot')
+        self.addConnection("vKpeSHbH8nwqUBcX28lP-0", "plot", output_port_name='x', input_port_name='X')
+        self.addConnection("vKpeSHbH8nwqUBcX28lP-0", "environment", output_port_name='x', input_port_name='x')
+        self.addConnection("vKpeSHbH8nwqUBcX28lP-0", "plot", output_port_name='y', input_port_name='Y')
+        self.addConnection("vKpeSHbH8nwqUBcX28lP-0", "environment", output_port_name='y', input_port_name='y')
+        self.addConnection("environment", "headingPlot", output_port_name='offset', input_port_name='IN1')
+
+
+class Test(CBD):
+    def __init__(self, block_name):
+        CBD.__init__(self, block_name, input_ports=[], output_ports=[])
+
+        # Create the Blocks
+        self.addBlock(Controller("Controller", T=(50)))
+        self.addBlock(SignalCollectorBlock("plot"))
+        self.addBlock(SignalCollectorBlock("headingPlot"))
+        self.addBlock(TimeBlock("x_KHUbakK0q4-W2-4amr-0"))
+        self.addBlock(DifferentialDrive("StaticPlant", wheel_axis=(0.18), wheel_radius=(0.03)))
+        self.addBlock(Odometry("mYLcSpGE1b6euuJ7r4uY-0", initial=([0, -3, 0])))
+
+        # Create the Connections
+        self.addConnection("x_KHUbakK0q4-W2-4amr-0", "Controller", output_port_name='OUT1', input_port_name='color')
+        self.addConnection("Controller", "StaticPlant", output_port_name='velocity', input_port_name='velocity')
+        self.addConnection("Controller", "StaticPlant", output_port_name='heading', input_port_name='steering')
+        self.addConnection("StaticPlant", "mYLcSpGE1b6euuJ7r4uY-0", output_port_name='phiLdot', input_port_name='phiLdot')
+        self.addConnection("StaticPlant", "mYLcSpGE1b6euuJ7r4uY-0", output_port_name='phiRdot', input_port_name='phiRdot')
+        self.addConnection("mYLcSpGE1b6euuJ7r4uY-0", "headingPlot", output_port_name='y', input_port_name='IN1')
+        self.addConnection("mYLcSpGE1b6euuJ7r4uY-0", "plot", output_port_name='x', input_port_name='IN1')
+
+

+ 48 - 0
examples/AGV/AGV_experiment.py

@@ -0,0 +1,48 @@
+#!/usr/bin/python3
+# This file was automatically generated from drawio2cbd with the command:
+#   /home/red/git/DrawioConvert/__main__.py -F CBD -e AGVVirtual -E delta=0.1 -aSgrvs -t 100 AGV.drawio
+
+from ev3 import _PI as PI
+from ev3 import *
+from env import PathBlock
+from AGV import *
+from CBD.simulator import Simulator
+import matplotlib.pyplot as plt
+from CBD.realtime.plotting import PlotManager, LinePlot, follow
+
+DELTA_T = 0.005
+
+fig, ax1 = plt.subplots(1, 1)
+# fig, (ax1, ax2) = plt.subplots(2, 1)
+ax1.set_xlim((-4.5, 4.5))
+ax1.set_ylim((-3.5, 5.5))
+# ax1.set_xlim((0, 100))
+# ax1.set_ylim((-0.07, 0.1))
+# ax2.set_xlim((0, 100))
+# ax2.set_ylim((-3, -2.7))
+
+cbd = AGVVirtual("AGVVirtual", initial=[0, -3, 0])
+# cbd = Test("Test")
+
+path = cbd.findBlock("environment")[0].path
+ax1.plot([e[0] for e in path], [e[1] for e in path], color='red')
+
+manager = PlotManager()
+manager.register("plot", cbd.findBlock("plot")[0], (fig, ax1), LinePlot())
+# manager.register("heading", cbd.findBlock("headingPlot")[0], (fig, ax2), LinePlot())
+# manager.connect('plot', 'update', lambda d, axis=ax1: axis.set_xlim((min(d[0]), max(d[0]))))
+# manager.connect('plot', 'update', lambda d, axis=ax1: axis.set_ylim((min(d[1]), max(d[1]))))
+# manager.connect('heading', 'update', lambda d, axis=ax2: axis.set_xlim(follow(d[0], 10.0, lower_bound=0.0)))
+# manager.connect('heading', 'update', lambda d, axis=ax2: axis.set_ylim((min(d[1]), max(d[1]))))
+
+# Run the Simulation
+sim = Simulator(cbd)
+sim.setRealTime(scale=0.1)
+sim.setProgressBar()
+sim.setDeltaT(DELTA_T)
+sim.run(100.0)
+
+plt.show()
+
+# print(cbd.findBlock("Controller")[0].getSignal("velocity"))
+# print(cbd.findBlock("plot")[0].data_xy)

+ 68 - 0
examples/AGV/env.py

@@ -0,0 +1,68 @@
+"""
+Pure code-based environment building blocks for AGV simulation.
+"""
+from CBD.CBD import BaseBlock
+
+class PathBlock(BaseBlock):
+	def __init__(self, block_name, path_file, path_width=0.06, grace=0.002, black=0, grey=0, white=100):
+		BaseBlock.__init__(self, block_name, ["x", "y"], ["color", "offset"])
+
+		self.path_width = path_width
+		self.grace = grace
+		self.black = black
+		self.grey = grey
+		self.white = white
+
+		self.path = []
+		with open(path_file, 'r') as file:
+			for line in file:
+				try:
+					pos = [float(l.strip()) for l in line.split(',')]
+				except Exception as e:
+					continue
+				self.path.append(pos[:2])
+
+	def compute(self, curIteration):
+		position = self.getInputSignal(curIteration, "x").value, self.getInputSignal(curIteration, "y").value
+		color = self.white
+		offset = float('inf')
+
+		for i, p1 in enumerate(self.path):
+			p2 = self.path[i-1]
+
+			dist = self.distance_to_line(p2, p1, position)
+			if dist < offset:
+				offset = dist
+
+			if dist < (self.path_width / 2.0):
+				color = self.black
+			elif color == self.white and dist <= (self.path_width / 2.0 + self.grace):
+				color = self.grey
+
+		self.appendToSignal(color, "color")
+		self.appendToSignal(offset, "offset")
+
+	def distance_to_line(self, p1, p2, p3):
+		"""
+		Computes the distance from a point p3 to the line segment given by (p1, p2).
+		Implementation based upon:
+			https://stackoverflow.com/a/6853926
+		"""
+		px = p2[0] - p1[0]
+		py = p2[1] - p1[1]
+
+		norm = px * px + py * py
+
+		if norm == 0.0:
+			ud = 0.0
+		else:
+			ud = float((p3[0] - p1[0]) * px + (p3[1] - p1[1]) * py) / norm
+			ud = min(max(ud, 0.0), 1.0)  # limits line segment to endpoints
+
+		xd = p1[0] + ud * px
+		yd = p1[1] + ud * py
+
+		dx = xd - p3[0]
+		dy = yd - p3[1]
+
+		return (dx * dx + dy * dy) ** 0.5

+ 16 - 0
examples/AGV/paths/falcon.csv

@@ -0,0 +1,16 @@
+x, y
+-0.5, 1
+-1, 2
+-1, 5
+-2, 5
+-3, 1
+-3, -1
+-2, -3
+2, -3
+3, -1
+3, 1
+2, 5
+1, 5
+1, 2
+0.5, 1
+-0.5, 1

+ 43 - 0
examples/AGV/paths/show_path.py

@@ -0,0 +1,43 @@
+# Read a CSV path specification and plot the data
+import matplotlib.pyplot as plt
+
+# Read and parse the file
+filename = input('Enter a file: ')
+x = []
+y = []
+with open(filename, 'r') as file:
+    for line in file:
+        try:
+            pos = [float(l.strip()) for l in line.split(',')]
+        except Exception as e:
+            continue
+        x.append(pos[0])
+        y.append(pos[1])
+
+# Set the axis ranges
+padding = 0.5
+xmin = min(x)
+ymin = min(y)
+xmax = max(x)
+ymax = max(y)
+xrange = abs(xmax - xmin)
+yrange = abs(ymax - ymin)
+
+if xrange > yrange:
+    yoff = (xrange - yrange) / 2
+    ymin -= yoff
+    ymax += yoff
+elif yrange > xrange:
+    xoff = (yrange - xrange) / 2
+    xmin -= xoff
+    xmax += xoff
+
+xmin -= padding
+ymin -= padding
+xmax += padding
+ymax += padding
+
+# Plot the path
+plt.plot(x, y)
+plt.axis([xmin, xmax, ymin, ymax])
+plt.show()

+ 5 - 4
src/CBD/lib/endpoints.py

@@ -46,7 +46,8 @@ class SignalCollectorBlock(CollectorBlock):
 		The collected data, as a pair of lists; i.e. the unzipped form of
 		The collected data, as a pair of lists; i.e. the unzipped form of
 		:code:`xs, ys`.
 		:code:`xs, ys`.
 		"""
 		"""
-		return [x for x, _ in self.data], [y for _, y in self.data]
+		D = self.data[:]
+		return [x for x, _ in D], [y for _, y in D]
 
 
 
 
 class PositionCollectorBlock(CollectorBlock):
 class PositionCollectorBlock(CollectorBlock):
@@ -57,8 +58,7 @@ class PositionCollectorBlock(CollectorBlock):
 		CollectorBlock.__init__(self, name, ["X", "Y"], [])
 		CollectorBlock.__init__(self, name, ["X", "Y"], [])
 
 
 	def compute(self, curIteration):
 	def compute(self, curIteration):
-		x = self.getInputSignal(curIteration, "X").value
-		y = self.getInputSignal(curIteration, "Y").value
+		x, y = self.getInputSignal(curIteration, "X").value, self.getInputSignal(curIteration, "Y").value
 		self.data.append((x, y))
 		self.data.append((x, y))
 
 
 	def clear(self):
 	def clear(self):
@@ -70,7 +70,8 @@ class PositionCollectorBlock(CollectorBlock):
 		The collected data, as a pair of lists; i.e. the unzipped form of
 		The collected data, as a pair of lists; i.e. the unzipped form of
 		:code:`xs, ys`.
 		:code:`xs, ys`.
 		"""
 		"""
-		return [x for x, _ in self.data], [y for _, y in self.data]
+		D = self.data[:]
+		return [x for x, _ in D], [y for _, y in D]
 
 
 
 
 class StatisticsCollectorBlock(CollectorBlock):
 class StatisticsCollectorBlock(CollectorBlock):

+ 2 - 2
src/CBD/lib/std.py

@@ -405,7 +405,7 @@ class IntegratorBlock(CBD):
 		# self.addConnection("delayState", "sumState")
 		# self.addConnection("delayState", "sumState")
 		# self.addConnection("sumState", "OUT1")
 		# self.addConnection("sumState", "OUT1")
 
 
-		# FORWARD DIFFERENCE:
+		# FORWARD EULER:
 		# self.addBlock(ProductBlock("mul"))
 		# self.addBlock(ProductBlock("mul"))
 		# self.addBlock(AdderBlock("sum"))
 		# self.addBlock(AdderBlock("sum"))
 		# self.addBlock(AdderBlock("sum_ic"))
 		# self.addBlock(AdderBlock("sum_ic"))
@@ -423,7 +423,7 @@ class IntegratorBlock(CBD):
 		# self.addConnection("mul", "sum_ic")
 		# self.addConnection("mul", "sum_ic")
 		# self.addConnection("sum", "OUT1")
 		# self.addConnection("sum", "OUT1")
 
 
-		# BACKWARD DIFFERENCE:
+		# BACKWARD EULER:
 		self.addBlock(ConstantBlock(block_name="zero", value=0))
 		self.addBlock(ConstantBlock(block_name="zero", value=0))
 		self.addBlock(DelayBlock(block_name="delayIn"))
 		self.addBlock(DelayBlock(block_name="delayIn"))
 		self.addBlock(ProductBlock(block_name="multDelta"))
 		self.addBlock(ProductBlock(block_name="multDelta"))

+ 16 - 3
src/CBD/solver.py

@@ -235,14 +235,27 @@ class Matrix:
 
 
 	Numpy could be used to even further increase efficiency, but this increases the
 	Numpy could be used to even further increase efficiency, but this increases the
 	required dependencies for external hardware systems (that may not provide these options).
 	required dependencies for external hardware systems (that may not provide these options).
+
+	Note:
+		Internally, the matrix is segmented into chunks of 500.000.000 items.
 	"""
 	"""
 	def __init__(self, rows, cols):
 	def __init__(self, rows, cols):
 		self.rows = rows
 		self.rows = rows
 		self.cols = cols
 		self.cols = cols
-		self.data = [0] * (rows * cols)
+		self.size = rows * cols
+		self.__max_list_size = 500 * 1000 * 1000
+		self.data = [[0] * ((rows * cols) % self.__max_list_size)]
+		for r in range(self.size // self.__max_list_size):
+			self.data.append([0] * self.__max_list_size)
 
 
 	def __getitem__(self, idx):
 	def __getitem__(self, idx):
-		return self.data[idx[0] * self.cols + idx[1]]
+		absolute = idx[0] * self.cols + idx[1]
+		outer = absolute // self.__max_list_size
+		inner = absolute % self.__max_list_size
+		return self.data[outer][inner]
 
 
 	def __setitem__(self, idx, value):
 	def __setitem__(self, idx, value):
-		self.data[idx[0] * self.cols + idx[1]] = value
+		absolute = idx[0] * self.cols + idx[1]
+		outer = absolute // self.__max_list_size
+		inner = absolute % self.__max_list_size
+		self.data[outer][inner] = value