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")