upb-lea / gym-electric-motor

Gym Electric Motor (GEM): An OpenAI Gym Environment for Electric Motors
https://upb-lea.github.io/gym-electric-motor/
MIT License
295 stars 66 forks source link

Model DC-link as RC-element for time-varying behavior #37

Closed wallscheid closed 4 years ago

wallscheid commented 4 years ago

Currently, all inverters are voltage-source inverter with idealized DC-link behavior i.e. it is assumed that the DC-link voltage is fixed and given. However, in realistic applications (automotive/automation) the DC-link is a capacitor fed from an external source (battery, rectifier from grid,...) and, therefore, the DC-link voltage is varying and time-dependent.

In order to make the GEM envs. a little bit more realistisch, the DC-link should be modeled as a RC-element (capacitor with internal ohmic resistance) which is fed from an ideal DC-voltage source. This results in an additional ODE which has to be transfered within GEM as part of the overall ODE system (including mechanical and electric motor ODEs).

Moreoever, this feature should be activatable and deactivatable via a corresponding parameterization

wkirgsn commented 4 years ago

see also #8

XyDrKRulof commented 4 years ago

So I've got some uncertainties regarding this issue:

1. I've got very little knowledge about the wiring of electrical systems. For now I just assumed and implemented an RC circuit in which the ODE consist only of the capacitor loading itself completely. I guess we have to regard currents going into the motor which are powered by the capacitor. In that case I am not exactly sure how to do so (and thus derive an appropriate ODE).

