Now that we have defined our system dynamics model we can turn our attention to the sensor models, located in src/crs/sensor_models/. Again, we will use the already defined Pacejka sensor model as a template to save time and will use of the 4 DoF parafoil system as an example.
In general the sensor model
The parafoil system we are using as an example has 2 main sensors: a GPS and an IMU. The GPS measurements are simply the inertial position and velocity, both of which can be calculated directly from the current state
Duplicate the pacejka_sensor_model/ directory and rename, e.g. to parafoil_4dof_sensor_model/
pacejka_sensor_model
├── CMakeLists.txt
├── include
│ └── pacejka_sensor_model
│ ├── imu_sensor_model.h
│ └── mocap_sensor_model.h
├── package.xml
└── src
├── imu_sensor_model.cpp
└── mocap_sensor_model.cppUpdate header files
- Rename file from
mocap_sensor_model.he.g. togps_sensor_model.h - Update
ifdefs - Find & replace all
pacejka_withparafoil_4dof_ - Remove all
car_(missed by find & replace) - Rename class from
MocapSensorModele.g. toGPSSensorModel - Update measurement output size
Update source files
-
Rename file from
mocap_sensor_model.cppe.g. togps_sensor_model.cpp -
find & replacce all
pacejka_withparafoil_4dof_ -
remove all
car_(missed by find & replace) -
find & replace all
mocapwithGPS -
find & replace all
MocapSensorModelwith new class name e.g.GPSSensorModel -
Update constructor which defines
casadifunction containing dynamics. Shown below is the example from the parafoil gps sensor/** * @brief Construct a new GPS Sensor Model. * * @param R measurement covariance Matrix */ GPSSensorModel::GPSSensorModel(const Eigen::Matrix<double, 6, 6>& R) : SensorModel(6, GPSSensorModel::SENSOR_KEY) // Measurement dimension is six { std::vector<casadi::MX> state_mx = { casadi::MX::sym("x"), casadi::MX::sym("y"), casadi::MX::sym("z"), casadi::MX::sym("phi"), casadi::MX::sym("theta"), casadi::MX::sym("psi"), casadi::MX::sym("u"), casadi::MX::sym("v"), casadi::MX::sym("w") }; // x, y, z, v_x, v_y, v_z std::vector<casadi::MX> measured_states_mx = { state_mx[0], state_mx[1], state_mx[2], cos(state_mx[3])*1*state_mx[6] + (cos(state_mx[5])*0*sin(state_mx[3]) - sin(state_mx[5])*cos(state_mx[3]))*state_mx[7] + (cos(state_mx[5])*0*cos(state_mx[3]) + sin(state_mx[5])*sin(state_mx[3]))*state_mx[8], sin(state_mx[5])*1*state_mx[6] + (sin(state_mx[5])*0*sin(state_mx[3]) + cos(state_mx[5])*cos(state_mx[3]))*state_mx[7] + (sin(state_mx[5])*0*cos(state_mx[3]) - cos(state_mx[5])*sin(state_mx[3]))*state_mx[8], -0*state_mx[6] + 1*sin(state_mx[3])*state_mx[7] + 1*cos(state_mx[3])*state_mx[8] }; measurement_function = casadi::Function("applyMeasurementModel", state_mx, measured_states_mx); R_ = R; }
-
Update all matrix sizes to match sensor output and state dimensions
Update header files
- File already named
imu_sensor_model.h - Update
ifdefs - Find & replace all
pacejka_withparafoil_4dof_ - remove all
car_(missed by find & replace) - Class is already named
ImuSensorModel - Update measurement output size (if necessary)
Update source files
-
File already named
imu_sensor_model.cpp -
Find & replace all
pacejka_withparafoil_4dof_ -
Remove all
car_(missed by find & replace) -
Class is already named
ImuSensorModel -
Ensure correct dynamics model is loaded i.e.
ContinuousParafoil4dofModelinstead ofContinuousPacejkaModel -
Update constructor which defines
casadifunction containing dynamics. Shown below is the example from the parafoil gps sensor./** * @brief Construct a new IMU Sensor Model. Note that the accelerations are not part of the 4 DoF parafoil state and * therefore the continuous model is needed * * @param parafoil_4dof_cont the continuous model * @param R measurement covariance Matrix */ ImuSensorModel::ImuSensorModel(const std::shared_ptr<crs_models::parafoil_4dof_model::ContinuousParafoil4dofModel> parafoil_4dof_cont, const Eigen::Matrix<double, 6, 6>& R) : SensorModel(6, ImuSensorModel::SENSOR_KEY) // Measurement dimension is six { std::vector<casadi::MX> state_mx = { casadi::MX::sym("x"), casadi::MX::sym("y"), casadi::MX::sym("z"), casadi::MX::sym("phi"), casadi::MX::sym("theta"), casadi::MX::sym("psi"), casadi::MX::sym("u"), casadi::MX::sym("v"), casadi::MX::sym("w") }; std::vector<casadi::MX> input_mx = { casadi::MX::sym("delta_s"), casadi::MX::sym("delta_a") }; auto cont_dynamics = parafoil_4dof_cont->getContinuousDynamics(state_mx, input_mx); // p, q, r, // accel_1, accel_2, accel_3, std::vector<casadi::MX> measured_states_mx = { cont_dynamics[3], sin(state_mx[3])*cont_dynamics[5], cos(state_mx[3])*cont_dynamics[5], cont_dynamics[6], cont_dynamics[7] + 9.81*sin(state_mx[3]), cont_dynamics[8] + 9.81*cos(state_mx[3]) }; state_mx.insert(state_mx.end(), input_mx.begin(), input_mx.end()); // Append input at the end of state vector measurement_function = casadi::Function("applyMeasurementModel", state_mx, measured_states_mx); R_ = R; }
Note that the constructor instantiates a continuous model which is called to generate the state derivatives.
-
Update all matrix sizes to match sensor output and state dimensions
Update CmakeLists.txt
- Update project name
- Update
add_libraryblock to match names of sensor files
Update package.xml
- Update package name
- Update maintainer name & email (if necessary)
- Update dependencies to include dynamics model
Folder structure should then be equivalent to the following:
parafoil_4dof_sensor_model
├── CMakeLists.txt
├── include
│ └── parafoil_4dof_sensor_model
│ ├── gps_sensor_model.h
│ └── imu_sensor_model.h
├── package.xml
└── src
├── gps_sensor_model.cpp
└── imu_sensor_model.cppNow that we have both our system dynamics model and our sensor models we are ready to start building our system architecture in ROS. First we should define our message files before we forget.