You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用PyFMI对反馈回路连接的两个协同仿真类型FMU进行联合仿真的技术咨询:设置原理、连接定义及结果全零问题排查

Hey there, let's work through your PyFMI co-simulation questions and fix that frustrating zero-result issue together!

PyFMI Co-Simulation: Answers & Troubleshooting

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:
    1. Grabs the latest output values from each slave.
    2. Sends those outputs to the target slaves you specified in your connections.
    3. 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 for y_ref (the external setpoint) and an input for the feedback signal (let’s call it y_feedback). The SUM logic should live inside the PI FMU, so it calculates u internally as y_ref - y_feedback.
  • Set up your connections:
    • Link sub_system2.y (plant output) to sub_system1.y_feedback (PI controller’s feedback input).
    • Feed your external setpoint signal to sub_system1.y_ref, not directly to u—since u is generated by the internal SUM block.
  • If the SUM block has to be external (not part of either FMU), you can use a custom input function to compute u on the fly. This function will grab the current plant output, combine it with the setpoint, and pass the result to the PI controller’s u input each time step.

Fixing Your "All Zero Results" Issue

Looking at your code, two critical conflicts are causing those blank zero outputs:

  1. Duplicate assignments to sub_system1.u:
    • You’re feeding an external cosine signal to sub_system1.u via input_object.
    • But your connections also route sub_system2.y (plant output) to sub_system1.u. This means u is getting two conflicting values at once—PyFMI doesn’t know which one to use, leading to undefined behavior (hence all zeros).
  2. Unnecessary initial value override: The sub_system1.set('u', u[0]) call is overriding the initial value of u before 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 calculates u internally from y_ref and y_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

火山引擎 最新活动