- 3.0.2 Documentation
Basic Tutorials

Creating a ROS package

CT is middleware free. Therefore, you are not bound to use ROS or catkin. However, for the sake of simplicity we will use catkin in this tutorial. In case you need a refresher on catkin, please go through the tutorials here. Make sure you have succesfully downloaded and compiled CT according to our How to use the Control Toolbox instructions. Afterwards go to your catkin workspace and create a new package

cd ~/catkin_ws/src
catkin_create_pkg my_ct_project roscpp ct_core ct_rbd ct_optcon ct_models

In this example, we made my_ct_project dependent on all CT packages. This is not strictly necessary, however there is no harm to it either.

Creating our first executable

First, we are going to simulate the dynamics of a damped oscillator. Let's create our main file

#include <ct/core/core.h>
int main(int argc, char** argv)
{
// a damped oscillator has two states, position and velocity
const size_t state_dim = ct::core::SecondOrderSystem::STATE_DIM; // = 2
// create a state
// we initialize it at a point with unit deflection and zero velocity
x(0) = 1.0;
x(1) = 0.0;
// create an oscillator, which is a predefined system in ct_core
double w_n = 10;
std::shared_ptr<ct::core::SecondOrderSystem> oscillator(new ct::core::SecondOrderSystem(w_n));
// create an integrator
ct::core::Integrator<state_dim> integrator(oscillator);
// simulate 1000 steps
double dt = 0.001;
ct::core::Time t0 = 0.0;
size_t nSteps = 1000;
integrator.integrate_n_steps(x, t0, nSteps, dt);
// print the new state
std::cout << "state after integration: " << x.transpose() << std::endl;
return 0;
}

So what happens in this code? We first include ct/core.h. This includes all relevant headers from CT Core. Other CT modules have similar header files such as ct/rbd.h . If you were to include ct/rbd.h you would not have to include ct/core.h anymore. It is automatically included.

Then, we create a state vector and set it to zero. Afterwards, we can create a shared pointer to a ct::core::SecondOrderSystem which is a damped oscillator. It is derived from type ct::core::System, which means we can directly plug it into a ct::core::Integrator. Here, we have many choices such as ct::core::IntegratorEuler or ct::core::ODE45. Here, we chose a fourth-order Runge-Kutta integrator. We then simulate (integrate) it forward for 1 second with a time step of 0.001 seconds. Finally, we print the new state.

Creating our first system

In the example above, we have been using an oscillator that is provided with CT. However, we might want to model our own system. Here, we will model a simple mass point subject to friction. However, if you are interested in more sophisticated models, especially Rigid Body Dynamics, make sure you check out the tutorials in ct_rbd as well.

First we create our system within a header Masspoint.h

#pragma once
#include <ct/core/core.h> // as usual, include CT
// create a class that derives from ct::core::System
class Masspoint : public ct::core::System<2>
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
static const size_t STATE_DIM = 2;
// constructor
Masspoint(double mass, double d) : mass_(mass), d_(d) {}
// copy constructor
Masspoint(const Masspoint& other) : mass_(other.mass_), d_(other.d_) {}
// destructor
~Masspoint() = default;
// clone method for deep copying
Masspoint* clone() const override
{
return new Masspoint(*this); // calls copy constructor
}
// The system dynamics. We override this method which gets called by e.g. the Integrator
const ct::core::Time& t,
ct::core::StateVector<STATE_DIM>& derivative) override
{
// first part of state derivative is the velocity
derivative(0) = x(1);
// second part is the acceleration which is caused by damper forces
derivative(1) = -d_ / mass_ * x(1);
}
private:
double mass_;
double d_;
};

As before, we can now integrate this system forward

#include <ct/core/core.h>
int main(int argc, char** argv)
{
// a damped oscillator has two states, position and velocity
const size_t state_dim = Masspoint::STATE_DIM; // = 2
// create a state
// we initialize it at 0
x.setZero();
// create our mass point instance
double mass = 1.0;
double d = 0.01;
std::shared_ptr<Masspoint> masspoint(new Masspoint(mass, d));
// create an integrator
ct::core::Integrator<state_dim> integrator(masspoint);
// simulate 1000 steps
double dt = 0.001;
ct::core::Time t0 = 0.0;
size_t nSteps = 1000;
integrator.integrate_n_steps(x, t0, nSteps, dt);
// print the new state
std::cout << "state after integration: " << x.transpose() << std::endl;
return 0;
}