Saturday, December 8, 2018

Linear Quadratic Regulator control of a single phase UPS with Scilab

So I am gradually getting used to Scilab. The documentation for Scilab is a bit scarce. The first link that I use as reference is the Scilab online help:
https://help.scilab.org/docs/6.0.1/en_US/index.html

Another link that's good for getting started with controls in Scilab is:
https://en.wikibooks.org/wiki/Control_Systems/Open_source_tools/Scilab

Anyway, to get started, I chose a problem that I spent a good amount of time during my PhD - control of a UPS. Typically, a UPS can be controlled with PI controllers in the synchronous reference frame, a topic that I have covered in a tutorial:
http://pythonpowerelectronics.com/contents/tutorials/tutorial7/post.html

Using multi-variable control has some significant advantages - you do not need to design multiple nested loops ensuring that the inner loop is super fast and the outer loop is reasonably fast. Here, there is a single loop. To begin with, a system, in the case, a single-phase UPS, which is an H-bridge inverter with an LC filter at the output, has to be represented in the state space form:

dx/dt = Ax + Bu
y = Cx + Du

Here, x is the state vector. y is the output. And u is the input. There is a very detailed theory behind state space analysis, and I would encourage you to read, if you need to know more. But, for this post, the state variables are those variables that have dynamics - inductor current and capacitor voltage. So,

x = [if Cf]

This is the code snippet for the entire LQR design:


To achieve a 120V RMS voltage output, I have chosen a 300V dc voltage bus (which is a bit of an overkill, but more on that later), a 1milli Henry inductor and a 200 micro Farad capacitor. I have written the dynamical equations at the top as comments and these can be expressed as state space equations with A, B, C, D matrices below that. I will be implementing the state feedback in discrete time and the sampling rate is 20 microseconds.

The first part - controllability of the system. For a system expressed in the state space form, the controllability matrix is:
[B AB]

Again, control theory. Without getting into details, the matrix B, AB, A^2B, .... define the space of vectors that can be achieved with a finite input. So, for a system with 2 states, B and AB, will map a state space. What this means is that the 2 vectors that are B and AB, will be like the x-axis and y-axis of this new space. In the 2 dimensional space, the x-axis and y-axis can be used to express any vector in 2 dimensions. And when any 2 vectors can be used to express any other vector in a 2 dimensional space, the 2 vectors are said to span the space. And if x-axis (1, 0) and y-axis (0,1) are put together as a matrix:
[0 1,
1 0]

This matrix has dimension 2 - which means 2 linearly independent vectors. Linearly independent vectors are vectors who cannot be projected on each other. Mathematically, of course, the definition is a bit more rigorous. A set of vectors v1, v2, ..., vn are linearly dependent if there exists a set of scalars a1, a2, ..., an such that:

a1*v1 + a2*v2 + .... + an*vn = 0

With all "a"s not being zero. If such a relation cannot be expressed, the vectors are linearly independent. Anyway, coming back to the matrix [B AB]. If this matrix has dimension 2 for our system of 2 state variables, the system is fully controllable. And if you try to execute the variable "controllability", you will find the rank is 2. Which means our UPS is fully controllable. And that is good news.

So, this means that there exists a feedback matrix K, which can be designed in various ways one of which is Linear Quadratic Regulator (LQR), such that the closed loop system:

u = K (xref - x)

Will be asymptotically stable. This means, when influenced by a disturbance, the system will return to a finite region around the reference (xref). And that is what we want.

The next part is to define the linear system for which I use the command syslin. This is a command for defining everything - state space representations, transfer functions etc. After that I define a matrix Q and a scalar (actually another matrix but since we have only one input, a scalar) R. These are for the function LQR. To obtain the state feedback matrix K from LQR, you need to solve the algebraic Riccatti equation. This equations takes inputs Q which are the weights giving to the state variables and R is the penalty on the control signal. Notice how I have given a much higher weight to the capacitor voltage vf (1) than the inductor current (if) as it is the capacitor voltage that we want to regulate. The penalty on the control R is 0.01 which is a number I chose by trial and error and I would recommend that you fiddle with Q and R too.

The result is the K matrix. The rest of the file I will talk about in a later blog post as that is a low pass filter I was trying to add after the controller but didn't work.

I take this K matrix and insert it into my simulator control code as:

mod_signal = (9.9966672*(volt_ref - volt_cf1) + 3.2144996*(curr_ref - curr_if1))/300.0

One important thing to note here is the factor 300 by which I divide the control signal. The control in this case is a modulation signal fed to a pulse width control. So it must be restricted between -1 and 1. However, the control as designed doesn't know about this and will try to spit out an actual value. So, to convert this actual value into a modulation signal, I divide it by the dc bus voltage.


