Adding a new Model
In order to simulate a system or use advanced state estimators/controllers, a mathematical description of the underlying model is needed. In the CRS framework, the dynamic_model packages provide abstract classes which encapsulate a model description.
For a custom model to be used with the CRS framework it must inherit either from the ContinuousDynamicModel or the DiscreteDynamicModel class.
Creating a new Discrete Model¶
Creating a new discrete model usually requires three main steps:
- Creation of a custom input, state and parameter struct
- Creation of the continuous model by inheriting from the Continuous Dynamic Model
- Creation of a discrete model by discretizing the continuous model using e.g. a rk4 integration.
The following step-by-step guide shows in depth how to create a new model:
Creating a new Dynamic Model Package¶
- Copy the package
custom_model_templatein thecrs/dynamic_modelpackage. - Change the state and input structs:
custom_model_template/include/custom_model_template/custom_input.h
Update the struct to match your input. Also, overwrite all provided operators, if possible.custom_model_template/include/custom_model_template/custom_state.h
Update the struct to match your state. Also, overwrite all provided operators, if possible.custom_model_template/include/custom_model_template/custom_params.h
Define all parameters needed for your model here (e.g. mass, friction coefficient, ...). Also, overwrite the yaml parsing.
-
Define your Dynamics in the
custom_model_template/include/custom_model_template/custom_model_continuous.handcustom_model_template/src/custom_model_continuous.cppSections which need changes are highlighted with
// ==================== TODO- Update all references to state dimensions <6> and input dimensions <2> with your dimension.
- Define your input variables with a human readable name in the constructor
custom_model_continuous.h)
ContinuousCustomModel(custom_params params) : params(params) { // <================= TODO define your model inputs and state in with variable names here =================> + std::vector<casadi::MX> state_mx = {casadi::MX::sym("x"), casadi::MX::sym("y"), + casadi::MX::sym("yaw"), casadi::MX::sym("v_x"), casadi::MX::sym("v_y"), casadi::MX::sym("yaw_rate")}; + std::vector<casadi::MX> input_mx = {casadi::MX::sym("torque"), casadi::MX::sym("steer")}; // <================= END REQUIRED CHANGES =================> std::vector<casadi::MX> state_dot_mx = getContinuousDynamics(state_mx, input_mx); // append control inputs to state vector (casadi functions only take one input) for (const auto input : input_mx) state_mx.push_back(input); f_ = casadi::Function("f_dot", state_mx, state_dot_mx); } // Constructor- Provide the mathematical description in
getContinuousDyanmics()ofcustom_model_template/src/custom_model_continuous.cpp
std::vector<casadi::MX> ContinuousCustomModel::getContinuousDynamics(const std::vector<casadi::MX> state, const std::vector<casadi::MX> control_input) { // ========================= TODO IMPLEMENT YOUR DYNAMICS HERE ========================= using namespace casadi; // Just for better readability, function inputs (states and control inputs) + auto x = state[0]; + auto y = state[1]; + auto yaw = state[2]; + auto v = state[3]; + auto torque = control_input[0]; + auto steer = control_input[1]; // Output of f(state, control) + auto x_dot = v * cos(yaw); + auto y_dot = v * sin(yaw); + auto yaw_dot = v * sin(beta) / params.lr; + auto v_dot = = -1 / params.tau * v + 1 / params.tau * torque; + auto state_dot = {x_dot, y_dot, yaw_dot, v_dot}; return state_dot; // ========================= END TODO ========================= } -
Wrap the continuous dynamics into a discrete dynamics wrapper. To do this, update
custom_model_template/include/custom_model_template/custom_model_discrete.h- Update all references to state dimensions <6> and input dimensions <2> with your dimension.
- Define your input variables with a human readable name in the constructor. Make sure they match the definition provided in the continuous implementation, but also include a 'Ts' input which is used to store the integration time.
// ==================== TODO define your symbols here ==================== + std::vector<casadi::MX> state_mx = {casadi::MX::sym("x"), casadi::MX::sym("y"), + casadi::MX::sym("yaw"), casadi::MX::sym("v_x"), casadi::MX::sym("v_y"), casadi::MX::sym("yaw_rate")}; + std::vector<casadi::MX> input_mx = {casadi::MX::sym("torque"), casadi::MX::sym("steer"), casadi::MX::sym("Ts")}; // ==================== END TODO ====================- Update the argument mapping in
applyModel(). Make sure that the order of the variables matches the order of the states as defined in (2.)
// ==================== TODO update argument mapping according to your struct definition ==================== casadi::Function::MapArg arg = { + {"x0", {state.pos_x, state.pos_y, state.yaw, state.vel_x, state.vel_y, state.yaw_rate}}, + {"p", {control_input.torque, control_input.steer, timestep}}}; custom_state output_state; // ==================== END TODO ====================
Done, you can build your your package be executing catkin build custom_model_template
Files to change/ update if a new Model was implemented¶
CRS files that need to be changed for a new model
Estimator files
| Files | Todo | Example file |
|---|---|---|
| Estimator config | Create new estimator config | ekf_params.yaml |
Sensor Models
| Files | Todo | Example file |
|---|---|---|
| Sensor model include | Create new sensor model | pacejka_sensor_model/imu_sensor_model.h |
| Sensor model src | Create new sensor model | pacejka_sensor_model/imu_sensor_model.cpp |
| Sensor model config | Create new sensor model config | sensor_params.yaml |
Controller files
| Files | Todo | Example file |
|---|---|---|
| Controller include | Create new controller | controls/pid_controller/include |
| Controller src | Create new controller | controls/pid_controller/src |
If a preexisting controller wants to be used with a new model: the .cpp and .h files need to be updated.
ROS files that need to be changed for a new model
Estimator files
| Files | Todo | Example file |
|---|---|---|
| Estimator config | Create new estimator config | ekf_params.yaml |
| Estimator include | Update state and input types | ros_estimators/kalman_estimator/pacejka_discrete_ekf.h |
| Estimator src | Update state and input type and dimensions | ros_estimators/kalman_estimator/pacejka_discrete_ekf.cpp |
Controller files
| Files | Todo | Example file |
|---|---|---|
| Controller config | Create new controller config | ros_controllers/pid_controller.yaml |
Simulator files
| Files | Todo | Example file |
|---|---|---|
| Simulator config | Create new simulator config | pacejka_car_simulator.yaml |
| Simulator src | Create new simulator src | ros_simulator/ros_pacejka_simulator.cpp |
| Simulator include | Create new simulator include | ros_simulator/ros_pacejka_simulator.h |