2. On the implementation side there are two possibilities to implement the supplier having to solve an ODE for the output: a. have the supplier solve the ODE himself - this would be the easiest way of implementing it and would change nothing of the bigger structure - but there might be accuracy problems if the ODE depends on other ODEs? b. the way I understand how you want it is to have the physical system solve it, just like the electrical and mechanical ODEs. For this, as far as I understand, we would've to change how the suppliers work currently. As of now the physical system calls get_voltage(i_sup) and receives it's supply voltage. If the physical system calculates the supply voltage this function should be removed and replaced by an ODE for each supplier (e.g. for the constant supply the ODE would return zero for every input since it's voltage is not changing over time). Additionally a lot of tweeking would be needed in the physical_systems.py as the two current ODEs are very specifically coded and called in there, e..g. through array indexing. Also every simulate() function would have to be updated accordingly. So I would like to get clear if this is indeed the intended way to solve this issue.

wallscheid commented 4 years ago

Regarding (1): The idea is that the DC-link is modeled as an RC-circuit loaded from an ideal DC voltage source. In the current implementation, only the ideal voltage source is assumed and that should be extended by the RC-circuit:

g7496

The ideal voltage source could be assumed as a power supply feeding feeding from the grid into the DC link capacitor. From the ODE viewpoint, the voltage U_dc(t) at the capacitor is the state variable.

Regarding (2): The DC-link voltage (state variable) of course becomes depending on the motor load current. Hence, it cannot be modeled in a stand alone way but has to be coupled with the other ODEs to be fused to one ODE system which is solved together. Regarding technical implementation in physical_systems.py @atra94 may can support / provide his opinion.

atra94 commented 4 years ago

Of course, we cannot model the Voltage Supply completely independent. But when we assume the supply current to be constant over the short period of one cycle, we could solve the ODE independent from the motor and load-ODEs.

If we put the Supply-ODE into the system_equation of the SCMLModel, we would also have to include the converter there. This increases the complexity a lot. Especially, the calculation of the jacobian of the whole system might become very hard with the Converter as a switched system in between.

Honestly, I cannot estimate how big the error might be, if we implement the variant 2.a compared to 2.b. But it is a tradeoff between code-complexity and simulation accuracy.

wallscheid commented 4 years ago

As the variable DC-link voltage model should be only optional (choice by user), adding computational complexity for the simulation shoudl not play a major role. If the user wishes to simulate fast on the basis of a simpler model he can do that. The idea of the variable DC link is to add model accuaracy/realism to GEM and of course that comes not for free from the computational simulation demand of view.

atra94 commented 4 years ago

I did not mean computational complexity but the additional complexity in the code because the system_equation and system_jacobian functions would need to be rewritten. Rewriting the system_equation is still quite forward, but the jacobian might be really complex especially due to the converters, which would also have to be included.

I was wondering, if this (possibly little) additional accuracy is really worth the higher complexity in the code (which will also make the code harder to maintain, test, extend etc.)

wallscheid commented 4 years ago

Maybe lets separate the discussion in two parts: discrete and continuous actions.

Discrete actions I agree that in this case things really become a little bit awkward, since we generate switchable ODEs. Depending on the switching state of the power electronic power supply unit, the motor phases are connected to the upper or lower potential of the capacitor. For example, when we operate a three-phase motor with a two-level inverter we would receive 8 switching states leading to 8 ODE systems of which always one is active. If the inverter changes its switching vector, the according ODE has to be activated and initialized with the state values from the previously active ODE.

Cont. actions As we do not work with distinct pulse pattern schemes in the cont. case but with simple averaged models, we could indeed split up the ODE systems into two parts which are calculated separately. The physical_system part as it is right now plus one single ODE line for the capacitor voltage. Based on the current electrical motor power flow we calculate the equivalent load current on the DC-link side which is required to solve the RC ODE to receive the voltage information as the input for the physical_system ODEs. To split up a direct coupling, we would have to assume that one variable (DC-link voltage or load current) is constant within one simulation step.

Starting with the cont. case for me seems easier and more straightforward. Hence, I would be in favor to focus on this first.

XyDrKRulof commented 4 years ago

I do currently have one failing test left which is the following line (from test_physical_systems.py): assert scml_system.converter.last_i_out == scml_system.electrical_motor.i_in(ode_state[2:])

I changed some small parts of the order of supply current and voltage calculation in physical_systems.py which is probably the reason for this assertion failure. Since I am not exactly sure what this assertion wants to test and if it's "okay" that it fails (because of a changed order) I just commented it out of the test file. This might need some further investigation, though.

For this problem see also issue #43

max-schenke commented 4 years ago

As it seems this test is meant to assure that the current that comes out of the converter is the same as the current that goes into the motor. This seems quite important, can you have a look whether the difference in between those currents is large or if it may be just numerical inconsistency?

XyDrKRulof commented 4 years ago

The values are 11.0002 and 11. I don't want to judge whether this is just an incosistency - what is your opinion on that?

max-schenke commented 4 years ago

@atra94 what would you say? The error can't really be of numeric fashion, can it?

If it is about numerics, we could use e.g.: assert sum(abs(motor_1.electrical_ode(state, [u_in], omega) - result)) < 1E-10

An assertion like this tests with finite accuracy. But I am not sure yet, I think scml_system.converter.last_i_out should be exactly the same as scml_system.electrical_motor.i_in(ode_state[2:]) since there are no calculations in between. (Or are there?)

wallscheid commented 4 years ago

Or is that a consequence of the decoupling of the ODEs within the DC-link and the motor?

XyDrKRulof commented 4 years ago

We are talking in the assertion about a constant voltage supply - so no ODE is getting solved for the supply.

The commit that changed physical_systems.py is 867ac27b02215c3f5daecbd35faf602b29c76da7 (" added support for the discrete switching case "). There I changed for each simulation():

        i_sup = self._converter.i_sup(i_in)
        u_sup = self._supply.get_voltage(self._t, i_sup)

        for t in switching_times[:-1]:
            i_sup = self._converter.i_sup(i_in)
            u_sup = self._supply.get_voltage(self._t, i_sup)
            u_in = self._converter.convert(i_in, self._ode_solver.t)
            u_in = [u * u_sup for u in u_in]
            self._ode_solver.set_f_params(u_in)
            ode_state = self._ode_solver.integrate(t)
            i_in = self._electrical_motor.i_in(ode_state[self._ode_currents_idx])

to

        for t in switching_times[:-1]:
            i_sup = self._converter.i_sup(i_in)
            u_sup = self._supply.get_voltage(self._t, i_sup)
            u_in = self._converter.convert(i_in, self._ode_solver.t)
            u_in = [u * u_sup for u in u_in]
            self._ode_solver.set_f_params(u_in)
            ode_state = self._ode_solver.integrate(t)
            i_in = self._electrical_motor.i_in(ode_state[self._ode_currents_idx])
         i_sup = self._converter.i_sup(i_in)
         u_sup = self._supply.get_voltage(self._t, i_sup)

In my understanding for the continuous case the code execution is the same with these changes since the loop shouldn't be entered . That means the assertion should fail for the discrete case. The reason for that might be because i_sup and u_sup get calculated after the loop which wasn't the case before.

Could that be the reason for the assertion failure?

XyDrKRulof commented 4 years ago

Had a call with @atra94 and we managed to fix that test.

@RohithCharanD Did you have the chance to look at my code already?

RohithCharanD commented 4 years ago

This can be closed. Internal review is done.

XyDrKRulof commented 4 years ago

The corresponding pull request was already merged into nightly. Will close this issue now.