使用PyFMI对反馈回路连接的两个协同仿真类型FMU进行联合仿真的技术咨询:设置原理、连接定义及结果全零问题排查
Hey there, let's work through your PyFMI co-simulation questions and fix that frustrating zero-result issue together!
1. How to Co-Simulate Two Feedback-Linked Co-Simulation FMUs with PyFMI
Getting a feedback loop up and running is straightforward once you nail the connections—here’s the playbook:
- Load your FMUs: Use
pyfmi.load_fmu()to import each co-simulation FMU as a separate slave model. - Map your feedback connections: Create a list of tuples where each tuple follows
(source_model, source_variable, target_model, target_variable). For your feedback loop, you’ll need two connections: one sending the first FMU’s output to the second’s input, and another routing the second’s output back to the first’s input (or the input that feeds its internal logic). - Spin up the coordinator: Pass your list of models and connections to
pyfmi.master.Master()—this class handles all the behind-the-scenes syncing between FMUs. - Run the simulation: Call
simulate()on the Master instance, making sure external inputs don’t clash with your feedback signals (more on that later!).
2. How PyFMI’s Co-Simulation Setup Works
Think of the Master class as the traffic controller for your co-simulation. It follows the FMI Co-Simulation standard to keep everything running smoothly:
- Each FMU acts as an independent "slave" that runs its own internal simulation.
- For every time step, the Master:
- Grabs the latest output values from each slave.
- Sends those outputs to the target slaves you specified in your connections.
- Syncs the time across all slaves, so everyone advances to the next step with the correct input data.
- You don’t have to manually pass data between FMUs—your connection list tells the Master exactly where to send each signal.
3. Defining Connections When Feeding Input to y_ref (With SUM Block Logic)
From your note, the input to u is processed by a SUM Block (I’m guessing it’s something like u = y_ref - y_plant, where y_plant is the output of your plant FMU). Here’s how to model this correctly:
- Check your FMU interfaces first: Make sure your PI controller FMU (
Simple_System_PI.fmu) has an input fory_ref(the external setpoint) and an input for the feedback signal (let’s call ity_feedback). The SUM logic should live inside the PI FMU, so it calculatesuinternally asy_ref - y_feedback. - Set up your connections:
- Link
sub_system2.y(plant output) tosub_system1.y_feedback(PI controller’s feedback input). - Feed your external setpoint signal to
sub_system1.y_ref, not directly tou—sinceuis generated by the internal SUM block.
- Link
- If the SUM block has to be external (not part of either FMU), you can use a custom input function to compute
uon the fly. This function will grab the current plant output, combine it with the setpoint, and pass the result to the PI controller’suinput each time step.
Fixing Your "All Zero Results" Issue
Looking at your code, two critical conflicts are causing those blank zero outputs:
- Duplicate assignments to
sub_system1.u:- You’re feeding an external cosine signal to
sub_system1.uviainput_object. - But your connections also route
sub_system2.y(plant output) tosub_system1.u. This meansuis getting two conflicting values at once—PyFMI doesn’t know which one to use, leading to undefined behavior (hence all zeros).
- You’re feeding an external cosine signal to
- Unnecessary initial value override: The
sub_system1.set('u', u[0])call is overriding the initial value ofubefore simulation starts, adding another layer of confusion.
Corrected Code Example
Let’s adjust the code assuming your PI controller has a y_ref input and internal SUM logic:
import pyfmi from pyfmi import load_fmu from pyfmi.master import Master import numpy as np import pylab as P # Load your FMUs sub_system1 = load_fmu("Simple_System_PI.fmu") # PI controller with y_ref and y_feedback inputs sub_system2 = load_fmu("Simple_System_Plant.fmu") # Plant with u input and y output # Define the feedback loop connections connections = [ (sub_system1, "y", sub_system2, "u"), # PI output -> Plant input (sub_system2, "y", sub_system1, "y_feedback") # Plant output -> PI feedback input ] # Generate your external setpoint (y_ref) t = np.linspace(0, 10, 100) y_ref = np.cos(t) u_traj = np.transpose(np.vstack((t, y_ref))) # Feed the setpoint to the PI's y_ref input input_object = ((sub_system1, 'y_ref'), u_traj) # Initialize the Master simulator master_simulator = Master(models=[sub_system1, sub_system2], connections=connections) # Run the simulation res = master_simulator.simulate(start_time=0.0, final_time=10.0, input=input_object) # Plot results to verify P.plot(res['time'], res[sub_system1].get('y'), label='PI Output') P.plot(res['time'], res[sub_system2].get('y'), label='Plant Output') P.plot(res['time'], y_ref, label='Setpoint (y_ref)') P.legend() P.show()
Key Fixes:
- Removed the conflicting connection to
sub_system1.u—now the PI controller calculatesuinternally fromy_refandy_feedback. - Updated connections to route the plant’s output to the PI’s feedback input instead of
u. - Got rid of the redundant
sub_system1.set('u', u[0])call.
If your PI controller doesn’t have a y_feedback input and the SUM block must be external, use a custom input function like this:
# Precompute your setpoint array t = np.linspace(0, 10, 100) y_ref = np.cos(t) def custom_input(current_time): # Get the current plant output plant_y = sub_system2.get('y') # Interpolate the setpoint to the current time current_y_ref = np.interp(current_time, t, y_ref) # Calculate u using the SUM logic u_val = current_y_ref - plant_y # Return the value to send to the PI's u input return [(sub_system1, 'u', u_val)] # Run simulation with the custom input res = master_simulator.simulate(start_time=0.0, final_time=10.0, input=custom_input)
内容的提问来源于stack exchange,提问作者Geeko




