Package: slxpy-fork
SlxPyToolchain for seamlessly generating efficient Simulink-to-Python binding and gymnasium-like environment wrapper.Forked fromslxpy, the Simulink-to-Python C++ bindings generator as Python package and MATLAB toolbox:Table of ContentsSlxPyTable of ContentsFlowchartFeaturesPrerequisities & Installation & Quick startMATLABPythonC++ toolchainInstallationQuick startModeling guideTunable parameterLimitations by Embedded CoderLimitations by SlxpyGym SupportModel requirementenv.tomlBasic settingConfigure gym-simulink mapping and gym spaceControl reset behavior to get initial observationDefine how parameters are initialized on each resetArchitectureFAQNumerous compiler errors about undefined identifier 'creal_T' with SimscapeImplementation noteAbout stack allocationAbout model class Copy/Move Constructible/AssignableChangelogFlowchartThe flowchart briefly describes common workflow for your smoother integration.graph TD;
    Learn[Learn fundamentals];
    Prerequisities[Prerequisities];
    Installation[Installation];
    Model[Prepare model];
    Create[Create project];
    Setup["(MATLAB) slxpy.setup_config(project_path)"];
    Codegen["(MATLAB) slxpy.codegen(project_path)"];
    Generate["(CLI) slxpy generate"];
    Build["(CLI) python setup.py build"];
    Test["Test & Done"];

    MTC(["On model.toml change"]);
    MC(["On model change"]);
    ETC(["On env.toml change"]);

    subgraph Preparation
    Prerequisities --> Installation;
    end
    Installation --> Create;
    subgraph Main workflow
        Create --> Setup --> Codegen --> Generate --> Build --> Test;
        Model --> Setup;
        MTC --Rerun--> Setup;
        MC --Rerun--> Codegen;
        ETC --Rerun--> Generate;
    endFeaturesAlmost complete Simulink and Embedded Coder features supportCompatible with a wide range of MATLAB versionsHelp tuning Simulink code generation configMininal dependencies and cross-platform, not depending on MATLAB after Simulink code generation stepExchange array data with numpy for efficiencyRaw and gymnasium (formerly gym) environment wrapper, with seeding and parameter randomization supportAutomatic single-node parallelization with vector environmentGenerate human-readable object__repr__for ease of useAutomatically generate stub file to assist development (with pybind11-stubgen)Compile with all modern C++ compilersPrerequisities & Installation & Quick startYou need to prepare Python, optionally MATLAB and a C++ compiler to begin with slxpy.MATLABOnly needed forStep 2(Simulink to C++)Version: >= R2018a ( >=R2021arecommended )R2021amay be the first version actually suitable for RL environment as it allowsinstance parameters. Previous versions of Embedded Coder will generates static parameters which might be difficult to use in a program (shared by all instances).For version >= R2018a, limited support is added.MATLAB prior to R2021a will inline parameters defined in model workspace when C++ interface is chosen. The script has some logic to allow you to code as R2021 workflow, and maintain tunability on prior to R2021a releases, but the script may fail on the first run and work for following runs due to unknown reasons. (In R2021a it's far easier)For version <= R2017b, some Simulink internal error prohibits proper code generation, thus unsupported.MATLAB sinceR2022asupportsreusable Simscape model, and slxpy provides corresponding support. Simscape enables powerful non-causal system modeling, which may be very useful for environment design.After Mathworks ticket 05353942 & 05373346, reusable C++ class in this release is an unintended bug, and only reusable C interface is officially supported. So, it may not yet work as expected. (Bug/Enhancement Submitted)Toolbox: Simulink, Embedded Coder, MATLAB Coder, Simulink CoderPythonAlmost always needed, except forStep 2Version: >= 3.8 (but generated binding can target Python 3.7)Slxpy uses a bunch of features added in Python 3.8C++ toolchainNeeded forStep 4(Building C++)ForStep 2, Embedded Coder does not depend on a C++ toolchain to generate code, but may display a warning for failing to generate build files, which is OK.For some MATLAB versions, however, if you are facing error like this:The model is configured for C++ code generation, but the C-only compiler, LCC, is the default compiler. To allow code generation, on the Code Generation pane:Select the 'Generate code only' check box.In the Toolchain field, select a toolchain from the drop-down list.it may be a logic error in Embedded Coder. Just select an alternative C++ toolchain other than LCC, even if it does not exist in your system.C++ 17 compatible compiler (one of)for Windows, Visual Studio 2019 or newerClang 5 or newerGCC 7 or newerInstallationYou need to install two packages, one Python package for Python main logic, one MATLAB toolbox for MATLAB interop.Install Python package withpip install slxpyIt is recommanded to use slxpy with conda (to enable multi-target build) and install slxpy in an dedicated conda environment, i.e.conda create -n slxpy python=3.9
conda activate slxpy
pip install slxpyInstall MATLAB toolboxDownloading toolbox fromFile Exchange linkand double-click it in MATLAB to install.Quick startPrepare a Simulink modelfoo.slxsuitable for code generation (Seedocs/modeling.md)Project creationmkdirbar# Create slxpy project folder, choose any name you likecdbar
slxpyinit# Interactively fill up basic informationThen adjustmodel.tomlandenv.tomlas needed (See comments in file, and alsodocs/env.md).Simulink code generationworkdir='bar';% Path to slxpy project folderslxpy.setup_config(workdir)% Only need to be run for the first timeslxpy.codegen(workdir)% Code generationSlxpy asset generation# Assuming still in bar folderslxpygenerateBuild extension# Assuming still in bar folderpythonsetup.pybuildTest extension# Assuming still in bar foldercdbuild/lib<platform-suffix>
pythonIn Python REPL, run# Substitute foo & bar to your corresponding model & project nameimportbara=bar.fooModelClass()b=bar.RawEnv()c=bar.RawEnvVec()d=bar.GymEnv()e=bar.GymEnvVec()# Could also provide an EnvSpec similar to Gym's EnvSpec# Check stub or call help(bar._env.EnvSpec) for more options.spec=bar._env.EnvSpec(id='bar-v0',max_episode_steps=100,strict_reset=True,)env=bar.GymEnv(spec)Modeling guideSlxpy follows standard simulink code generation process. So, if your model follows standard, minimal adjustments are required for proper code generation. So, detailed discussion about Simulink modeling is out of scope for this README, you shall refer to Simulink documentation for instructions.To support gym environment generation, see Gym Support section.Tunable parameterTo make environment tunable, thus could have randomness, physical parameters, random seed, initial state of intergrator, etc shall be created with following two steps.Set them inModel workspaceasSimulink Parameter. If it's aMATLAB variablerather than aSimulink Parameter, right-click entries, selectConvert to parameter objectto convert.Tick theArgumentcheckbox.Limitations by Embedded CoderS Function: You have to provide a.tlcfile for S Function code generation, but.tlcis a difficult topic. So, I recommend usingMATLAB Functionblock when possible.Fixed-step Solver: Variable-step solver do not support code generation in Embedded Coder. (Some models may get wrong simulation result in Fixed-step Solver if numeric condition is bad. Make sure to validate before code generation for proper results.)Algebraic Loop: Simulink could partially handle algebraic loop, but code generation does not. Try avoiding it using aUnit DelayorMemoryblock, or solve it iteratively in aMATLAB Functionblock.Variable-sized input: Embedded Coder C++ interface do not support it.Other blocks not supported by code generation, refer to Simulink documentationLimitations by SlxpyVariable-sized output: difficult to handle properly, currently not consideredFixed-point data: difficult to handle properly, currently not consideredBitfield: difficult to handle properly, currently not consideredEvent/function-call based system: difficult to handle properly, currently not consideredString: string-related blocks are not supported. Stringstd::stringlead to non-POD struct in C++, breaking a fundemental assumption for SlxpyLuckily, entries mentioned above might rarely be used in modeling.Gym SupportIf modeling properly, Slxpy could generate gym environment with minimal configuration.Model requirementOne inport of data typedouble(default) asaction. Recommend to have exactly one inport, as additional inports will get zero input (meaningless).One output of data typedouble(default) asobservation. Recommend to be the first outport.One scalar output of data typedouble(default) asreward. Recommend to be the second outport.One scalar output of data typelogicalasdone. Recommend to be the third outport.Any additional outports of data typedouble(default) to be included ininfodict.env.tomlConfiguration fileenv.tomlcan be used to control various aspects of environment wrapping, including action_space, observation_space, initial observation and parameter initialization.Basic setting## Config version. DO NOT CHANGE.__version__="1.0.0"## Generate raw environment wrapper.use_raw=true## Generate gym-flavor environment wrapper (tensor action, tensor observation).## NOTE: gym-flavor environment has to meet certain criteria. See "gym" section below.use_gym=true## Environment initialization needs randomness (generally true).use_rng=true## Generate vectorized wrapper over raw/gym environment.use_vec=trueConfigure gym-simulink mapping and gym space## Configure gym-simulink mapping.[gym]## Action key in model inport(s).## Data MUST be a double scalar or array.## By default, the 1st inport is taken (Generally only one inport is sensible).## Uncomment the line below to provide an alternative key.# action_key = "act"## Observation key in model outports.## Data MUST be a double scalar or array.## By default, the 1st outport is taken.## Uncomment the line below to provide an alternative key.# observation_key = "obs"## Reward key in model outports.## Data MUST be a double scalar.## By default, the 2nd outport is taken.## Uncomment the line below to provide an alternative key.# reward_key = "rew"## Done key in model outports.## Data MUST be a boolean (or logical in MATLAB) scalar.## By default, the 3rd outport is taken.## Uncomment the line below to provide an alternative key.# done_key = "done"## Put additional outports to info dict.## Option: true -> all additional outports are included##         false -> empty info dict##         list of keys -> selected outports are included, e.g. ["foo", "bar"]info=true## Reward range, e.g. ["-inf", "inf"] | ["-inf", 0] | [-10, 10]reward_range=["-inf","inf"]## Action space, similar to gym.space## "type" includes: Box, Discrete, MultiDiscrete, MultiBinary[gym.action_space]type="Discrete"n=2## Observation space, see action_space above[gym.observation_space]type="Box"low=0.0high=1.0shape=[2,2]dtype="float64"Control reset behavior to get initial observation# Options controlling reset behavior[reset]## Take one step after environment initialization to get initial observation.## If set to true/false, optionally provide a initializer for initial action/observation.first_step=true## Only valid when "first_step = true".## By default, initial action is initialized with "default initialization".## Uncomment the line below to provide an "aggregate initialization" list.action="{ 1.0 }"## Only valid when "first_step = false".## By default, initial observation is initialized with "default initialization"## and might be affected by const block output optimization.## Uncomment the line below to provide an "aggregate initialization" list.# observation = "{ 1.0 }"Define how parameters are initialized on each reset## A table to define individual parameter initialization policy[parameter][parameter.seed_1]type="seed"[parameter.seed_2]type="seed"[parameter.constant_1]type="constant"value=1.0[parameter.constant_2]type="constant"value="{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }"[parameter.uniform_1]type="uniform"low=0.0high=1.0[parameter.uniform_2]type="uniform"low=0.0high=1.0[parameter.uniform_3]type="uniform"low=[0.0,0.2,0.4,0.6,0.8,1.0]high=1.0[parameter.uniform_4]type="uniform"low=0.0high=[0.0,0.2,0.4,0.6,0.8,1.0][parameter.uniform_5]type="uniform"low=[0.0,0.2,0.4,0.6,0.8,1.0]high=[0.0,0.2,0.4,0.6,0.8,1.0][parameter.custom]type="custom"code="std::fill_n(params.custom, 6, -1);"ArchitectureFrontend: Convert source to IRBackend: Generate Pybind11 binding with IR and modern C++ features, using Jinja2 for template generationFAQNumerous compiler errors about undefined identifier 'creal_T' with SimscapeTry to set simulink featurecomplextotrueinmodel.toml.
Though Embedded Coder did not complain, some Simscape (multibody) functions may implicitly depend on complex structsc*_T.
This may be a flaw of Mathworks product design.Implementation noteAbout stack allocationSimulink structs could have arbitrary size, ranging from only one scalar field to a large array storing an image,
previous implementation (88b504f and previous) has unintended stack allocations,
which causes stack overflow and immediate abort for large structs.After identifying the problem, stack allocations are changed to heap allocation and placement new.About model class Copy/Move Constructible/AssignableSimulink generated C++ class do not forbid these four default constructors, but pointers in RTModel may point to
invalid locations ifinitializeis not called again.
So, avoid calling these four default constructors explicitly or implicitly.Changelogv1.6.0.post1New versioning scheme, forks from slxpy are now labeled as same version with a.postNsuffix.Baseline for fork. (No change from original Python implementation slxpy==v1.6.0)v1.6.1.post1Updated fork to slxpy==v1.6.1v1.6.1.post2Based on v1.6.1.post1 with some improvements (the old fork version labeled slxpy==v1.6.2 is similar to slxpy-fork==v1.6.0.post1).Added model name and version via Jinja2 template ofsetup.pyfor better package management of compiled models.Multi-linejsonfiles with indentations of four whitespaces for proper support of git tracking generated models.v1.6.1.post3Minor changes to package informationv1.6.1.post4Use version strings instead of numbers that otherwise would be truncated with trailing zeros or result in parsing error.v1.6.1.post5Improve test extension scripts to also work with pytestStructs are now read/write properties after pybind11 build if they are writable in the C++ code