This the result. The light blue line is the reference and the purple line is the actual voltage. To begin with, it doesn't track - there is a steady state error. I will address this in detail in a future blog. However, for now it is to do with the reference xref = [ifref vfref]. The reference for the voltage is easy, because we want to regulate it. But the reference for the inductor current is a bit tricky and for now I have assumed it to be the same as the output load current. Hence, the approximation. But, there is something very special with state feedback. Notice how quickly, the tracking begins - almost immediate. With PI control it would take several cycles to get even close. Also, there is no conversion from stationary reference frame to synchronous reference frame. So this is a great way to produce voltages that have harmonics. Check out the next plot.




To generate harmonic voltages with the regular method, you would need several reference frame transformations. Here, everything is instantaneous.

There are a couple of problems - the steady state error is the biggest glaring problem. Besides, a few others like a bit more ripple than expected. This topic is by no means over and there will be more blog posts to come.

Stay tuned for updates. I'll be back!

Sunday, September 2, 2018

Implementing class based views and class hierarchy

So finally I managed to migrate the function based views in the Django code to class based views. This has resulted in significant decrease in code repetition and the code is now much more readable and maintainable.

To start, I needed to create a class hierarchy. The base class in SimulationData which extracts the simulation model instance, reads the circuit files and processes them. The next layer is ListControlVariables that inherits SimulationData and extracts control files and their variables.

The class SimulationData is:


The methods of the class perform functions like retrieving the simulation model instance, getting the circuit schematic model instances, checking if there are errors in reading the circuit files, processing the simulation circuit files and updating the database.

A few are obvious like getting the simulation model instance or circuit files model instances. Some not so obvious. When the circuit schematics are processed, they are checked for errors. These errors are both errors like component not found, unmatching jump labels etc. Also, connectivity errors are found - broken branches, jumps next to nodes etc. This checking is done almost every time because the simulator uses the "component_objects" dictionary of Python objects to contain information about every component found in the circuit. This is essential because different types of components have different classes - example resistors have Resistor class while voltage sources have VoltageSource class. Every time a class is found, it is instantiated and added to component_objects. So component_objects is the key to processing circuit components. By having the process_circuit_schematics() method in the SimulationData class, it is available to all other classes that inherit it.

Another non-obvious class method in update_db(). The database contains every circuit component in its records. And the details include the cell position of the circuit component and the polarity if any. These can always change as a user can move the components around in the schematic spreadsheet. The update_db() method updates the information in the schematic with the database.

The SimulationData class is the base class as the above methods are needed for every simulation. The next layer in the class hierarchy is the control class which is called ListControlVariables class. This is because there may be some simulations that don't have control. However, to figure out how a control function works, the circuit files will need to be processed and this means control needs SimulationData. ListControlVariables is as below:


This class provides method to perform repeated tasks with respect to control files. Extracting the control file model instance. Providing a list of all special variables in the control file as context variables.

With these two classes implemented the amount of times the process circuit schematic code was written to get back the dictionary of component_objects or that of components_found has decreased. Moreover, with component_objects and component_founds class attributes rather than regular variables, the function call is much less cumbersome as compared to before.

The next blog post will describe how class based views for listing circuit items will be created.

Sunday, August 19, 2018

Class Based Views in the Django app

The past few releases have seen small to significant changes in the Django web app. And every time I made these changes, the one thing that struck me is how awful the Django code is. At the time I was creating the web app for the circuit simulator, I was just getting into web development and I had little knowledge of best practices, what a REST API was and of course the look and feel of a good website. I just wanted something that could be run off a server.

Now that I have been designing websites for a while and also had positions of full stack web developer, I realize I need to change the code. To begin with in the Django app, I used function based views which results in a lot of redundant and repeated code. I need to convert this to class based views to be able to reuse code and implement DRY.

The next is that for some reason, I wanted to have the minimum number of links in the app. No reason for doing so. But because I had just one link /new-simulation/ for the entire simulation editing and running to see the results, I have incredibly complex and difficult to maintain code in the function view that serves this URL. As an example, for all purposes, I have used submit buttons that have different values or submit buttons with hidden fields to pass information back to the view function. In reality, the only time you need to use a submit button is while submitting a form. To access any other information, only a GET request is needed. To pass information between URL and view function, there are much more convenient methods such as URL parameters. This will result in a several links for each operation performed. But so what? URLs can be plentiful. the views that serve them need to be most efficient.

