Languages

Apollo

Run SENTIL inside Baidu Apollo as a Cyber RT module: STL and PrSTL monitoring over Apollo channels, a shield or a synthesized controller, and the Bazel reference.

The sentil-apollo module drops into an Apollo workspace as modules/sentil and runs the SENTIL engine as Cyber RT components. A monitor watches Apollo channels against STL and probabilistic STL specifications and publishes a verdict per formula. A control component shields Apollo's nominal command inside a specification's bounds, or synthesizes its own command from the specification. Everything is configured in protobuf text, so a deployment is a config file plus a launch command, with no glue code. The build links the staged core through the @sentil_cpp Bazel external repository; once the tree is staged, Bazel never touches Rust. Cyber RT runs on Linux only.

Install

Apollo has no package registry, and the releases page carries no Apollo asset. One route exists: the module is source, copied into an Apollo workspace as modules/sentil and built there by Apollo's buildtool on top of Bazel. The operating system to think about is the dev container's rather than the host's, and since Cyber RT is Linux only there is no macOS or Windows workspace to drop into. Apollo 9.0 and newer build modules with the package method these steps use.

From source

Start the dev container and run every command below inside it, where buildtool and Bazel live. The third step builds the core with cargo, so a Rust toolchain has to be on hand once; install one from rustup.rs if the container has none.

aem start

Get the SENTIL source. A clone leaves you on the default branch until you check out a tag; the archive is the 0.3.0 tree and needs no git.

git clone https://github.com/sedislab/SENTIL
cd SENTIL
curl -L https://github.com/sedislab/SENTIL/archive/refs/tags/v0.3.0.tar.gz -o sentil.tar.gz
tar -xzf sentil.tar.gz
cd SENTIL-0.3.0

Build the core once and stage an install tree. This is the shared from source build followed by a copy of the headers, the shared library, and the deterministic oracle into one prefix. Any prefix works, and /opt/sentil is the one CI uses; whichever you pick is the path that @sentil_cpp takes two steps down.

cargo build --release -p sentil-ffi
install -d /opt/sentil/include/sentil /opt/sentil/lib /opt/sentil/share/sentil
cp sentil-cpp/include/sentil/*.hpp /opt/sentil/include/sentil/
install -m644 sentil-ffi/include/sentil.h /opt/sentil/include/sentil.h
install -m644 target/release/libsentil.so /opt/sentil/lib/libsentil.so
install -m644 benchmarks/deterministic/oracle.json /opt/sentil/share/sentil/oracle.json

The staged tree is the C header, the C++ surface, the shared library, and the oracle oracle_parity_test replays:

/opt/sentil
├── include
│   ├── sentil.h
│   └── sentil
│       ├── sentil.hpp
│       ├── types.hpp
│       └── errors.hpp
├── lib
│   └── libsentil.so
└── share
    └── sentil
        └── oracle.json

The oracle rides in the install tree because an Apollo workspace has no other way to reach the SENTIL checkout, and a released binary bundle already ships it in this layout. A tree staged without it still builds; the parity test then fails with the path it looked for.

Confirm it links before Bazel starts depending on it. The module's symbol reference names every engine call the components make, so it compiles against the staged tree alone and needs no Apollo workspace:

g++ -std=c++17 -I/opt/sentil/include sentil-apollo/tests/symbol_reference.cc \
  -L/opt/sentil/lib -lsentil -o symbol_reference
LD_LIBRARY_PATH=/opt/sentil/lib ./symbol_reference

It prints nothing and exits 0. A missing or stale core fails at the link, naming the symbol it wanted, instead of surfacing halfway through a module build.

Copy the module into the Apollo workspace as modules/sentil. The name is not yours to choose: cyberfile.xml declares //modules/sentil as its src_path.

cd /path/to/apollo-workspace
cp -r /path/to/SENTIL/sentil-apollo modules/sentil

Declare @sentil_cpp in the workspace, pointing at the staged tree with bzl/sentil_cpp.BUILD as its build file. Every target in the module depends on @sentil_cpp//:sentil_cpp, which is the only door the engine comes through.

WORKSPACE
new_local_repository(
    name = "sentil_cpp",
    path = "/opt/sentil",
    build_file = "//modules/sentil:bzl/sentil_cpp.BUILD",
)

bzl/ carries no BUILD file of its own, so the build file is addressed as a path inside the //modules/sentil package rather than as //modules/sentil/bzl:sentil_cpp.BUILD, which would ask Bazel for a package that is not there.

Build the module, install it, and run its tests. The sentil in both buildtool commands is the name cyberfile.xml declares, alongside version 0.3.0, type module, and the Apollo dependencies cyber and common-msgs.

buildtool build -p sentil
buildtool install sentil
bazel test //modules/sentil/tests/...

Six test targets sit under tests/: field_extractor_test over protobuf reflection, oracle_parity_test for the deterministic oracle through the linked core, synthesizer_test for the synthesis bridge, example_synthesis_test and synthesize_then_monitor_example_test for the two shipped synthesis examples, and symbol_reference, which pins every engine call so a header drift breaks the build instead of a drive. None of the six needs a Cyber RT runtime, so a failure here points at the module or the staged core and not at a missing runtime. One of them reaches outside the module: oracle_parity_test takes @sentil_cpp//:deterministic_oracle as data, which is the share/sentil/oracle.json you staged in the first step.

Your first monitor

Write a formula and a field mapping into the config the monitor dag points at, then launch. This is the shipped examples/ego_speed_monitor.pb.txt shape: the ego speed must stay under 20 m/s over any five second window.

modules/sentil/monitor/conf/sentil_monitor.pb.txt
formulas {
  id: 1
  expression: "G[0, 5.0] (ego_speed < 20.0)"
}
input_channels {
  channel: "/apollo/canbus/chassis"
  message_type: "apollo.canbus.Chassis"
  fields {
    variable: "ego_speed"
    field_path: "speed_mps"
  }
}
output_channel: "/apollo/sentil/status"
cyber_launch start modules/sentil/monitor/launch/sentil_monitor.launch
cyber_monitor -c /apollo/sentil/status

The predicate ego_speed < 20.0 scores 20 - ego_speed, so a chassis reading of 17.2 m/s carries a margin of 2.8 and a reading of 21.0 scores -1.0; G, read always, keeps the worst margin in the window, so one bad second decides the verdict. That worst margin is the robustness each FormulaResult on the status carries, alongside a satisfied flag, a severity, and an all_satisfied flag across the set. The engine folds each message at O(1) amortized cost per sample through the monotonic deque, so the monitor holds a real-time loop.

Channels as signals

Each input_channels entry names a channel, its fully qualified message_type, and the fields to read. A field binds a formula variable to either a field_path or a builtin.

A field_path is a dotted, optionally indexed path into the message: speed_mps, pose.position.x, perception_obstacle[0].position.x. Paths resolve against the message descriptor when the component initializes, so a typo fails at startup instead of reading as a silent zero mid-drive.

A builtin reduces the whole obstacle list to one number. A fixed index like perception_obstacle[0] names whichever obstacle perception listed first, not the nearest, so the reductions a safety formula actually wants are built in:

BuiltinMeaning
NEAREST_OBSTACLE_DISTANCEThe smallest planar range to any obstacle
MIN_TTCThe smallest range over closing speed, over obstacles closing ahead
FRONT_GAPThe smallest forward range to an obstacle within the ego lane corridor

The builtins read the standard perception obstacle list and treat each obstacle's position and velocity as ego relative. An upstream adapter must transform Apollo's world-frame perception into the ego frame before the monitor reads it.

Where the readers come from differs by shell. The fused monitor's readers are declared in its dag file. The timed monitor creates its own readers, matching each mapping to a reader by message_type and taking the mapping's channel, with the standard perception, localization, and chassis channels as the fallback.

Streaming and the two monitor shells

SentilMonitorComponent, the default, is a fused component triggered by perception with localization and chassis as co-channels, so each verdict reflects one consistent message set. SentilTimedMonitorComponent reads the same channels through pull readers and evaluates on a timer, every 100 ms as shipped in its dag; reach for it when a steady verdict cadence matters more than reacting to each perception frame.

Both shells share one engine and one readiness discipline. Each tick is stamped with the perception header time, falling back to the Cyber RT clock, and a stalled or rewound stamp skips the tick rather than feeding the engine a non-monotonic time. No status is published until every variable a formula binds has been seen at least once. While a bounded window is still open the verdict is unresolved: the status reports the robustness as a min and max bound with is_concrete false and the severity WARN, and it tightens to a concrete value once the window closes.

For a P formula the status also carries the live probability on every tick: prob_result holds the running estimate with its Wilson interval at the configured confidence, which is the same live-probability accessor the other bindings expose, delivered as a message field.

Downstream, planning subscribes to /apollo/sentil/status and falls back when all_satisfied goes false or a probabilistic interval drops below its floor. examples/safety_planner_subscriber.cc is the small standalone version of that contract; it tests interval().lower() against the floor, the pessimistic end, since waiting for the upper end would fall back only after even the optimistic estimate had failed.

Probabilistic monitoring

A formula selects the probabilistic path with its leading P and nothing else; the P operator is the whole switch. For each such formula the monitor builds a probabilistic monitor from lifting_registry, lifts each reading into a sample_budget ensemble, and reports the satisfaction estimate with a Wilson interval at confidence. The shipped examples/follow_distance_prstl.pb.txt holds a follow distance under Gaussian perception noise:

follow_distance_prstl.pb.txt
formulas {
  id: 1
  expression: "P>=0.99(G[0, 2.0] (front_gap > 5.0))"
}
input_channels {
  channel: "/apollo/perception/obstacles"
  message_type: "apollo.perception.PerceptionObstacles"
  fields {
    variable: "front_gap"
    builtin: "FRONT_GAP"
  }
}
output_channel: "/apollo/sentil/status"
lifting_registry {
  variable: "front_gap"
  noise {
    gaussian {
      mean: 0.0
      std_dev: 0.25
    }
  }
  interaction: ADDITIVE
}
sample_budget: 1000
confidence: 0.99

The noise oneof accepts eight families, listed with their parameters in the monitor config reference below, and interaction is ADDITIVE or MULTIPLICATIVE. The full theory behind the families lives in noise models.

algorithm, semantics, and sprt_config configure only the offline sentil_record_analyzer. The live monitor ignores algorithm entirely: the leading P alone builds the probabilistic path, from lifting_registry, sample_budget, and confidence.

For the heavier statistics a per-tick budget cannot afford, sentil_record_analyzer replays a recorded drive over the same config:

sentil_record_analyzer --config=modules/sentil/monitor/conf/sentil_monitor.pb.txt --record=drive.record

The analyzer reads the record into a trace by sample and hold: each message updates its variables, and a row is appended once every configured variable has been seen, on a strictly increasing time axis. It then runs the configured algorithm per formula. DETERMINISTIC computes the robustness, dense or discrete per semantics. SMC estimates the probability with its interval. SPRT runs Wald's sequential test between p0 and p1; the sprt_config defaults are p0 0.90, p1 0.95, alpha and beta 0.05, and sample_budget caps the sample count, so an undecided run reports inconclusive when the budget runs out. AMS is rejected with an error: rare-event splitting runs over a stochastic model, not a recorded trace, so reach for the rare events path in the library or the CLI.

Synthesis and control

SentilControlComponent turns a specification into a ControlCommand, in one of three modes set in control/conf/sentil_control.pb.txt.

SHIELD, the default, guards Apollo's own controller. Each tick it takes the latest nominal command off nominal_channel, reads the actuation fields named by control_outputs, projects them into bounds through the safety filter, and writes the projected command with the rest of the nominal command intact. Until a nominal command has been observed it publishes nothing; the timer fires, the tick passes, no command is written.

SYNTHESIZE makes SENTIL the controller. Each tick it builds the state vector, plans over the model's horizon inside a hard budget of deadline_fraction * control_period_ms, and writes this step's synthesized input onto the command through control_outputs. The controller is anytime, emitting the best feasible input found within the budget. When the state cannot be built yet, because a variable has not been seen, the tick is skipped without publishing.

ADVISORY plans exactly as SYNTHESIZE but publishes to /apollo/sentil/control_advice with no actuation authority, so a synthesized controller can be judged against a live drive before it is trusted. In this mode the fixed advice channel overrides output_channel.

Two contracts here differ from the monitor. First, the state readers are hardcoded to /apollo/localization/pose and /apollo/canbus/chassis; a state_inputs entry selects what to extract through its message_type and fields, and its channel value is not used to place a reader. Second, a ControlOutput.field_path must name a top-level float or double field of ControlCommand, such as throttle, brake, steering_target, or acceleration; unlike the monitor's field_path it accepts no dotted or indexed paths, and an entry whose index reaches input_width or whose field is not floating point fails at startup. bounds is optional and an omitted bounds means unbounded inputs. The shipped dag ticks every 10 ms to match the default control_period_ms; if you change the period, change the dag interval with it. The launch file sets exception_handler: respawn, so a crashed control process restarts rather than staying dead.

The design-time half is sentil_synthesizer, which reads a SYNTHESIZE-shaped config carrying a model and needs no Cyber RT runtime:

sentil_synthesizer --config=examples/synthesize_control.pb.txt --op=plan

--op=plan synthesizes the open-loop input sequence over the horizon, reporting its robustness and whether it holds, so an infeasible spec comes back as the closest plan with holds false rather than nothing. --op=witness searches for a counterexample input that violates the spec, and requires bounds to search over. --op=chance perturbs the model's autonomous dynamics with Gaussian process noise of --process_std and checks that the spec holds with probability at least --probability at confidence --confidence. The shipped examples/synthesize_control.pb.txt is a runnable double integrator held between 1 and 9 metres; examples/synthesize_then_monitor.cc is the same loop as a standalone program, synthesizing offline and then checking the plan with the engine the monitor runs online. Backend selection and the smooth semantics behind all of this are covered in synthesis backends.

Errors

The module fails loudly at startup and stays quiet at runtime. Init() returns false, and the component does not start, on a config that does not parse, on a FieldResolutionError from a path or builtin that does not resolve against its message type, or on a std::invalid_argument from a bad control_outputs entry, so a misconfiguration surfaces at cyber_launch rather than on the road. At runtime the monitor catches an evaluation exception, logs it, and skips the tick; the process never crashes on input. A control tick that throws logs the failure, and the launch file's respawn brings the process back. The offline tools exit 1 on an engine error and sentil_synthesizer exits 2 on a usage error: a missing config, an unknown --op, or witness without bounds.

Components and runtime files

The three components, their shells, and their cadence:

ComponentBaseDAGCadence
SentilMonitorComponentcyber::Component<PerceptionObstacles, LocalizationEstimate, Chassis>monitor/dag/sentil_monitor.dagEach perception frame
SentilTimedMonitorComponentcyber::TimerComponentmonitor/dag/sentil_timed_monitor.dagTimer, 100 ms as shipped
SentilControlComponentcyber::TimerComponentcontrol/dag/sentil_control.dagTimer, 10 ms as shipped, kept equal to control_period_ms
FileRole
monitor/dag/sentil_monitor.dagFused topology: perception trigger (qos depth 10, pending queue 10), localization and chassis co-channels
monitor/dag/sentil_timed_monitor.dagTimer topology, interval: 100
control/dag/sentil_control.dagTimer topology, interval: 10
monitor/launch/sentil_monitor.launchModule sentil_safety_monitor, process sentil_monitor
control/launch/sentil_control.launchModule sentil_control, process sentil_control, exception_handler: respawn
cyberfile.xmlPackage manifest: name sentil, version 0.3.0, type module, depends on cyber and common-msgs

Channels

ChannelDirectionMessageNote
/apollo/perception/obstaclesRead (monitor)apollo.perception.PerceptionObstaclesTrigger of the fused monitor
/apollo/localization/poseRead (monitor, control)apollo.localization.LocalizationEstimateHardcoded reader in the control component
/apollo/canbus/chassisRead (monitor, control)apollo.canbus.ChassisHardcoded reader in the control component
/apollo/sentil/statusPublished (monitor)SentilStatusDefault of output_channel
/apollo/control/nominalRead (control, SHIELD)apollo.control.ControlCommandDefault of nominal_channel
/apollo/controlPublished (control)apollo.control.ControlCommandDefault of output_channel
/apollo/sentil/control_advicePublished (control, ADVISORY)apollo.control.ControlCommandFixed; overrides output_channel in ADVISORY

Monitor config (SentilConfig)

The monitor reads a SentilConfig in protobuf text (proto/sentil_config.proto), default monitor/conf/sentil_monitor.pb.txt. The read-by column is honest: two fields in the schema have no consumer anywhere in the module.

FieldTypeRead byMeaning
formulasrepeated FormulaMonitor and analyzerThe specifications to check
input_channelsrepeated ChannelMappingMonitor and analyzerThe channel-to-variable mapping
output_channelstringMonitorThe status channel, default /apollo/sentil/status
semanticsTimeSemanticsAnalyzer onlyDENSE (default) or DISCRETE, picking dense or discrete robustness on replay
interpolationInterpolationModeNothingLINEAR (default), CONSTANT, or SPLINE; accepted and unread today
backendExecutionBackendNothingCPU (default) or GPU; accepted and unread today
algorithmProbAlgoAnalyzer onlyDETERMINISTIC (default), SMC, SPRT, or AMS (rejected with an error)
sample_budgetuint64Monitor and analyzerThe SMC ensemble size, default 1000; also caps SPRT samples on replay
confidencedoubleMonitor and analyzerThe interval level, default 0.95
sprt_configSprtConfigAnalyzer onlyThe SPRT hypotheses and error rates
lifting_registryrepeated LiftingEntryMonitor and analyzerA noise model and interaction per variable

The nested messages:

MessageFields
Formulaid (uint64), expression (string)
ChannelMappingchannel, message_type (fully qualified, e.g. apollo.canbus.Chassis), fields
FieldMappingvariable, then one of field_path or builtin
LiftingEntryvariable, noise, interaction (a NoiseInteraction: ADDITIVE default, or MULTIPLICATIVE)
SprtConfigp0 (default 0.90), p1 (0.95), alpha (0.05), beta (0.05)

The NoiseModel oneof carries eight families:

CaseParameters and defaults
diracA single double, the deterministic value
gaussianmean (0.0), std_dev (1.0)
uniformlow (0.0), high (1.0)
log_normalmu (0.0), sigma (1.0)
exponentialrate (1.0)
gammashape (1.0), scale (1.0)
betaalpha (1.0), beta (1.0)
truncated_normalmean (0.0), std_dev (1.0), lower, upper

Status message (SentilStatus)

Published on output_channel, defined in proto/sentil_status.proto.

MessageFieldMeaning
SentilStatusheaderApollo header, stamped with the evaluation time
resultsOne FormulaResult per formula
computation_time_msThe evaluation time for this tick
all_satisfiedTrue when every formula holds
FormulaResultid, expressionThe formula
robustnessA Robustness: is_concrete, min, max, equal once concrete
satisfiedWhether the formula holds
severityA Severity: OK satisfied, WARN unresolved, ERROR violated; FATAL is in the enum and never emitted
prob_resultA ProbabilisticResult, present for a P formula
ProbabilisticResultsamplesThe ensemble size, equal to sample_budget
satisfactionsThe estimate rounded onto the ensemble
probabilityThe running estimate
intervalA ConfidenceInterval: lower, upper, confidence_level

Control config (SentilControlConfig)

The control component reads a SentilControlConfig (proto/sentil_control_config.proto), default control/conf/sentil_control.pb.txt.

FieldTypeMeaning
modeControlModeSHIELD (default), SYNTHESIZE, or ADVISORY
specSpecThe specification; unused by SHIELD
modelLinearModelThe plant; unused by SHIELD
input_widthuint32The control input dimension, default 1
control_period_msdoubleThe tick period, default 10.0
deadline_fractiondoubleThe fraction of the period to plan within, default 0.8
state_inputsrepeated ChannelMappingWhat to extract from localization and chassis; the channel value is unused
boundsBoundsPer-input lower and upper; optional, omitted means unbounded
nominal_channelstringThe SHIELD input, default /apollo/control/nominal
output_channelstringThe command output, default /apollo/control
control_outputsrepeated ControlOutputInput index to a top-level ControlCommand field
smoothSmoothConfigThe soft min and max temperature for planning; unused by SHIELD
MessageFields
LinearModela (row-major n by n), b (row-major n by input_width), x0, variables, dt (default 0.1), horizon (default 20)
Specexpression, or a premade specification name with an optional variant
Boundslower, upper
ControlOutputindex (must be below input_width), field_path (a top-level float or double ControlCommand field)
SmoothConfigtemperature (default 10.0)

Bazel targets

Everything the module builds. The components and libraries are public, so an extension component can depend on them.

TargetKindWhat it is
//modules/sentil/monitor:libsentil_monitor_component.socomponentThe fused monitor
//modules/sentil/monitor:libsentil_timed_monitor_component.socomponentThe timer monitor
//modules/sentil/monitor:monitor_enginelibraryThe shared monitoring engine behind both shells
//modules/sentil/control:libsentil_control_component.socomponentThe control component
//modules/sentil/common:engine_configlibraryThe proto-to-engine bridge
//modules/sentil/common:field_extractorlibraryField path and builtin resolution
//modules/sentil/proto:sentil_config_protoprotoThe monitor config
//modules/sentil/proto:sentil_status_protoprotoThe status message
//modules/sentil/proto:sentil_control_config_protoprotoThe control config
//modules/sentil/tools:sentil_record_analyzerbinaryOffline record replay
//modules/sentil/tools:sentil_synthesizerbinaryOffline plan, witness, and chance check
//modules/sentil/examples:libsafety_planner_subscriber.socomponentThe verdict-subscriber example
//modules/sentil/examples:synthesize_then_monitorbinaryThe standalone synthesize-then-check example
//modules/sentil/tests:field_extractor_testtestExtraction and builtins, no Cyber RT needed
//modules/sentil/tests:oracle_parity_testtestThe deterministic oracle through the linked core
//modules/sentil/tests:synthesizer_testtestThe synthesis bridge
//modules/sentil/tests:symbol_referencetestNames every engine symbol so a header drift breaks the build
@sentil_cpp//:sentil_cppexternalThe staged core: include/sentil/*.hpp, include/sentil.h, lib/libsentil.so
bzl/sentil_cpp.BUILDbuild fileThe BUILD file for the @sentil_cpp repository

Offline tool flags

sentil_record_analyzer takes two flags:

FlagMeaning
--configThe SentilConfig text proto
--recordThe cyber .record file to analyze

sentil_synthesizer takes five; the last three apply to --op=chance:

FlagDefaultMeaning
--configThe SentilControlConfig text proto
--opplanplan, witness, or chance
--probability0.95The target satisfaction probability
--confidence0.95The confidence level of the bound
--process_std0.0The Gaussian process-noise standard deviation

The C++ bridge

The engine bridge and the extractor are ordinary Bazel-public libraries, so a custom component can link them and reuse the module's plumbing. The headers are monitor/monitor_engine.h, common/field_extractor.h, and common/engine_config.h.

NameSignatureOne line
MonitorEngine::Buildvoid Build(const SentilConfig&)Build the monitors and extractor; a bad config throws here
MonitorEngine::Evaluatebool Evaluate(const PerceptionObstacles&, const LocalizationEstimate&, const Chassis&, SentilStatus*)Fold one message set; false means skip the tick, nothing written
FieldExtractor::add_channelvoid add_channel(const std::string& message_type, const std::vector<FieldMapping>&)Resolve a channel's fields; throws on a bad type or path
FieldExtractor::extract_intovoid extract_into(const std::string& message_type, const Message&, std::vector<std::string>*, std::vector<double>*) constAppend the channel's variables and values
ResolvedFieldResolvedField(const Descriptor*, const FieldMapping&)One resolved path or builtin; throws FieldResolutionError
ResolvedField::variableconst std::string& variable() constThe variable this field feeds
ResolvedField::extractdouble extract(const Message&) constRead the scalar, no string parsing on the hot path
FieldResolutionError: public std::runtime_errorThe startup fail-loud type for unresolvable paths
Builtinenum classkNearestObstacleDistance, kMinTimeToCollision, kFrontGap
noise_from_proto::sentil::NoiseModel noise_from_proto(const NoiseModel&)One noise model; throws on an empty oneof
lifting_from_proto::sentil::LiftingRegistry lifting_from_proto(const SentilConfig&)The whole lifting registry
smc_from_proto::sentil::SmcConfig smc_from_proto(const SentilConfig&)Sample budget and confidence, engine defaults elsewhere
bounds_from_proto::sentil::Bounds bounds_from_proto(const Bounds&)Per-input bounds; throws on mismatched lengths
model_from_proto::sentil::SystemModel model_from_proto(const LinearModel&, std::size_t input_width)The linear plant; throws on bad matrix shapes
formula_from_spec::sentil::Formula formula_from_spec(const Spec&)A raw expression or a named library specification
tile_bounds::sentil::Bounds tile_bounds(const Bounds&, std::size_t input_width, std::size_t horizon)Per-step bounds tiled across the horizon
chance_system_from_model::sentil::StochasticSystem chance_system_from_model(const LinearModel&, double process_std)The perturbed autonomous system a chance check samples

The types under these signatures are the module's protos and the engine surface from sentil.hpp, documented on the C++ page.

Shipped examples

FileWhat it shows
examples/ego_speed_monitor.pb.txtThe config-only hello world, a speed limit off the chassis
examples/follow_distance_prstl.pb.txtThe PrSTL follow distance through FRONT_GAP
examples/synthesize_control.pb.txtA runnable SYNTHESIZE config, the double integrator
examples/cbf_shield_control.pb.txtThe recommended SHIELD deployment
examples/safety_planner_subscriber.ccActing on the verdict: fallback on all_satisfied false or a low interval
examples/synthesize_then_monitor.ccSynthesize offline, then check the plan with the same engine, no Cyber RT
Edit this page on GitHub