Vs Commands Embedded Python
Vs Commands Embedded Python
This technical memo shows by examples how a VehicleSim (VS) vehicle math model is extended
using the embedded Python interpreter.
VS Solvers have internal math models with built-in equations used to predict the behavior of
vehicles in response to the driving environment and driver/rider controls. In addition to these
“native” models, VehicleSim products provide numerous resources to the user to extend these
models or to provide alternative models completely. These resources are described in the tech
memo: Automating Runs with the VS API, and the tech memo: Extending VS Math Models with
VS Commands and the VS API.
The above-mentioned resources can make use of external simulation environments such as
Simulink (MATLAB) or LabVIEW as platforms for the user to build their own models. In
addition, the user can make use of the VS API to build or enhance math models in various
compiled languages, including C, C++, and Visual Basic. The VS API can also be accessed via
an external Python interpreter.
The VS API allows external code to control the simulation by accessing and advancing the
simulation by a time step. Between time steps, external code can access and modify variables of
the simulation and then advance the simulation further. In this way, an advanced user can modify
1 / 32 March 2022
or replace a portion of the simulation model and still make use of the remaining portions of the
simulation to effect a desired result.
An advanced user can also extend models with VS Commands. This is done within the solver
framework and needs no external resources. Variables are modified between time steps via
runtime equations applied at various times within the simulation (EQ_INIT, EQ_IN, EQ_DYN,
etc.). However, VS Commands have limits. They cannot handle arrays; they cannot loop; they do
not support user-defined functions; etc.
To support more complicated model extensions made within the solver, VS Solvers now support
an internal (embedded) Python interpreter.
Note Currently, Embedded Python cannot be used for Real Time system
applications.
With this command, a string is directly processed by the Python interpreter. The main use for this
command is to designate a Python module for use by the simulation, e.g.:
RUN_PYTHON_STRING “import steercontrol”
In this case, the module [Link] is loaded into the interpreter, and its functions and
variables are available for use. The interpreter looks in a few subdirectories to access module
files. The preferred directory for the user to locate their Python modules is
xxx_Data\Extensions\Python.]
The model functions themselves are accessed with another command, Python(). Inputs and
outputs for the function calls are accomplished with user-defined VS tables. An example call is
shown:
EQ_IN pyret = PYTHON(pycond,"[Link]","Op",in_table,out_table)
With this call, several elements are present, which will be explained. First, this equation is an
expression applied with the EQ_IN command, which means the equation is applied at: “every
time step, just before the built-in equations of motion are applied.” The PYTHON() call can be
applied with any of the other equations commands (EQ_INIT, EQ_DYN, EQ_OUT, etc.).
The pyret variable is used to receive the result of the Python call. The result is primarily to
indicate the success or failure of code operation itself; output relating to the simulation variables
themselves are sent to the output array. Having the PYTHON()command return a value also puts
the call in the form of an equation, which the equation commands are expecting.
The PYTHON()call has five parameters. The first parameter, pycond, is a conditional that
determines whether the call is executed. If the value is 0, then the call is not run. If it is non-zero,
2 / 32
then the call will be run. The conditional parameter is of value if the Python call executes a very
complicated procedure which need not be updated at every time step.
The second parameter is the Python function to be run. This is usually in the form of a string
”[Link]”, based on a module which has been previously imported. This
references the function that performs the desired operation(s) in Python. Since the function can
take multiple inputs and produce multiple returns, a single function may access and update
several VS variables.
The third parameter is also a string (needing quotes) which is used as an optional text input to the
Python function. This is the signal string and can be used to provide a readable conditional input
to the function, or a text parameter (such as a filename) that may be needed by the function. The
parameter does not need to be used and can be ignored by the function.
The fourth parameter is a table of type STEP which is used to provide inputs to the Python
function. The table is defined previously, but updated VS variables can be loaded into the array
(using the appropriate EQ_XX command). The array needs to be one dimensional (a TABLE, not a
CARPET) and large enough to hold all the inputs needed by the function.
The fifth and last parameter is also a table of type STEP which is used to hold the outputs from
the Python function. Like the inputs, the outputs are floating point values. As with the input table,
the output table should be sized for the outputs produced. After calling the Python function, the
output array values can then be used to update VS variables using the add equation commands
(EQ_XXX). If the keyword None is provided as either the fourth or fifth parameter (or both) then
either the input or output is ignored. The command define_table is limited to creating tables
of size two or greater.
If the EQ_XXX command format is not desired, a call can be made as a standalone VS Command
as well:
RUN_PYTHON_PROG [Link] signal in_table out_table
This functions similarly to the Python() except there is no return value, and no conditional
parameter that determines whether the call is executed. Other than that, this access to the Python
utility is similar to the Python() call.
Here are some VS commands that demonstrate how the variable arrays are defined and loaded,
the Python command is called, and how the output values are retrieved.
! Define table for inputs to Embedded Python
define_table in
in_table step
1,1
2,2
3,3
4,4
endtable
3 / 32
2,2
endtable
In the above code segment, the parameter L_FORWARD and the variables YAW, XCG_TM, and
YCG_TM are used to calculate the new variables xprev and yprev. Because these calls make
use of an add equation command (EQ_XX) the equations examined at every time step. In this
example, however, the conditional pycond is set such that the Python code is executed once
every 50 time steps.
4 / 32
command. In this example, the second, the third, and the fourth parameters are not being used.
RUN_PYTHON_PROG executes only once.
The contents of hello_world.py is shown in Figure 1. This script simply writes the string “Hello
World” in a file called [Link] and also in a pop-up window. After running the simulation,
[Link] can be found in xxx_Data directory.
Note A listing of VS Python functions available for use with the Embedded
Python utility can be found the Embedded Python section of the “VS
Commands Reference Manual”.
5 / 32
Figure 2. Script to Add Value 1 to a Counter at Every Time Step.
Since four actuator brake pressure values are going to be updated every time step, the table is
sized for four outputs. It is important to match a table size and output numbers. The function
6 / 32
brake in constant_braking.py is called using python(). There is no input table for this call.
The outputs are stored in outtab_table.
Note This example uses a solver wrapper to execute the simulation. This is
not required for this example, but some packages (such as NumPy) may
require embedded Python simulations to be executed with a solver
wrapper.
7 / 32
Example 4: Simple Steering Control
A simple steering control will be used as an example model extension. This is the same controller
that has been used in several other examples, including the example using VS commands. The
controller is based on a “preview point” in front of the vehicle and the relationship of that
preview point to the road centerline. If the point is not on target, a steering wheel angle is
calculated that is proportional to the lateral distance between the point and the target lateral
position. Figure 4 shows the point represented with an arrow in front of the vehicle. The intended
location is the center of the right-hand lane. The arrow is a little to the right of the target line, and
therefore the vehicle steering wheel should be turned to the left.
Note The example controller presented in this memo is described more fully in
another memo “Extending VS Math Models with VS Commands and the
VS API”, which be reviewed for more details of this model extension.
This section will focus on the implementation of this model extension
using the embedded Python option.
8 / 32
Although the native equations in the VS Solver include the effect of the imported steer, they do
not define the angle. (After all, the whole purpose of the imported steer is to replace or modify the
value already available in the native equations of the model.) Therefore, the example extensions
will calculate the steering wheel angle, based on the position of the target point, which in turn
depends on the current vehicle position.
The output variables available in a VS Math Model can also be viewed using the View button on
the Run Control screen. Reading through the list of available variables shows that there are
many X and Y coordinates, for points such as the origin of the sprung mass coordinate system,
the vehicle center of gravity (CG), etc. A preview point for a new steering controller can be based
on any of the existing X and Y coordinate pairs. In this example, the X and Y coordinates of the
vehicle CG will be used.
where
LAT_TRACK is the target lateral position relative to the road centerline,
ROAD_L is a function available in VS Solvers (see VS Commands) that gives the lateral
distance of a point defined by X and Y coordinates relative to the road reference line
(typically the centerline),
XPREVIEW and YPREVIEW are X and Y coordinates of a point in front of the vehicle, and
The controller calculation is potentially complicated at the start of the run because not all of the
output variables have been calculated yet. For this simple example, the steering wheel angle will
9 / 32
be defined as zero until the simulation time is greater than zero. Therefore, equation 2 is replaced
with a more complicated expression:
IMP_STEER_SW = IF_GT_0_THEN(T,
(LAT_TRACK –ROAD_L(XPREVIEW, YPREVIEW))*GAIN_STEER_CTRL, 0) (5)
This sets the steer to 0 until T is greater than 0, and then uses the expression from equation 2.
Figure 5. Run Screen for a simple steering controller using Embedded Python.
This screen is very similar to the Run Screen used for defining the Steer Controller with VS
Commands. The primary difference is the VS Commands Screen at , ‘Steer Controller with
Embedded Python’. So let’s look at that:
10 / 32
Figure 6. VS Command Screen (upper) for a simple steering controller using Embedded Python.
In this first part of the VS Commands Screen, the LAT_TRACK and GAIN_STEER_CTRL
parameters and the L_FORWARD variables are defined and given values. The python command
is called (also at every time step, conditioned on the value of pycond) and the output placed in
outtab. The variables STEER_CTRL, xprev and yprev are loaded from the output table,
again at every time step.
The Python routine referenced and called with the access call is [Link]. This
is routine in the Python file [Link] and is shown below (Figure 7).
11 / 32
Figure 7. Python routine ‘steer’ which generates Steer, Xpreview and Ypreview.
12 / 32
The Python routine, in this simple example, calculates the values for STEER_CTRL, Xpreview
and Ypreview, which had been calculated natively with the VS Commands implementation of
this controller. The difference is that if a more complicated calculation is needed, the user has
access to the full Python language (loops, different data types, etc.) as well as the Python libraries
of calls and routines. The input values used by the Python routine are either are accessed directly
with VS specific calls that can access VS parameters and values (as in this case) or via an input
list that can be passed into the function in the form of a VS STEP table. In this case, the needed
inputs are loaded into the table, and the Python routine accesses the values in the form of a
Python list of real values. For this call, the signal value (a string) and the name of the output table
(also a string) are used. The ‘NONE’ value indicates that an input table is not used. The outputs
produced by the Python routine use the [Link] variable (accessed from the vs module) to
hold the output values to be sent back to the VS simulation.
ROAD_L_ID() is a VS command which is also accessible from the Embedded Python. After
Xpreview and Ypreview have been calculated, as with the VS Commands version of the Steer
Control, ROAD_L_ID() is called to get the lateral offset from the road for the point given to it.
This value, along with other values retrieved (in this example) using [Link]() to get the present
value of a VS parameter or variable are used to calculate the steer value.
The routine uses the inputs to calculate the steer value using the simple equation that was
previously accomplished with VS Commands. In this routine, the signal value used to use the
“OPEN” steer equation. If other steer equations were to be considered, they could be accessed
with other signal values. After the Python routine is called, the steer value (STEER_CTRL) is
applied to IMP_STEER_SW to complete the model. Xpreview and Ypreview are also
available to allow the drawing of the preview arrow in the video simulation.
The Python routine file itself needs to be located somewhere where the Embedded Python can
find it. At present (2019.1) the Embedded Python looks for directories relative to the directory
location of the executable being run (for example, ..\CarSim_Prog or ..\TruckSim_Prog). The
directories sought out include XXX_Prog\Programs\Python, XXX_Prog\Programs\Python\Lib,
and XXX_Data\Extensions\Python. The ‘Lib’ directory is part of the Embedded Python system
and needs to be accessible for the Python routines to run.
This is a simple implementation of a steering controller which makes use of the embedded Python
utility. It demonstrates how data is transferred to and from the Python environment.
13 / 32
Figure 8. Simulation of vehicle that generates new paths to avoid obstacles.
14 / 32
Figure 9. Run Screen for collision avoidance simulation using Embedded Python.
The run screen has two links of particular note, the link to the obstacles placed in the path and
the link to the events sequence to effect a path change to avoid an obstacle. The screen
defining the obstacles is shown below (Figure 10).
Figure 10. Obstacle Screen for collision avoidance simulation defining obstacles to be avoided.
In Figure 10, the yellow field at potentially assigns a random value to SEEDVAL. But this is
only done if the comment designation (!) is removed. If SEEDVAL is active, then the fourth
obstacle moves randomly (within certain bounds) each time the simulation is run.
15 / 32
The sequence of events used by the collision avoidance simulation is summarized in Figure . The
simulation begins with the vehicle awaiting a detection (Avoid Obstruction). After an obstacle
has been detected, information is stored in the input array, awaiting the call to the Embedded
Python (New Detect). This is repeated (information is overwritten with new information) until the
distance from the obstacle drops below 35 meters. At this point, the Python routine is called with
the loaded information, a new table is created, and this table is loaded to become the new LTARG
table (Update path). At this point, the new LTARG table is used, and the vehicle follows the new
path to avoid the obstacle. Time advances for 3 seconds and then it moves to the next event,
awaiting a new detection (Returned to Path). Upon encountering a new obstacle, the event
advances again, repeating the process (New Detect).
Figure 11.
Figure 11. Event
Event sequence
sequence for
for the
the collision
collision avoidance
avoidance simulation.
simulation.
16 / 32
Listing 1. Python routine used to create new path based on obstacle location.
import math
import vs
# This is a routine which takes input from a running vehicle and
# creates a new path to avoid the detected obstruction.
def pathtoavoid (signal, intab):
vals = []
entry = [0.0]*2
Velocity = intab[0]
Distance = intab[1]
Bearing = intab[2]
LatTarg = intab[3]
Station = intab[4]
entry[0] = (Station+(velnorm*Distance/2))
entry[1] = (LatTarg+pathshift/[Link](2))
[Link]([Link]())
entry[0] = (Station+Distance)
entry[1] = (LatTarg+pathshift)
[Link]([Link]())
entry[0] = (Station+(3*velnorm*Distance)/2)
entry[1] = (LatTarg+pathshift/[Link](2))
[Link]([Link]())
entry[0] = (Station+(2*velnorm*Distance))
entry[1] = LatTarg
[Link]([Link]())
return 0.0
17 / 32
The new table defining the avoiding path is created and loaded during the Update Path Event.
Figure 12. Simulation of vehicle that updates path to avoid multiple obstacles.
18 / 32
vehicle finds itself off the desired path (due to previous path adjustments) it will readjust itself to
get back on the desired path.
In addition to creating a new path based on detected obstacles, the simulation also detects any
collisions the vehicle may have with any of the objects. If a collision occurs, then the red
collision signal is turned on and the time of collision is noted. As configured, the vehicle does
not collide with any objects. To get the vehicle to collide with an object, the position of the
fourth deer can be adjusted so that the vehicle will collide with it. Instructions on how the
position can be adjusted can be found in the yellow comments section of the obstacle screen
(Figure 14).
The run screen for the Collision Avoidance Simulation is shown below (Figure 13).
Figure 13. Run Screen for collision avoidance simulation using Embedded Python.
The run screen has two links of particular note, the link to the obstacles placed in the path and
the link to the events sequence to effect a path change to avoid an obstacle. The screen
defining the obstacles is shown below (Figure 14).
19 / 32
Figure 14. Obstacle Screen for path planning simulation defining obstacles to be avoided.
In Figure 14, the yellow field at indicates how to modify the position of Deer #4 so that a
collision will occur. This is done by accessing the Blue Field at . The Yellow Field at as
contains the commands for the simulation to check for collisions. The variable ‘num_obs’
(defined in the field above) is used by the Embedded Python to determine the number of objects
in the simulation.
The sequence of events used by the collision avoidance simulation is summarized in Figure The
simulation begins with the vehicle awaiting a detection (Avoid Obstruction). After an obstacle
has been detected, information is stored in the input array, awaiting the call to the Embedded
Python (New Detect). This is repeated (information is overwritten with new information) until the
distance from the obstacle drops below 35 meters. At this point, the Python routine is called with
the loaded information, a new table is created, and this table is loaded to become the new LTARG
table (Update path). If no detections occur for a period of time, and the vehicle is off of its
intended path, it will move to get back to its path (Move Back To Path). At this point, the new
LTARG table is used, and the vehicle follows the new path to avoid the obstacle. Time advances
for 3 seconds and then it moves to the next event, awaiting a new detection (Returned To Path).
Upon encountering a new obstacle, the event advances again, repeating the process (New Detect).
Again, if no obstacles are seen for a time, and the vehicle is not on its intended path, it will move
to get back on its path (Move Back To Path). After executing a move to move back to path, the
event will advance to again wait for new detections (Returned To Path).
20 / 32
Avoid New Update Returned
Obstruction Detect Path To Path
Move Back
To Path
21 / 32
Listing 3. Python routine used to detect collisions based on vehicle and objects locations.
import math
import vs
num_objects = int([Link]("NUM_OBS"))
#Look through all objects, to see if they are close to current
#position of vehicle.
[Link](0)
return 0.0
If the position of the last deer is adjusted as mentioned above, then the vehicle will collide with
the deer. The collision will be detected and noted on the simulation display. A screen shot
showing the point of collision is shown in Figure 16.
22 / 32
Figure 16 Simulation with modified obstacle position resulting in a collision.
23 / 32
Figure 17. Simulation of vehicle that optimizes a parameter.
Implementation
This example uses Embedded Python to calculate the metric and record the best values
encountered so far. A simple table is used to hold the values of L_FORWARD to test, but more
sophisticated strategies could also be employed. This simulation only runs a fixed number of
iterations, but again, a more sophisticated strategy could also be employed to have a variable
number of iterations, if desired. The user also has the option for the output results to be displayed
with a Pop-Up screen (the simulation ends). To activate this screen, a single variable, PrintVals
needs to be changed from 0 to 1.
The run screen for the Parameter Optimizing Simulation is shown below (Figure 18).
24 / 32
Figure 18. Run Screen for parameter optimization simulation using Embedded Python.
The run screen has three links of particular note, the link to the commands that define the
Steer Controller with Embedded Python (this is similar to the simple Steering Controller example
used in other simulations), the link that contains the tables and code to update the parameter
for each iteration, 3 the link to the events sequence which iterates the simulation through a
number of attempts, and records the results. The screen showing the parameter update is shown
below (Figure 19).
25 / 32
Figure 19. Parameter Update Screen for Parameter Optimizing Simulation (upper).
What is notable here are several variables defined to support checking different parameter
choices. One table holds the different values to be used for the L_FORWARD parameter.
Another table is defined to hold the metric result that indicates the suitability of a particular
parameter for the simulation vehicle to closely follow the desired path. These metric results for a
parameter are compiled during each iteration using that parameter. The metric in this case is just
a sum of the absolute value of the lateral error (distance of the vehicle’s position from the desired
path) that is calculated each time the steering value is updated.
Another variable of interest is the PrintVals variable. If set to one, then a Pop-Up screen will
occur at the end of the simulation to show the metric results for all of the parameter choices and
the best value for the parameter, based on the metric. If not using the Pop-Up screen (nominal
operation) then the user can see the best value of L_FORWARD by viewing the ‘Echo file with
final conditions’ output file and noting the value of ‘BESTVAL’. The metric value for this
setting is found in ‘BESTVALERROR’. An ending screen showing the Pop-Up screen with the
simulation results is shown in Figure 21.
26 / 32
Figure 20. Parameter Update Screen for Parameter Optimizing Simulation (lower).
This bottom portion of the screen is shown merely to show how the L_FORWARD parameter is
updated. Using an EQ_DYN, the value is assigned from the values table, based on the iteration
being run. The iteration variable, ITERS, is updated via the events sequence.
The events sequence increases the iteration count if the vehicle has reached a station near the end
of the road. It then stores the calculate error metric and resets the vehicle to Station 0, updates the
L_FORWARD parameter to a new value, and restarts the error accumulation. After the desired
number of iterations have been run, the simulation ends.
Figure 21. End Pop-Up Screen for Parameter Optimizing Simulation when PrintVals = 1.
27 / 32
The simulation demonstrates, in a very simple and straightforward way, how a simulation can be
iterated through different values of a single parameter. A metric is calculated to evaluate the
relative worthiness of a parameter choice compared with the other choices that are considered.
Based on the metric, an optimized parameter can be selected.
28 / 32
and informs that all getval and setval in the code up until the error has occurred. In this example,
it informs that it has processed at least up to the line of code, getval: Pbk_Con (Figure 25),
but did not reach the line of code var: pbk_L1 (about 30 lines later), so the user can
somewhat narrow down where the problem occurs. The user can also add several
[Link]()functions in the code instead of using [Link]. For example, [Link](“Pass 1”)
is added in earlier part of the code, and [Link](“Pass 2”) can be added later part of the code.
When executed, a pop-up message is displayed with these print statements.
The [Link]() and [Link]() commands can print out with each time step, so they are usually not
practical to be active when running working code. To halt a simulation which is displaying a
print or debug screen with each time step, simply hit the main ‘stop’ button on the paused
simulation control, and then close the print or debug window. Sometimes, the user may have to
move the print/debug window to access the simulation ‘stop’ button.
As mentioned above, unfortunately, [Link]() and [Link]() can be useful for only
certain types of the error. Therefore, adding and testing a small block of code at a time is
recommended when writing an embedded Python program.
29 / 32
Figure 25. Message When [Link](1) is Used.
Note The [Link] object is useful for getting values, (sometimes) setting
values, and interrogating the contents of VS tables:
[Link](“TAB_NAME”).num_rows()
[Link](“TAB_NAME”).num_cols()
[Link](“TAB_NAME”).entry(c,r,n).value()
[Link](“TAB_NAME”).entry(c,r,n).set_value(value)
Get (or sometimes Set) a Table value based on x-value (r), y-value (c)
and table index (n) if there are multiple tables in a group.
[Link](“TAB_NAME”).entry(i,j,n).defvalue()
[Link](“TAB_NAME”).entry(-1,j,n).defvalue()
[Link](“TAB_NAME”).entry(i,-1,n).defvalue()
Get the defined values (how the table was initially defined) of a table
where i is the column index, j is the row index, and n is the table index. If
you set i or j to -1, you can retrieve the x or y entry values, respectively.
30 / 32
Note If an outside Python distribution has been updated, it is possible that the
solver may not recognize all of the updates. The user may need to add
one or more directory paths for the simulation to work. This can be done
using the Python command [Link](). In particular, the
‘site-packages’ sub-directory in the Python distribution area may
need to be added.
Note Some packages (notably NumPy) do not close properly when run from
embedded Python distributions. This is a problem or bug with NumPy in
that it does not close properly unless the parent process is ended. The
behavior is that a simulation using NumPy can run once, but if the
browser interface is run again, the embedded Python fails. This can be
problematic if using the browser to run and develop a simulation.
There is a simple workaround, however. If one calls the solver using an
external wrapper, then the process is not associated with the browser
process, and the problem does not occur. This can be done by selecting
‘Models: Self-Contained Solvers’ on the run screen, and then selecting
31 / 32
either the 32-bit or 64-bit External Wrapper programs (included) to run
the solver. The Braking example uses a solver wrapper to show how this
is done.
Summary
This document shows how a VS Math Model can be extended using the Embedded Python utility.
Three examples are provided; one is a nominal steering controller, which has been implemented
by other means (including ordinary VS commands) previously; another example creates new
target paths as needed to avoid novel obstacles a vehicle may encounter in a simulation; and the
third example runs multiple iterations to find an optimal value to a simulation parameter.
The examples are intended to show the breadth of capability that is feasible with the Embedded
Python utility.
Basically, by making use of the Embedded Python utility to extend the model:
1. No additional software needed beyond CarSim/TruckSim/BikeSim. Some type of utility
or package is needed to create and edit Python routines.
2. Variables and parameters can be sent to the Python utility via the input table. There is no
limit to the number of input values that can be sent to the utility.
3. Based on inputs provided with the input table, the user has a complete, advanced
programming language (Python) to develop a new algorithm or routine which can process
the input data and produce output(s) for use by the simulation.
4. There is no limit to the number of outputs (real numbers) that can be sent from the utility
back to the simulation.
5. The basic commands needed to access the utility are RUN_PYTHON_STRING and
RUN_PYTHON_PROG.
Other methods are available that use the VS API to run a VS Solver from the control of another
program. These external programs can be written in MATLAB, C, and even Python. The
Embedded Python utility serves a different purpose; control need not be given to an external
program (though that is allowed) but an internal routine needed to update a simulation variable
can now be written in a full-featured language to complement the existing VS Commands.
Moreover, since Python is interpretive, the routine can be modified and updated without going
through a compilation step or employing 3rd party packages. The user thus has a new tool to aid in
low level routine development for their simulations.
32 / 32