User Tools

Site Tools


getting_started:tutorials:mockrobot

Mock Robot Example

This tutorial demonstrates an EEROS application complete with a control system, a safety system, and a sequencer. You can find the code in the directory with examples. Navigate to examples/mockRobotExample.

The Full Picture

This example does not need any hardware. The moving of a robot is pretended by using integrators summing up. This represents the position on an axis. Before a robot can be moved it has usually to be homed. Therefore, this example comprises of two sequences, Homing and MovingUpDown.

The main application defines a control system and a safety system. The safety system task is assigned as main task and passed to the executor. Further, two sequences are defined and added to the sequencer. Please note, that none of them are yet started. After this the executor starts running.

int main(int argc, char **argv) {
  ...
  double period = 0.01;
  MockRobotControlSystem controlSystem(period);
  MockRobotSafetyProperties ssProperties(controlSystem, period);
  SafetySystem safetySys(ssProperties, period);
 
  auto& executor = Executor::instance();
  executor.setMainTask(safetySys);
 
  auto& sequencer = Sequencer::instance();
  HomingSequence homingSeq("Homing Sequence", sequencer, controlSystem);
  sequencer.addSequence(homingSeq);
  UpAndDownSequence upDownSeq("UpAndDown Sequence", sequencer, controlSystem);
  sequencer.addSequence(upDownSeq);
 
  ...
  executor.run();
  sequencer.wait();
}

The executor only stops after the system is stopped by pressing Ctrl-C. For this purpose a signal handler has to be defined which itself stops the executor and the sequencer.

void signalHandler(int signum) {
  SafetySystem::exitHandler();
  Sequencer::instance().abort();;
}

The main program must wait for the sequencer to stop until it can finish, in order to terminate gracefully without any running thread.

Control System

The control system is very simple. Each of the two axis x and y, it has a constant block acting as a set point and an integrator block pretending the position to move.
In the constructor of the control system the blocks are created, connected, and the integrator are enabled. All of the blocks are added to a time domain which itself is passed to the executor. This time domain runs in parallel to the safety system task.

Safety System

The safety system has four levels and a couple of safety events to switch between these levels.

Safety system with levels and events.

The event abort can be triggered in all levels except slOff. The safety system starts in the level slHoming. This level defines a level action as follows:

  slHoming.setLevelAction([&](SafetyContext* privateContext) {
    if (slHoming.getNofActivations() == 1)
      Sequencer::instance().getSequenceByName("Homing Sequence")->start();
    if (cs.iX.getOut().getSignal().getValue() >= 1.0) cs.setpointX.setValue(0);
    if (cs.iY.getOut().getSignal().getValue() >= 1.0) cs.setpointY.setValue(0);
    if (Sequencer::instance().getSequenceByName("Homing Sequence")->getRunningState() == SequenceState::terminated)
      privateContext->triggerEvent(homingDone);
  });

As a first step the sequence “Homing Sequence”, which was registered in the sequencer at startup, is started. This sequence moves the robot slowly to its limit, see next chapter. As soon as one of the axis reaches this limit, the movement stops. After the “Homing Sequence” has terminated a safety event is triggered which causes a switch to the next safety level.
This level does nothing except wait for 2 seconds until it switches to the level slMoving.

  slReady.setLevelAction([=](SafetyContext* privateContext) {
    if (slReady.getNofActivations() * ts >= 2)
      privateContext->triggerEvent(startMoving);
    });
  });

This level simply starts the “UpAndDown Sequence”.

  slMoving.setLevelAction([&](SafetyContext* privateContext) {
    if (slMoving.getNofActivations() == 1)
      Sequencer::instance().getSequenceByName("UpAndDown Sequence")->start();
  });

Sequencer

The main programm registered two sequences. The first of the two is responsible for homing. It is defined as nonblocking, which makes it run in parallel to the main program and notably to the safety system which calls the start method of this sequence.

class HomingSequence : public Sequence {
public:
  HomingSequence(std::string name, Sequencer& seq, MockRobotControlSystem& cs) : Sequence(name, seq), cs(cs) {  }
 
  int action() {
    cs.setpointX.setValue(0.1);
    cs.setpointY.setValue(0.1);
  }
  bool checkExitCondition() {return cs.iX.getOut().getSignal().getValue() >= 1.0 && cs.iY.getOut().getSignal().getValue() >= 1.0;}
private:
  MockRobotControlSystem& cs;
};

What does the sequence do? It's action method simply adjusts the set points of the two axis to a small positive value. That causes the position of the robot (represented by the two integrators) to slowly move upwards. The sequence, which in this case is a simple step, terminates as soon as its exit condition is met. That is when both axis reach the position of 1.0.

The second sequence “UpAndDown Sequence” is as follows:

class MoveUp : public Step {
public:
  MoveUp(std::string name, Sequencer& sequencer, BaseSequence* caller, MockRobotControlSystem& cs) : Step(name, sequencer, caller), cs(cs) { }
  int action() {
    cs.setpointX.setValue(0.7);
    cs.setpointY.setValue(0.3);
  }
  bool checkExitCondition() {return cs.iX.getOut().getSignal().getValue() >= 5.0;}
private:
  MockRobotControlSystem& cs;
};
 
class MoveDown : public Step {
public:
  MoveDown(std::string name, Sequencer& sequencer, BaseSequence* caller, MockRobotControlSystem& cs) : Step(name, sequencer, caller), cs(cs) { }
  int action() {
    cs.setpointX.setValue(-0.6);
    cs.setpointY.setValue(-0.3);
  }
  bool checkExitCondition() {return cs.iX.getOut().getSignal().getValue() <= -5.0;}
private:
  MockRobotControlSystem& cs;
};
 
class UpAndDownSequence : public Sequence {
public:
  UpAndDownSequence(std::string name, Sequencer& seq, MockRobotControlSystem& cs) : Sequence(name, seq), cs(cs), moveUp("move up", seq, this, cs), moveDown("move down", seq, this, cs) { }
 
  int action() {
    while (Sequencer::running) {
      moveUp();
      moveDown();
    }
  }
private:
  MockRobotControlSystem& cs;
  MoveUp moveUp;
  MoveDown moveDown;
};

Its action method is an endless loop running the steps “move up” and “move down” repetitively. Here again, the sequence is nonblocking while the single steps block when called. Both steps simply set the set point of the two integrators which causes them to run up- or downwards. Both steps stop when their exit condition is met. The sequence itself needs no exit condition because of its endless loop.

Neither of the two sequences has an associated monitor. In reality this could be very useful to check for a blocked robot arm for instance.

getting_started/tutorials/mockrobot.txt · Last modified: 2018/08/07 12:28 (external edit)