Code generation for MIC32 (Fuzzy controller testing)¶
This demo presents the development of the Engee model for testing a fuzzy controller over the full range of input signals with output to the DAC of a MIK32V2 microcontroller.
The purpose of the example is to test the operation of a fuzzy regulator with two inputs and one output. The fuzzy regulator model is built from the individual blocks of the Engee basic library for easy familiarisation with the functionality of the regulator. The target device used in this demo is a MIK32 NUKE V0.3 debug board based on K1948VK018 MIK32 Amur microcontroller. Compilation and loading of the code into the microcontroller is done from VS Code with PlatformIO extension.
The general view of the model is shown in the figure below.
It consists of two input setting blocks, test signals, a fuzzy controller subsystem with two inputs and one output, an output signal scaling subsystem, and MIC32 peripheral blocks of the HAL library.
This setting of the input signals makes it possible to check the results of the controller output calculations over the entire value range of one and the other signal.
The work of the subsystem scaling
consists in changing the range of values and data type of the regulator output signal: $dac\_val = 2048\cdot (y+1)$:
The signal is limited from 0 to 4095 because the MIC32 DACs are 12-bit, the data format is integer, unsigned, 2 bytes.
Structure of the fuzzy controller¶
The fuzzy controller is based on Mamdani's algorithm and consists of identical phasification and aggregation blocks, activation block (min function), accumulation block (max function), defuzzification block (centre of gravity method for one-point sets, COGS).
The number of linguistic terms of the input signals is 3 and the output signal is 3.
Phasification and aggregation¶
The input variables have three linguistic terms taking non-zero truth values in the ranges - "LOW": $x_S \in(-\infty;\ 0)$, "MEDIUM": $x_M \in(-1;\ 1)$, "HIGH": $x_L \in(0;\ \infty)$. At the aggregation stage, triangular membership functions (FPs) symmetric with respect to zero are applied to the input signals. In the fuzzyfication
subsystems, the FPs are reproduced by 1-D Lookup Table blocks. The calculation of TPs for the input variables terms is given below.
Saturation function of input signals:
Application of saturation to terms, construction of membership functions:
In the activation step, subconclusions for all combinations of terms are defined in the subsystem activation
. Activation is performed by the minimum function:
$$\mu_{SS} = \min(\mu_{1S},\mu_{2S}),\
\mu_{SM} = \min(\mu_{1S},\mu_{2M}),\
\mu_{SL} = \min(\mu_{1S},\mu_{2L})$$
$$\mu_{MS} = \min(\mu_{1M},\mu_{2S}),\
\mu_{MM} = \min(\mu_{1M},\mu_{2M}),\
\mu_{ML} = \min(\mu_{1M},\mu_{2L})$$
$$\mu_{LS} = \min(\mu_{1L},\mu_{2S}),\
\mu_{LM} = \min(\mu_{1L},\mu_{2M}),\
\mu_{LL} = \min(\mu_{1L},\mu_{2L})$$
The following rule base is reproduced in the accumulation
subsystem:
- IF ($x_{1}=$ "LOW" AND ($x_{2}=$ "LOW" OR $x_{2}=$ "MEDIUM")) THEN $y =$ "LOW".
- IF ($x_{1}=$ "LOW" AND $x_{2}=$ "HIGH") OR
($x_{1}=$ "MEDIUM" AND ($x_{2}=$ "LOW" OR $x_{2}=$ "MEDIUM") )) OR
($x_{1}=$ "HIGH" AND $x_{2}=$ "LOW") TO $y =$ "MEDIUM".
- ** IF** ($x_{1}=$ "MEDIUM" AND $x_{2}=$ "HIGH") OR
($x_{1}=$ "HIGH" AND ($x_{2}=$ "MEDIUM" OR $x_{2}=$ "HIGH")) TO $y =$ "HIGH"
For clarity, let's present it in the form of a table:
The degrees of truth of the output terms are defined in the maximum function of the subconclusions according to the expressions:
$$\mu_{y_S} = \max(\mu_{SS},\ \mu_{SM}),$$
$$\mu_{y_M} = \max(\mu_{SL},\ \mu_{MS},\ \mu_{MM},\ \mu_{LS}),$$
$$\mu_{y_L} = \max(\mu_{ML},\ \mu_{LM},\ \mu_{LL})$$
The subsystem defuzzyfication
implements the centre of gravity method for one-point sets (COGS). Midpoints for the membership functions of the output terms: $y_S= -1;\ y_M= 0;\ y_L= 1$. The calculation is realised without and with ($y$ ) and with ($y_1$ ). For modelling and testing we use the first method.
Let's load and run the example model:
Plot the graphs of the output variable
This signal in the program loaded into the controller and will be sent to the digital to analogue converter.
Code generation and project assembly¶
[ Info: Generated code and artifacts: /user/start/examples/codegen/mik32_fuzzy/code
The project with the model is built in IDE VS Code+PlatformIO, the process of building is similar to the process from the example MIC32: Sawtooth Signal Generator.
The only exception is that the compiled programme is loaded not into the RAM of the controller, but into flash-memory via SPIFI interface. To configure PlatformIO in the example, the configuration file platformio.ini
is attached. Let's move on to the execution of the code on the microcontroller.
Code execution on MIK 32¶
After successful assembly and compilation of the project, connect an oscilloscope to channel P1.12 of the microcontroller and display the oscillogram.
As can be seen from the oscillogram, the DAC channel reproduces the shape of the modelled output signal of the fuzzy controller under test.
In this example we have considered the structure of the fuzzy controller in the Engee model, simulated its testing and tested the controller on the MIC32 Amur microcontroller after code generation from the model. The results of the modelling and testing on the controller are identical and consistent with the specified algorithm.
{"id": "883db1bc-effa-410d-8a28-d8c4c0db9f9b", "data": [{"showlegend": false, "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y1", "colorscale": [[0, "rgba(0, 0, 255, 1.000)"], [0.5, "rgba(255, 0, 0, 1.000)"], [1, "rgba(0, 128, 0, 1.000)"]], "yaxis": "y", "zmin": 0.5, "z": [[0.5, 1.5, 1.5], [0.5, 1.5, 2.5], [1.5, 2.5, 2.5]], "zmax": 2.5, "y": [0.5, 1.5, 2.5, 3.5], "type": "heatmap", "showscale": false, "x": [0.5, 1.5, 2.5, 3.5], "zaxis": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 4, "currentCount": 4}}}, {"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y2", "zmin": 0.5, "yaxis": "y", "legendgroup": "y2", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [-299.5, 303.5], "type": "scatter", "x": [0.5, 0.5], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y2", "zmin": 0.5, "yaxis": "y", "legendgroup": "y2", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [-299.5, 303.5], "type": "scatter", "x": [1.5, 1.5], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y2", "zmin": 0.5, "yaxis": "y", "legendgroup": "y2", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [-299.5, 303.5], "type": "scatter", "x": [2.5, 2.5], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y2", "zmin": 0.5, "yaxis": "y", "legendgroup": "y2", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [-299.5, 303.5], "type": "scatter", "x": [3.5, 3.5], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y3", "zmin": 0.5, "yaxis": "y", "legendgroup": "y3", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [0.5, 0.5], "type": "scatter", "x": [-533.1246518237803, 537.1246518237803], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y3", "zmin": 0.5, "yaxis": "y", "legendgroup": "y3", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [1.5, 1.5], "type": "scatter", "x": [-533.1246518237803, 537.1246518237803], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y3", "zmin": 0.5, "yaxis": "y", "legendgroup": "y3", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [2.5, 2.5], "type": "scatter", "x": [-533.1246518237803, 537.1246518237803], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}, {"showlegend": false, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.5086805555555556, "title": {"text": ""}, "len": 0.8657316272965879, "x": 0.9943757030371203}, "name": "y3", "zmin": 0.5, "yaxis": "y", "legendgroup": "y3", "zmax": 2.5, "line": {"color": "rgba(0, 0, 0, 1)", "shape": "linear", "dash": "solid", "width": 1}, "y": [3.5, 3.5], "type": "scatter", "x": [-533.1246518237803, 537.1246518237803], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 2, "currentCount": 2}}}], "config": {"showlegend": false, "xaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [1, 2, 3], "range": [-0.6623117006158221, 4.662311700615822], "domain": [0.11633858267716533, 0.9943757030371203], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["низкое", "среднее", "высокое"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "y", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "Термы x1", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "paper_bgcolor": "rgba(255, 255, 255, 1.000)", "annotations": [{"yanchor": "top", "xanchor": "center", "rotation": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "paper", "showarrow": false, "text": "База правил", "xref": "paper", "x": 0.5553571428571429}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "низкое", "xref": "x", "x": 1}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "среднее", "xref": "x", "x": 2}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "среднее", "xref": "x", "x": 3}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 2, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "низкое", "xref": "x", "x": 1}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 2, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "среднее", "xref": "x", "x": 2}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 2, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "высокое", "xref": "x", "x": 3}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 3, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "среднее", "xref": "x", "x": 1}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 3, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "высокое", "xref": "x", "x": 2}, {"yanchor": "middle", "xanchor": "center", "rotation": 0, "y": 3, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "y", "showarrow": false, "text": "высокое", "xref": "x", "x": 3}], "height": 400, "margin": {"l": 0, "b": 20, "r": 0, "t": 20}, "plot_bgcolor": "rgba(255, 255, 255, 1.000)", "yaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [1, 2, 3], "range": [0.5, 3.5], "domain": [0.07581474190726165, 0.9415463692038496], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["низкое", "среднее", "высокое"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "x", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "Термы x2", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "width": 679}}
{"id": "86b2128e-7898-40df-926d-edeb4b4a316e", "data": [{"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"y": 0.48958333333333337, "title": {"text": ""}, "len": 0.9039260717410325, "x": 0.5254622736082012}, "name": "y", "yaxis": "y", "legendgroup": "y", "line": {"color": "rgba(0, 154, 250, 1.000)", "shape": "linear", "dash": "solid", "width": 1}, "y": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 409, 409, 819, 819, 1228, 1228, 1638, 1638, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 409, 409, 819, 819, 1228, 1228, 1638, 1638, 2048, 2048, 2457, 2457, 2599, 2599, 2678, 2678, 2756, 2756, 2835, 2835, 2914, 2914, 2993, 2993, 3072, 3072, 3150, 3150, 3229, 3229, 787, 787, 708, 708, 630, 630, 551, 551, 472, 472, 393, 393, 409, 409, 819, 819, 1228, 1228, 1638, 1638, 2048, 2048, 2457, 2457, 2867, 2867, 3276, 3276, 3686, 3686, 3702, 3702, 3623, 3623, 3544, 3544, 3465, 3465, 3387, 3387, 3308, 3308, 866, 866, 945, 945, 1024, 1024, 1102, 1102, 1181, 1181, 1260, 1260, 1748, 1748, 2237, 2237, 2725, 2725, 3213, 3213, 3702, 3702, 3686, 3686, 3276, 3276, 3276, 3276, 3686, 3686, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2457, 2457, 2867, 2867, 3276, 3276, 3686, 3686, 4095, 4095, 3686, 3686, 3276, 3276, 3276, 3276, 3686, 3686, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "type": "scatter", "x": [0, 0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.04, 0.04, 0.05, 0.05, 0.06, 0.06, 0.07, 0.07, 0.08, 0.08, 0.09, 0.09, 0.1, 0.1, 0.11, 0.11, 0.12, 0.12, 0.13, 0.13, 0.14, 0.14, 0.15, 0.15, 0.16, 0.16, 0.17, 0.17, 0.18, 0.18, 0.19, 0.19, 0.2, 0.2, 0.21, 0.21, 0.22, 0.22, 0.23, 0.23, 0.24, 0.24, 0.25, 0.25, 0.26, 0.26, 0.27, 0.27, 0.28, 0.28, 0.29, 0.29, 0.3, 0.3, 0.31, 0.31, 0.32, 0.32, 0.33, 0.33, 0.34, 0.34, 0.35, 0.35, 0.36, 0.36, 0.37, 0.37, 0.38, 0.38, 0.39, 0.39, 0.4, 0.4, 0.41, 0.41, 0.42, 0.42, 0.43, 0.43, 0.44, 0.44, 0.45, 0.45, 0.46, 0.46, 0.47, 0.47, 0.48, 0.48, 0.49, 0.49, 0.5, 0.5, 0.51, 0.51, 0.52, 0.52, 0.53, 0.53, 0.54, 0.54, 0.55, 0.55, 0.56, 0.56, 0.57, 0.57, 0.58, 0.58, 0.59, 0.59, 0.6, 0.6, 0.61, 0.61, 0.62, 0.62, 0.63, 0.63, 0.64, 0.64, 0.65, 0.65, 0.66, 0.66, 0.67, 0.67, 0.68, 0.68, 0.69, 0.69, 0.7, 0.7, 0.71, 0.71, 0.72, 0.72, 0.73, 0.73, 0.74, 0.74, 0.75, 0.75, 0.76, 0.76, 0.77, 0.77, 0.78, 0.78, 0.79, 0.79, 0.8, 0.8, 0.81, 0.81, 0.82, 0.82, 0.83, 0.83, 0.84, 0.84, 0.85, 0.85, 0.86, 0.86, 0.87, 0.87, 0.88, 0.88, 0.89, 0.89, 0.9, 0.9, 0.91, 0.91, 0.92, 0.92, 0.93, 0.93, 0.94, 0.94, 0.95, 0.95, 0.96, 0.96, 0.97, 0.97, 0.98, 0.98, 0.99, 0.99, 1, 1, 1.01, 1.01, 1.02, 1.02, 1.03, 1.03, 1.04, 1.04, 1.05, 1.05, 1.06, 1.06, 1.07, 1.07, 1.08, 1.08, 1.09, 1.09, 1.1, 1.1], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 221, "currentCount": 221}}}], "config": {"showlegend": true, "xaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [0, 0.25, 0.5, 0.75, 1], "range": [0, 1.1], "domain": [0.5253313771854498, 0.5254622736082012], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["0.00", "0.25", "0.50", "0.75", "1.00"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "y", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "paper_bgcolor": "rgba(255, 255, 255, 1.000)", "annotations": [{"yanchor": "top", "xanchor": "center", "rotation": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 20}, "yref": "paper", "showarrow": false, "text": "Выходной сигнал", "xref": "paper", "x": 0.5253968253968254}], "height": 400, "margin": {"l": 0, "b": 20, "r": 0, "t": 20}, "plot_bgcolor": "rgba(255, 255, 255, 1.000)", "yaxis": {"showticklabels": true, "gridwidth": 0.5, "tickvals": [0, 1000, 2000, 3000, 4000], "range": [-122.84999999999991, 4217.85], "domain": [0.03762029746281716, 0.9415463692038496], "mirror": false, "tickangle": 0, "showline": true, "ticktext": ["0", "1000", "2000", "3000", "4000"], "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "x", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "legend": {"yanchor": "auto", "xanchor": "auto", "bordercolor": "rgba(0, 0, 0, 1)", "bgcolor": "rgba(255, 255, 255, 1.000)", "borderwidth": 1, "tracegroupgap": 0, "y": 1, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "title": {"font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}, "text": ""}, "traceorder": "normal", "x": 1}, "width": 679}}