File size: 5,041 Bytes
774e2a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import linalg
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import time

def show_pid_control():
    """Interface pour le contrôle PID"""
    
    st.header("🎯 Contrôle PID")
    
    # Explication théorique
    with st.expander("📖 Théorie du contrôle PID", expanded=True):
        st.markdown(r"""
        ### Contrôleur PID
        
        La loi de commande PID est :
        
        $$
        u(t) = K_p e(t) + K_i \int_0^t e(\tau) d\tau + K_d \frac{de(t)}{dt}
        $$
        
        où :
        - $e(t)$ : erreur de suivi
        - $K_p$ : gain proportionnel
        - $K_i$ : gain intégral
        - $K_d$ : gain dérivé
        
        ### Effets des gains
        
        - **Kp** : Rapidité de réponse, mais oscillations si trop élevé
        - **Ki** : Élimination de l'erreur statique
        - **Kd** : Amortissement des oscillations
        """)
    
    # Interface pour le contrôle PID
    st.subheader("🧪 Simulateur de contrôle PID")
    
    col1, col2 = st.columns(2)
    
    with col1:
        st.markdown("### Paramètres du système")
        
        system_type = st.selectbox("Type de système",
                                  ["Masse-ressort", "Double intégrateur", "Robot souple"])
        
        if system_type == "Masse-ressort":
            st.info("Système masse-ressort : m=1kg, k=10N/m, c=0.1Ns/m")
        
        elif system_type == "Double intégrateur":
            st.info("Double intégrateur : position et vitesse")
        
        elif system_type == "Robot souple":
            st.info("Robot souple : poutre en flexion")
    
    with col2:
        st.markdown("### Gains PID")
        
        Kp = st.slider("Kp (Proportionnel)", 0.1, 100.0, 5.0, 0.1)
        Ki = st.slider("Ki (Intégral)", 0.0, 50.0, 1.0, 0.1)
        Kd = st.slider("Kd (Dérivé)", 0.0, 10.0, 0.1, 0.01)
        
        reference = st.slider("Consigne", -5.0, 5.0, 1.0, 0.1)
    
    if st.button("🎯 Lancer le contrôle PID", type="primary"):
        simulate_pid_control(system_type, Kp, Ki, Kd, reference)

def simulate_pid_control(system_type, Kp, Ki, Kd, reference):
    """Simule le contrôle PID"""
    
    dt = 0.01
    n_steps = 1000
    t = np.linspace(0, (n_steps-1)*dt, n_steps)
    
    # Système simulé
    if system_type == "Masse-ressort":
        A = np.array([[0, 1], [-10, -0.1]])
        B = np.array([[0], [1]])
        C = np.array([[1, 0]])
    elif system_type == "Double intégrateur":
        A = np.array([[0, 1], [0, 0]])
        B = np.array([[0], [1]])
        C = np.array([[1, 0]])
    else:
        # Robot souple simplifié
        A = np.array([[0, 1], [-1, -0.1]])
        B = np.array([[0], [1]])
        C = np.array([[1, 0]])
    
    # Simulation PID
    x = np.zeros((2, n_steps))
    u = np.zeros(n_steps)
    error = np.zeros(n_steps)
    integral = 0
    prev_error = 0
    
    for i in range(1, n_steps):
        # Erreur
        error[i] = (reference - C @ x[:, i-1])[0]
        
        # PID
        integral += error[i] * dt
        derivative = (error[i] - prev_error) / dt
        u[i] = Kp * error[i] + Ki * integral + Kd * derivative
        
        # Évolution du système
        x_dot = A @ x[:, i-1] + B.flatten() * u[i]
        x[:, i] = x[:, i-1] + x_dot * dt
        
        prev_error = error[i]
    
    # Visualisation
    fig = make_subplots(rows=2, cols=1,
                       subplot_titles=("Réponse du système", "Commande PID"),
                       vertical_spacing=0.15)
    
    # Réponse
    fig.add_trace(go.Scatter(x=t, y=x[0, :], name='Sortie',
                            line=dict(color='blue')),
                 row=1, col=1)
    fig.add_trace(go.Scatter(x=t, y=np.ones_like(t) * reference, name='Consigne',
                            line=dict(color='red', dash='dash')),
                 row=1, col=1)
    fig.add_trace(go.Scatter(x=t, y=error, name='Erreur',
                            line=dict(color='green')),
                 row=1, col=1)
    
    # Commande
    fig.add_trace(go.Scatter(x=t, y=u, name='Commande u(t)',
                            line=dict(color='purple')),
                 row=2, col=1)
    
    fig.update_layout(height=600, showlegend=True)
    st.plotly_chart(fig, use_container_width=True)
    
    # Métriques
    steady_state_error = np.abs(error[-100:]).mean()
    overshoot = (np.max(x[0, :]) - reference) / reference * 100 if reference != 0 else 0
    
    col1, col2, col3 = st.columns(3)
    
    with col1:
        st.metric("Erreur statique", f"{steady_state_error:.4f}")
    
    with col2:
        st.metric("Dépassement (%)", f"{overshoot:.1f}%")
    
    with col3:
        settling_time = np.where(np.abs(error) < 0.05 * np.abs(reference))[0]
        settling_time = settling_time[0] * dt if len(settling_time) > 0 else n_steps * dt
        st.metric("Temps de réponse", f"{settling_time:.2f} s")