Next, the look and feel of the app. When I started, I didn't know any frontend development. Now I regularly code in JavaScript and use CSS, Bootstrap and sometimes even combine all these in a frontend framework like Angular. So, I need to bring in Bootstrap and custom CSS pages into this web app for the circuit simulator to make it much more visually appealing.

The migration to Django class based views has started. It is progressing page by page and might be done in another 2 weeks. The front end changes will take another week at most. In a month, a much better web app should be available which I hope will have more engineers using it.

As before, to get regular updates on the project, follow my Facebook page:
https://www.facebook.com/pythonpowerelectronics

And to learn how to use the simulator or access other tutorials, check out my YouTube channel:
https://www.youtube.com/channel/UCxVbKNK18A_a9Ohd0Kb7kNA

Thursday, July 26, 2018

Importing and exporting parameters in the Django web app

The main reason for creating the Django based web app was to have a single interface for most tasks related to simulating circuits. For example, in the command line interface, you would need to do the following:

1. Create circuit schematics in a spreadsheet.
2. Launch the circuit simulator in command line with "python circuit_solver.py"
3. Enter the simulation parameters in circuit_inputs.csv.
4. Enter the component parameters in the parameter spreadsheets.
5. Enter the control variables in the control descriptors.
6. When the simulation is running, run separate plot commands in either matplotlib or gnuplot to view results.

There are a few steps that are fundamental and unlikely to change. Those are circuits will be designed in circuit schematics with a spreadsheet software and saved as .csv files and control functions will have to be written in text files as .py files. But the command line needs a lot of back and forth between spreadsheets and also plotting the output needs a whole new set of commands and the need to learn gnuplot or matplotlib.

The Django web app simplified that. Only circuit schematics and control functions are still independent. All other parameters and commands can be given in the web interface by clicking on buttons and filling forms. Much more convenient without needing to go back and forth. However, the problem came while transferring simulations. Because now the simulation is stored in a SQLITE database and copy pasting the database will transfer the entire simulation collection rather than just what you want. What is needed is a way to extract the simulation as a set of simple files that can be zipped and copied or emailed.

What I have now added to the Django app has been an export button and an import button. After creating your simulation and checking that it runs the way you expect it to, you can click on export buttons on every circuit component parameters edit page and export all the parameters of that circuit schematic as a .csv file. Additionally, when you want to recreate a simulation in another computer or transfer it to someone else, you need to send the .csv files and use the import button to upload the parameter spreadsheet and copy the parameters to the database. The same process can be performed for control functions as well.

These features are available in version 2.0.7 for Python 2 and in version 4.0.3 for Python 3.
http://pythonpowerelectronics.com/contents/softwaredownloads.html

I am already finding this tool useful in maintaining simulations in all my computers and creating and arranging libraries of simulations. Try it out and let me know if you have any questions or comments. To know more about installing the software, check out my You Tube video:
https://www.youtube.com/watch?v=JRKUenYBIA4&t=9s
https://www.youtube.com/watch?v=jM28A2MD8u4&t=41s

Monday, July 2, 2018

Transformer circuits, Python 3/Django 2

I have been traveling since June 1 and have not been able to make any YouTube videos. I will get back to Canada on July 9 which is next week. The past month I have been trying out different circuits and also testing the migration to Python 3. The plan is to completely migrate both the command line and web app to Python 3 within six months as Python 2 is already legacy software.

While simulating a flyback converter, I found a bug in how I calculate the number of independent loops in a circuit. From fundamental network analysis, the number of loops is equal to B-N+1 where B is the number of branches, N the number of nodes. I never bothered to question where the "1" came from. The "1" stands for the number if connected sub-circuits. So, in a normal circuit without any transformer or any other machine that has electrically isolated parts, there is only one connected circuit. In such a case, the number of loops will be B-N+1.

But if the circuit has a transformer or a machine like an induction motor, there will be parts of the circuit that are electrically isolated from each other. So for a transformer with two windings, there will be two sub-circuits. In such as case, the number of independent loops will be B-N+2. In general, if there are M isolated sub-circuits, the number of loops will be B-N+M.

I was already calculating the number of connected sub-circuits by an iterative algorithm. Start with one branch and look for branches that are incident at either of it's nodes and keep growing the connected circuit. Count the number of connected circuits. This count is used to determine the number of independent loops that are expected so that additional loops will be considered linear combinations of the previous loops and can be deleted.

The next bug I found was with using the Django based web app with Python 3. Previously I was using Python 2.7 and Django 1.8. Both of these are grossly outdated. Latest version of Python is 3.6 and Django is 2.0.5. Turns out in versions of Django after 1.9, apps are not loaded when the server is started. An app is a component of the project which contains the view functions that define behaviour when a URL is accessed and also defines the database structure. In the circuit simulator, there is one app called simulations. This is listed in INSTALLED_APPS list in the settings.py file. Usually, Django will load all apps that are listed once the runserver command is executed. Turns out in Django 1.9 and later, this doesn't happen automatically. I guess this is to allow lazy loading. Which means, if an app is not used, no need to load it and burden the server.

