Skip to content

non-intuitive Time-Domain Simulation functions behavior #239

Description

@hungpham2511

Hi, I notice that the simulation functions: step_response, impulse_response, for discrete time systems is not very intuitive, and is also inconsistent with matlab's behavior (I sort of have the habit of checking matlab documentation for a python-control function if there are suprises).

Consider this snippet

import control as co
import numpy as np
A1 = co.tf([1], [1, 6, 5])
A1 = co.c2d(A1, 0.5)
T, y = co.step_response(A1, np.arange(0, 5, 0.2))

we would expect T to be the time instances that correspond to values in y (matlab behavior), but it turns out not the case. I got T.shape = (25,) while y.shape=(1, 10). The same behavior can be observed for other time-domain functions for discrete-time systems (continuous-time systems are alright).

Activity

  1. ilayn commented on Oct 25, 2018

    @ilayn

    It is just giving you back the time array but since the sampling time of the system doesn't match the time increment it is resampling the bounds.

  2. hungpham2511 commented on Nov 22, 2018

    @hungpham2511
    ContributorAuthor

    That's right.

    However, this behavior is not intuitive to users. I would prefer it to be the actual time-array that is used to compute the impulse response. This information can be used, say, for plotting. Also, with the current behavior, the returned time-array T does not convey any information.

  3. murrayrm commented on Dec 28, 2018

    @murrayrm
    Member

    I agree that this behavior is non-intuitive and probably incorrect. Marking as a bug to be fixed in the next release.

  4. added this to the 0.8.2 milestone on Dec 28, 2018
  5. murrayrm commented on Jan 5, 2019

    @murrayrm
    Member

    I did a bit of looking into this. Some observations and issues:

    • The 'bug' in this code is something that is apparently inherited from the scipy.signal.dlsim(), which is what step_response() eventually calls (through a call to forced_response()). In particular, it appears that if you pass dlsim() a time vector that doesn't match the sampling time, it doesn't generate an error and, furthermore, it returns whatever you passed it (same behavior as what was noticed here).

    • There is a further inconsistency in the way that T=None is handled in the control simulation functions for discrete time systems: the time vector that is generated will have dt=1 independent of what the actually sampling time for the system is.

    • There may also be some issues with the way that the default response time is determined when you pass T=None for discrete time systems. For step_response() and initial_response() this is supposed to be based on the dynamics matrix A. This uses the _default_response_times() function from scipy.signal.ltisys, but I believe that function assumes continuous time (need to check).

    I think that the correct behavior for discrete time systems is the following (which would match MATLAB):

    • The values of the output argument T should always match the times at which the corresponding outputs Y are obtained. This is consistent with the idea that you plot the outputs of a simulation function by using commands of the form
    t, y = step_response(sys)
    plot(t, y)
    
    • If you pass an explicit set of sampling times for a discrete time system, the time increment should match the sampling time (with dt=True being interpreted as a sampling time of 1). In this case, the output argument T will match the input argument T.

    • If you omit the sampling time, the value of T will be determined based on either the input signal (for forced_response() or the time constants coming from the system dynamics. In either case, the difference between time points should be equal to the sampling time of the system (or 1 if dt=True).

    • For forced_response(), the length of the input vector U should match the length of the time vector T.

    The good news is that a relatively modest set of changes are required to fix all of these things up. Comments welcome on whether I am missing anything, but I'll start putting together test cases for the properties above and then updating the code.

  6. self-assigned this
    on Jan 5, 2019
  7. murrayrm commented on Apr 11, 2019

    @murrayrm
    Member

    Fixed in PR #286

  8. tomaszG247 commented on Apr 17, 2019

    @tomaszG247

    Hello,
    @murrayrm :After stumbling upon this issue in 0.8.1, I installed 0.8.2, which I believe only partly solves the issue:
    When running

    T_heat = 1  
    K_heat = 5
    num_heat = [K_heat]
    den_heat = [T_heat, 1]
    LTI_FO_model = control.tf(num_heat, den_heat)
    LTI_FO_modeld = LTI_FO_model.sample(Ts=0.25)
    (T, yout) = control.step_response(LTI_FO_model)
    (Td, youtd) = control.step_response(LTI_FO_modeld)
    plt.plot(T, yout)
    plt.plot(Td,youtd)
    plt.show()
    

    I am still having the problem that youtd is 2D while T is 1D. The error reads
    x and y must have same first dimension, but have shapes (9,) and (1, 9)

  9. murrayrm commented on Apr 20, 2019

    @murrayrm
    Member

    Will look into this and see if I can generate a unit test that exhibits the error, so that we can nail this one down.

  10. murrayrm commented on Apr 21, 2019

    @murrayrm
    Member

    This behavior has been fixed in PR #295. Can close as soon as that PR is merged.

  11. modified the milestones: 0.8.2, 0.8.3 on Apr 21, 2019
  12. murrayrm commented on May 2, 2019

    @murrayrm
    Member

    PR #295 is merged. Closing this (again).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions