CRISP

Using CRISP

Include <crisp/crisp.hpp> and link against crisp::crisp. The CRISP package headers contain the full declarations.

cmake_minimum_required(VERSION 3.20)
project(my_simulation LANGUAGES CXX)
find_package(crisp CONFIG REQUIRED)
add_executable(my_simulation main.cpp)
target_compile_features(my_simulation PRIVATE cxx_std_20)
target_link_libraries(my_simulation PRIVATE crisp::crisp)

Configure with crisp_DIR pointing to the package's lib/cmake/crisp directory and Eigen 3.4.0 available to CMake.

Build and step a model

This program simulates a falling sphere without opening a window. Dimensions are in metres, masses in kilograms, and time in seconds.

#include <crisp/crisp.hpp>
#include <cstdio>

int main() {
  auto builder = crisp::make_model_builder();
  builder->opt().dt = 0.005;
  builder->opt().sol.type = crisp::solver_e::canal;

  auto& body = builder->addBody(
    {.name = "sphere",
     .pos = {0.0, 0.0, 0.2},
     .mass = 0.1,
     .inertia = {0.000016, 0.000016, 0.000016, 0, 0, 0}},
    false);
  body.addGeom({.type = crisp::geometry_e::sphere,
                .size = crisp::make_sphere_size(0.02)});

  auto model = builder->build();
  auto data = crisp::make_data(*model);
  crisp::reset(*model, *data);
  for (int i = 0; i < 1000; ++i) {
    crisp::step(*model, *data);
  }
  std::printf("Time: %.3f s; contacts: %d\n",
              data->sim.time, data->size.ncon);
  return data->state.q().allFinite() ? 0 : 1;
}

addBody(..., false) makes the body free to move. build() produces the model, and make_data() allocates its working state. Keep the model alive while using its data.

Open the viewer

To display a built model instead of stepping it yourself:

// model is the handle returned by builder->build().
auto app = crisp::make_app(std::move(model));
app->init("My simulation", 1280, 800);
while (app->isOpen()) {
  app->render();
}

make_app takes ownership of the model. The application manages simulation stepping; render() updates the viewer. Set control callbacks before init().

Control a model

For direct stepping, write actuator inputs through data.act.u() and data.act.udot() before calling step. A feedback controller that needs refreshed kinematics can run between step1 and step2; see the API reference for their order.

With AppManager, install a callback before init():

app->setControl([](auto const& model, auto const& data, auto u, auto udot) {
  // Fill u and udot for this model's actuators.
});

The lambda shows the callback signature. Position actuators take position and velocity targets; force actuators take generalized forces. Size and order the inputs to match the model. See Actuation for the input conventions.

See Examples for time-dependent controllers. Captured objects must remain alive while the application can call the controller.

Inspect and visualize

After a direct step, read data.state.q(), data.state.v(), and data.con.feature to inspect the computed state and contacts. In a running application, acquire app->lock() before accessing its live model or data outside the control callback.

The viewer can also display contact points, contact vectors, colliders, and bounding boxes. The Scene interface supports applications that manage their own graphics context.

See API reference for operation signatures and ownership rules.