So, if I run the server, everything works fine until I reach the stage of the running the simulation. At this point, it throws an error that apps are not loaded yet. To solve this, at the top of the views.py file I need to add "import django" and "django.setup()". This causes the app to be loaded and now the simulator works as before.

It will be another week before I get back to Canada. Quite a few updates to the website are pending. Will be happy to get back with my Linux machine soon. Developing on Windows sucks.

Saturday, May 26, 2018

YouTube playlists and lecture series on using the simulator

I reorganized my YouTube channel into playlists:
https://www.youtube.com/channel/UCxVbKNK18A_a9Ohd0Kb7kNA/playlists

So now there is a playlist for every topic rather than just a collection of videos. I am trying to break the videos up into pieces as it is easier to record them and also upload them on YouTube. A playlist ensures everything is in order inside a topic.

Recording videos has been a learning experience. I am gradually getting the hang of using recording and editing software. I use Kazam in Linux to record the screen while programming or going through slides. I use Kdenlive to edit the videos. Initially the videos needed only a clipping of the last extra seconds. Now I need to boost up the volume as I find the volume is quite low. Also as simulations will get longer, it will be needed to pause the video often and that means cut out parts that are just pauses.

The first major lecture series is one on how to use the circuit simulator web app version. How to install the circuit simulator is a separate playlist:
https://www.youtube.com/watch?v=JRKUenYBIA4&list=PL-_jTul4we2SJqPHEANecXJXDFRztsGHM

This playlist contains videos on how to install Python Power Electronics in Windows and Linux.

The lecture series on how to use the simulator should be watched after you have installed a version of the circuit simulator on your system.
https://www.youtube.com/watch?v=Vv0wYq0BPsU&list=PL-_jTul4we2TFRLC8KZcxOMrqhRzI0J3Y

The circuit chosen is a single-phase diode bridge rectifier with a dc capacitor at the output. The load consists of a constant resistor load and a switched resistor load. The first part of the lecture series describes the simulator without using any control functions. The latter part of the simulator describes how to write control functions. The main emphasis here is on the special variables that can be defined with control functions and how they are to be used.

As time goes on more detailed simulation results will be described in the videos. For now the above tutorial is to get you started with Python Power Electronics so that you can use it for your own projects. The video lecture references two short papers that are available on my website:
http://www.pythonpowerelectronics.com/contents/papers/timing_scheduler.pdf
http://www.pythonpowerelectronics.com/contents/papers/control_structure.pdf

And of course for details on the simulator and how control functions are processed or the user interface is handled, check out my book:
https://www.springer.com/gp/book/9783319739830

The book contains entire chapters on user interface, control functions and an entire example on how to simulate a reactive power compensator for a three-phase distribution system.

Making videos is fun and I will continue making them. I feel that videos engage people much more than documents or even slide shows. Stay tuned for more videos on the YouTube channel:
https://www.youtube.com/channel/UCxVbKNK18A_a9Ohd0Kb7kNA

For continuous news and updates, follow my Facebook page:
https://www.facebook.com/pythonpowerelectronics/

Wednesday, May 2, 2018

Releasing the command line version compatible with Python 3

So finally I started with migration to Python 3. Find the download link:
http://pythonpowerelectronics.com/contents/softwaredownloads.html

It wasn't too tough a migration. Mainly the print statement have become functions. So a lot of editing of these statements. Another change which I can't quite understand the necessity of was that raw_input function to get inputs from the user has been removed and an input function is available instead. A more subtle change and this is something which can cause errors to crop up much later is the nature of division.

In Python 2, dividing an integer by an integer yields an integer. So 5/2 will produce 2. In most languages it is the same. To produce exact division, you would have to write 5/2.0 or 5/(2*1.0) etc. Basically one of the operands must be a float. But in Python 3, 5/2 will produce 2.5. To produce an integer result, this would have to be an integer division 5//2.

I many of my loops I use integer division to iterate up to the mid-point of an array. I changed in a couple of places the regular division to integer division or else the for-loop throws an error because the index cannot be a float. But errors are expected later as there a number of such for loops which act on special conditions and it would take a vast number of different circuits before all these loops are discovered and changed.

The next step is to migrate the Django based web app to Python 3. Until then feel free to download and check out the CLI above with Python 3 with your circuits. And please do report errors so that I can fix bugs.