Finite-Control-Set Model Predictive Control (FCS-MPC) ****************************************************** We apply a finite-control set (FCS) model predictive control (MPC) approach to realize current control of a permanent magnet synchronous motor (PMSM) and Synchronous Reluctance Motor (SRM) in rotor field-oriented coordinates. Unlike continuous-control set (CCS) MPC, FCS-MPC directly evaluates a finite set of switching states to optimize the control input based on a cost function over a prediction horizon. .. figure:: ../../../plots/mpc_structure.png .. figure:: ../../../plots/mpc_scheme.png With the help of the system model, the output variables are predicted for each possible switching state in the finite control set. The optimizer evaluates a cost function (typically the quadratic control error) for all possible switching combinations over the prediction horizon. The switching state that minimizes the cost function is selected and applied to the system in the next time step. Unlike CCS-MPC, FCS-MPC does not require an iterative numerical solver or barrier functions to handle constraints, as the voltage limits are inherently respected by evaluating only the physically realizable switching states of the converter. The computational efficiency of FCS-MPC comes from the direct enumeration and evaluation of the finite number of possible switching states, rather than solving an optimization problem with constraints. MPC Current Controller ====================== .. autoclass:: gem_controllers.mpc_current_controller.MPCCurrentController :members: :undoc-members: :inherited-members: :show-inheritance: :member-order: groupwise Example Usage ============= The following example demonstrates how to apply the :class:`MPCCurrentController` to control a permanent magnet synchronous motor (PMSM) using a finite-control-set MPC approach within the ``gym-electric-motor`` simulation environment. .. code-block:: python import numpy as np import matplotlib matplotlib.use('Qt5Agg') from gem_controllers import GemController import gym_electric_motor as gem from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType from gym_electric_motor.physical_systems import ConstantSpeedLoad from gym_electric_motor.reference_generators import ( MultipleReferenceGenerator, SwitchedReferenceGenerator, TriangularReferenceGenerator, SinusoidalReferenceGenerator, StepReferenceGenerator ) from gym_electric_motor.visualization.motor_dashboard import MotorDashboard motor_parameter = dict(r_s=15e-3, l_d=0.37e-3, l_q=1.2e-3, psi_p=65.6e-3, p=3, j_rotor=0.06) limit_values = dict(i=160 * 1.41, omega=12000 * np.pi / 30, u=450) nominal_values = {key: 0.7 * limit for key, limit in limit_values.items()} q_generator = SwitchedReferenceGenerator( sub_generators=[ SinusoidalReferenceGenerator(reference_state='i_sq', amplitude_range=(0, 0.3), offset_range=(0, 0.2)), StepReferenceGenerator(reference_state='i_sq', amplitude_range=(0, 0.5)), TriangularReferenceGenerator(reference_state='i_sq', amplitude_range=(0, 0.3), offset_range=(0, 0.2)) ], super_episode_length=(500, 1000) ) d_generator = SwitchedReferenceGenerator( sub_generators=[ SinusoidalReferenceGenerator(reference_state='i_sd', amplitude_range=(0, 0.3), offset_range=(0, 0.2)), StepReferenceGenerator(reference_state='i_sd', amplitude_range=(0, 0.5)), TriangularReferenceGenerator(reference_state='i_sd', amplitude_range=(0, 0.3), offset_range=(0, 0.2)) ], super_episode_length=(500, 1000) ) reference_generator = MultipleReferenceGenerator([d_generator, q_generator]) visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10) motor = Motor( MotorType.PermanentMagnetSynchronousMotor, ControlType.CurrentControl, ActionType.Finite ) env = gem.make( motor.env_id(), visualization=visu, load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), reference_generator=reference_generator, reward_function=dict( reward_weights={'i_sq': 1, 'i_sd': 1}, reward_power=0.5, ), supply=dict(u_nominal=400), motor=dict( motor_parameter=motor_parameter, limit_values=limit_values, nominal_values=nominal_values ), ) visu.initialize() controller = GemController.make( env=env, env_id=motor.env_id(), base_current_controller="MPC", block_diagram=False, prediction_horizon=1, w_d=0.5, w_q=2.0 ) (state, reference), _ = env.reset() cum_rew = 0 for i in range(10000): env.render() action = controller.control(state, reference) (state, reference), reward, terminated, truncated, _ = env.step(action) cum_rew += reward if terminated: (state, reference), _ = env.reset() controller.reset() print('Reward =', cum_rew) env.close() Simulation Results ================== The following figures illustrate the performance of the FCS-MPC controller in current control of the PMSM under varying reference trajectories. The controller accurately tracks both the d- and q-axis current references while ensuring smooth control actions. .. figure:: ../../../plots/MPC_Time_Plots.png FCS-MPC current tracking of *id* and *iq*. The results show that the finite-control-set MPC effectively minimizes the current tracking error within each sampling period while satisfying the converter switching constraints. Compared to conventional PI controllers, the FCS-MPC achieves faster dynamic response and improved steady-state performance without overshoot.