Skip to content

Contents

Introduction

With the system dynamics and sensors modeled, and all messages defined we can begin setting up the ROS architecture. While for long-term development it is strongly recommended to use the already defined ros_simulator package within ros4crs, this can be a daunting challengs for users new to ROS. In this tutorial we will therefore start by defining our own barebones ROS simulator which calls our dynamical model to advance the state and prints the result to the terminal.

Step-by-Step

Setup Folder Structure

Let's take a look at crs/src/ros4crs/ros_simulator as a template

ros_simulator
├── app
   └── simulation_node.cpp
├── CMakeLists.txt
├── config
   ├── kinematic_car_simulator.yaml
   └── pacejka_car_simulator.yaml
├── include
   └── ros_simulator
       ├── common
          ├── noise_model.h
          └── ros_simulator.h
       ├── component_registry.h
       ├── delayed_publisher.h
       ├── gaussian_noise_model.h
       ├── ros_kinematic_simulator.h
       └── ros_pacejka_simulator.h
├── launch
   ├── default.launch
   └── default_standalone.launch
├── package.xml
├── src
   ├── component_registry.cpp
   ├── ros_kinematic_simulator.cpp
   └── ros_pacejka_simulator.cpp
└── test
    ├── config
       └── test_pacejka_imu_vicon.yaml
    ├── ros_simulator_test.cpp
    └── ros_simulator_test.launch

This obviously contains quite a few files, not all of which are strictly necessary to get a minimal ROS simulator up and running. We can either duplicate the ros_simulator/ directory and trim it down, or create a new one and generate the appropriate files as we go along. In the end we should have a folder-tree which is equivalent to the following:

parafoil_simulator
├── app
   └── simulation_node.cpp
├── CMakeLists.txt
├── include
├── launch
   └── parafoil_3dof_launch.launch
├── package.xml
└── src

containing the app, which is what ROS will actually execute, a launch file, which tells ROS what to execute, and the administrative CMakeLists and package files. Note that we have left the include/ folder even though it contains no files, this is because this will be the home of our header files.

Simulator Node

(Re)write app/simulation_node.cpp as minimum working example

1. Includes requisite files from model
2. Instantiates `params`, `state`, and `control_input` structs
3. Prints state as sanity check
4. Creates `parafoil_3dof_model` class instance pointer
5. Propagates model forward once
6. returns 0 if successful
#include <ros/ros.h>
#include <casadi/casadi.hpp>

#include <iostream>

#include <parafoil_3dof_model/parafoil_3dof_model_discrete.h>
#include <parafoil_3dof_model/parafoil_3dof_params.h>
#include <parafoil_3dof_model/parafoil_3dof_state.h>
#include <parafoil_3dof_model/parafoil_3dof_input.h>

int main(int argc, char** argv)
{

crs_models::parafoil_3dof_model::parafoil_3dof_params params = {-0.5, -0.25};
crs_models::parafoil_3dof_model::parafoil_3dof_state state = {1.0, 1.0};
crs_models::parafoil_3dof_model::parafoil_3dof_input control_input = {0.0, 0.0};

std::cout << state << std::endl;

std::unique_ptr<crs_models::parafoil_3dof_model::DiscreteFeedforwardModel> model;
model = std::make_unique<crs_models::parafoil_3dof_model::DiscreteFeedforwardModel>(params);

state = model->applyModel(state, control_input, 1.0);

std::cout << state << std::endl;

return 0;
}

Note that unlike in previous tutorials we are not using a 4dof model but rather a 3dof model, this makes no difference besides the dimensions of the parameters, states, inputs. The structure remains the same.

Launch File

Write launch file

  1. sets namespace
  2. creates node from my package, of the simulation node
<launch> 

<group ns = "test_ns">

    <node pkg="parafoil_simulator" name="parafoil_simulator" type="simulation_node" output="screen">
    </node>

</group>

</launch>

Admin Files

Update package.xml

  1. change pkg name
  2. change author/maintainer details
  3. remove unnecessary dependencies
  4. add your model as depend
<?xml version="1.0"?>
<package format="2">
<name>parafoil_simulator</name>
<version>0.0.1</version>
<description>TODO </description>

<author email="norrisg@ethz.ch">Griffin Norris</author>
<maintainer email="norrisg@ethz.ch">Griffin Norris</maintainer>

<license>BSD 2-Clause </license>

<buildtool_depend>catkin</buildtool_depend>
<buildtool_depend>catkin_simple</buildtool_depend>
<depend>roscpp</depend>

<depend>dynamic_models</depend>

<!-- Optional Dependencies -->
<depend>parafoil_3dof_model</depend>

<export></export>
</package>

Update CMakeLists.txt

  1. Change project name to match package name
  2. Remove pacejka nd kinematic model library inclusions
  3. Remove tests
  4. Change app/simulation_node.cpp (if file name was changed)
  5. Change library linking to casadi (here explicitly), also remove stdc++fs weirdness
cmake_minimum_required(VERSION 2.8.3)
project(parafoil_simulator)

set(CMAKE_CXX_STANDARD 17)
find_package(catkin_simple REQUIRED)
find_package(casadi)
catkin_simple()
catkin_package()

#############
# Libraries #
#############
include_directories(
include
${catkin_INCLUDE_DIRS}
)

###############
# Executables #
###############
cs_add_executable(simulation_node
app/simulation_node.cpp
        )
target_link_libraries(simulation_node casadi)

##########
# Export #
##########

cs_install()
cs_export(INCLUDE_DIRS ${CATKIN_DEVEL_PREFIX}/include)

Test Simulator

  1. Open a terminal, start crs-docker
  2. Build

    crs build
    
  3. Source

    source devel/setup.bash
    
  4. Launch roslaunch <pkg name> <launch file>

    roslaunch parafoil_simulator parafoil_3dof_launch.launch
    
  5. Observe Output

    SUMMARY
    ========
    
    PARAMETERS
    * /rosdistro: noetic
    * /rosversion: 1.15.14
    
    NODES
    /test_ns/
        parafoil_simulator (parafoil_simulator/simulation_node)
    
    auto-starting new master
    process[master]: started with pid [4588]
    ROS_MASTER_URI=http://localhost:11311
    
    setting /run_id to 798e88ee-7878-11ed-9ac4-0242ac120002
    process[rosout-1]: started with pid [4598]
    started core service [/rosout]
    process[test_ns/parafoil_simulator-2]: started with pid [4601]
    parafoil_3dof_state:
    Pos_x: 0.000000
    Pos_y: 0.000000
    Pos_z: -100.000000
    Psi: 0.785398
    Psi_rate: 0.000000
    
    [WARNING] No Q Matrix specified for DiscreteParafoil3dofModel. Using identity Matrix!
    parafoil_3dof_state:
    Pos_x: 6.993287
    Pos_y: 6.993285
    Pos_z: -95.280000
    Psi: 0.785398
    Psi_rate: 0.000000
    
    [test_ns/parafoil_simulator-2] process has finished cleanly
    log file: /root/.ros/log/798e88ee-7878-11ed-9ac4-0242ac120002/test_ns-parafoil_simulator-2*.log
    

Up Next

Obviously there is not much we can do by simply printing our states to the terminal. In the next tutorial we will make use of those messages we defined by adding a publisher for your states