For a while now I have been working on a bench supply. As part of this I have been trying to get a PID controller to work. At first it was simple, but after asking my Dad about it (he does power electronics), he suggested that I use a cascaded PID loop for controlling the voltage and current using the voltage alone. I have sort of a bench model going, but I don’t really want to start construction until I have everything finalized since blowing things up and making mistakes is kind of expensive for my meager college student budget. Tweaking that without a working bench model that I am willing to blow up is kind of hard, so I started trying to figure out how to simulate it. Being partial to simulink (I’ve used it before with some nice pre-built blocks), I wanted to be able to lay it out graphically like control system diagrams usually show and I also wanted to be able to view plots over time. So thus was born my latest project: SimuPy.
Python seemed like an ideal language for this since I wanted it to look nice, be extensible, and be almost universally cross platform. I am relying heavily on the Qt library because it runs on almost anything and it has the ability to use slots and signals on pretty much any object as well. I guess another option would have been Java, but seeing as I don’t like Java that much, Python was what I went with. In addition, I am weighing a couple other options:
- At the sacrifice of portability, I could make the interface half in C++ and half Python so that I still keep the extensibility without having the requirement of installing python on the person’s computer to get it to work. The simulation part would then turn into a python library that the program would load.
- Write the entire thing in C++ for speed and true multithreading and use Python solely for extensions. This would be a little more difficult since I kind of leverage the dynamic typing thing that Python has going on.
So far, my current structure has everything based on a Model which contains Blocks. In addition, there is a simulation Context which holds information about each Block and where the simulation is in terms of the current step (simulations are stepped over time (dt)). Contexts are also where a Block will store all of its information that it needs to retain during the current step and in the next step. A Block is an operation over time in the flow of the simulation: it could be a simple addition, maybe a derivative or integral, or it could be a full PID controller. Blocks declare themselves to have a number of Input objects and Output objects. Inputs/Outputs are named and have a slot/function called set which sets the value of the input or output. Outputs have a signal called ‘ready’ which inputs connect their ‘set’ slot to. When an output’s ‘set’ method is called, it emits its signal. When an input is set and it sees that all inputs attached to its block are set, it performs a “step” on the block. In addition, there are 3 special blocks: An EntryBlock, ExitBlock, and ModelBlock. Entry and Exit blocks are used in models since a model can have “Entry” and “Exit” points. These points can be used to loop a value from an Exit to an Entry (if they have the same name) or can be used as Input and Output objects if the Model is placed inside a ModelBlock. ModelBlocks are blocks which contain a model which they execute in a child simulation Context to their context. In this way, blocks can be nested. If one creates a Model with 2 Entries and 2 Exits with a pair of those Entries and Exits having the same name and then the Model is attached to a ModelBlock, the ModelBlock will have 1 input and one output to corrispond to the free Entry and Exit on the Model. Models can’t be recursive, but they can be nested so as long as a higher level block doesn’t contain a block which at some child level contains the same higher level block, there can be some sense of re-usability and modularity to a simulation.
Blocks are subclassed into a package called model. The __init__.py file in the model package defines the basic form for a block and then the individual modules in the package define more specific blocks. The blocks then have their constructors cached by reflection so that a block can be constructed by simply naming its name. To extend the blocks available in a simulation, all that must be done is to drop the new module python file into the model folder. I am considering changing this a bit to separate out user-added modules from the “system” modules in kind of the same fashion as I did with the WebSocketServer where I had the files in a folder be loaded into the context of another package.
Simulations are to be stored in an XML format which is going to be more or less human readable and should preserve the look and feel of the simulation. I am still working on the exact format at the moment, but that is the next step.
As for the GUI, I plan on using Qt since it seems the most cross-platform (sorry GTK…Windows needs too much help to load you and PyDev in eclipse doesn’t like the whole introspection thing). I plan on releasing the project under the Apache License (but don’t yet quote me on that or hold me to it…I may choose a different license later once I get more of a feel for how the project would be used). Either way, I plan on publishing the source code on github since it looks like nothing like this really exists in a simple form. Sure, there are clones of Simulink to work with Octave and things like that, but it doesn’t look like there are few, if any, stand-alone applications that do this (except perhaps a paid program called logic.ly, but this should be able to duplicate the functionality of that program as well). I guess it is kind of a niche market since the only people who do this kind of thing usually can afford Simulink and Matlab.
For the record, I do have access to Simulink and Matlab through the University I am attending, but where would the fun be in that?