Professional Documents
Culture Documents
This will create gSimulation.h and gSimulation.cpp in your project directory. These files already contain
some code and examples.
Open sources/cpp/projects/crash_course/gSimulation.h and locate the block where the ports are
defined.
//---------------------------------------------------------------------// Ports (These are the only variables that may be declared public)
//---------------------------------------------------------------------public:
tControllerInput<double> ci_signal_1;
This section defines the interface of your group to the outside.
Replace the example port with the ports we want to have for our group:
/*! Desired velocity (in m/s) */
tControllerInput<double> velocity;
/*! Desired angular velocity */
tControllerInput<double> angular_velocity;
/*! Position of our robot in the world coordinate system */
tSensorOutput<rrlib::math::tPose2D> pose;
/*! Simulated distance sensor values to the front and to the rear */
tSensorOutput<double> ir_distance_front, ir_distance_rear;
The type in the brackets is the data type that will be transferred via this port. This can be any C++ type
for which the stream operators for serialization have been overloaded (seeAdvanced/Suitable Port Data
Types).
Note, that we use the type tPose2D from the rrlib_math library. That's why the following include needs
to be added to the External includes section (in gSimulation.h):
//---------------------------------------------------------------------// External includes (system with <>, local with "")
//---------------------------------------------------------------------#include "rrlib/math/tPose2D.h"
Creating Modules
Main simulation module
Now, we will create the first module - for the main simulation. In the following, it is demonstrated how a
module is is created.
~/finroc$ finroc_create
Create a SenseControlModule named "MainSimulation" insources/cpp/projects/crash_course in more
or less the same way as we created the group. Comment: "This module simulates a differential-driven
robot and two distance sensors".
Note, that the generated source files already contain various comments on what kind of code belongs
where.
Below these ports, add two more ports and some parameters:
We need some internal variables for our calculations. They belong in the private area of the class
(before the private destructor):
}
else
{
robot_counter_internal++;
robot_counter.Publish(robot_counter_internal);
current_pose.Reset();
current_speed = 0;
FINROC_LOG_PRINTF(ERROR, "Robot crashed at a speed of %f m/s and was destroyed.
Respawning. Destroyed Robots: %d.", avg_speed, robot_counter_internal);
}
}
FINROC_LOG_PRINT(DEBUG_VERBOSE_1, "New robot position: ", current_pose);
// Calculate sensor values
tPose2D robot_in_2m = current_pose.Translated(tVec2d(2,
0).Rotated(current_pose.Yaw()));
tPose2D robot_2m_back = current_pose.Translated(tVec2d(-2,
0).Rotated(current_pose.Yaw()));
double front_distance = max_ir_sensor_distance.Get();
double rear_distance = max_ir_sensor_distance.Get();
double dx = fabs(robot_in_2m.X() - current_pose.X());
if (robot_in_2m.X() > 2)
{
front_distance = (2 * (2 - current_pose.X()) / dx) * 1000.0;
}
if (robot_2m_back.X() > 2)
{
rear_distance = (2 * (2 - current_pose.X()) / dx) * 1000.0;
}
// publish updated values
pose.Publish(current_pose, now);
ir_distance_front.Publish(front_distance, now);
ir_distance_rear.Publish(rear_distance, now);
Add the external include
#include "plugins/scheduling/tThreadContainerThread.h"
As you probably noticed, the generated source files contain prose text with instructions (that won't
compile) at places you should pay attention to, before using the module. The
methods OnStaticParameterChange() and OnParameterChange() are such candidates. As we do not use
them in this module, you can delete them completely (from the .h and the .cpp file). Also, delete the
content of the Control() method.
Sensor noise simulation module
Now we will create a plain Module (no SenseControlModule) for simulating sensor noise. It will add a
gauss-distributed random value to the numeric input. Call it "AddNoise". Comment: "Adds noise (a
gauss-distributed random value) to the numeric input."
~/finroc$ finroc_create
We create a plain Module instead of a SenseControlModule, because we cannot really say if the
processed values in this module are always sensor data (in this tutorial we can, but let's say it will
become a generic library module).
angular_velocity.ConnectTo(main_sim->angular_velocity);
main_sim->pose.ConnectTo(pose);
main_sim->ir_distance_front.ConnectTo(add_noise->input);
add_noise->output.ConnectTo(ir_distance_front);
This will create the two modules and connect their ports. (We will instantiate and connect a
second AddNoise module for the rear sensor later - graphically).
Furthermore, we need to include the two module types. (These belong in the internal includes section,
because they come from the same project/repository)
#include "projects/crash_course/mMainSimulation.h"
#include "projects/crash_course/mAddNoise.h"
Create part
In Finroc, there are two ways to create an application that can actually be executed.
We will create a binary executable here. When the finstruct tool is introduced in the next chapter, the
second option is explained.
~/finroc$ finroc_create
Create a part in sources/cpp/projects/crash_course and name it "CrashCourse".
Open sources/cpp/projects/crash_course/pCrashCourse.cpp. At the end, replace
new finroc::crash_course::mSimulation(main_thread);
with (we want to place our simulation group below the top-level thread container):
new finroc::crash_course::gSimulation(main_thread);
Then, set the main thread's cycle time to to 40ms:
main_thread->SetCycleTime(std::chrono::milliseconds(40));
Furthermore, projects/crash_course/gSimulation.h needs to be included instead
ofprojects/crash_course/mCrashCourse.h.
Furthermore we can rename the application's root element to "Crash Course" by changing the
variable cMAIN_THREAD_CONTAINER_NAME.
Finally, we need to tell our build system what it should build. Therefore we create amake.xml file
in sources/cpp/projects/crash_course:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<targets>
<library>
<sources exclude="pCrashCourse.cpp">
*.cpp
</sources>
</library>
<program name="finroc_crash_course">
<sources>
pCrashCourse.cpp
</sources>
</program>
</targets>
This builds a library libfinroc_projects_crash_course.so containing all our new components.
Furthermore, a program called finroc_crash_course will be created. SeeAdvanced/MakeBuilder for
details on the make.xml file format.
Build Part
So now everything should be fine and we can start compilation. If Finroc is not installed on your
system, this will take a moment.
in Finroc 13.10:
~/finroc$ makeSafe -j4
in any newer version of Finroc:
~/finroc$ make -j4
(4 stands for the number of parralel build jobs. A value of number of CPU-cores + 1 is recommended)
If there are no errors, the build operation finishes with a done message.
Start Part
We can now start the part. As all Finroc scripts and programs, the --help command line options lists the
available already predefined options for starting the program:
~/finroc$ finroc_crash_course --help
The default options are fine.
~/finroc$ finroc_crash_course
Finstruct
Now, the part is running, but we do not see anything except of the console output yet. To inspect and
interact with this part, we have the two tools fingui and finstruct.
Since our console running the part is blocked, we need to open another one. (In the new console we
need to run source scripts/setenv in the Finroc directory again.)
10
The tree view on the left can be used to navigate through the hierarchy of framework elements.
If you want, you can experiment with this tool. You should not worry about breaking anything. Since the
part is hard-coded, it is always possible to restart it and everything should return to normal.
Inspect port and parameter values
Navigate to the AddNoise Front module's Outputs. The view will change and show the current
value of the output port.
Now select the "AddNoise" module in the left tree and View->Port Data (View->PortView in
Finroc 13.10) from the menu. This will show all the module's ports and its parameter.
11
If you press Single Update (blue toolbar button on the right), the displayed port values will be
updated once (currently, only Output will change). If you activate Auto Update, the port values will be
updated regularly.
Deactivate auto-update and enter 5 m as Standard Deviation Parameter and press the green
"Apply" button. Note, how the output value range changes (you need to update values again to see the
effect).
Now enter 5 cm.
Here you can see what effects setting units in ports and parameters has: Values retrieved via
Get() are converted to the port's unit if the incoming value has another unit. Units of output ports are
merely attached to the value (Nevertheless, we encourage developers to use plain SI units whenever
possible - e.g. meter, gram).
Now, set the view back to Auto Select in the View menu.
Graphically create and connect modules
So now we are going to create a second AddNoise module from within finstruct. Navigate back to see
the structure of the gSimulation again (as in the first finstruct image) and select Connect mode in the
tree toolbar.
If you click on the arrows in the diagram on the right, the corresponding connections are shown in the
connection panel on the left.
Right-click in the diagram on the right and select Create Element.... A dialog appers. Change the
module name to AddNoise Rear.
12
13
In the same way, connect AddNoise Rear/Output with Sensor Output/Ir Distance Rear (of the Simulation
group!).
If you look at the ports of AddNoise Rear, you can see that it is already operating.
Now save the changes to our group by right-clicking in the right diagram and selectingSave
"projects/crash_course/gSimulation.h.xml".
The result can be viewed in sources/cpp/projects/crash_course/gSimulation.h.xml.
<?xml version="1.0" encoding="UTF-8"?>
<FinstructableGroup>
<element name="AddNoise Rear" group="finroc_projects_crash_course" type="AddNoise">
<parameters/>
</element>
<edge src="MainSimulation/Sensor Output/Ir Distance Rear" dest="AddNoise
Rear/Input/Input"/>
<edge src="AddNoise Rear/Output/Output" dest="Sensor Output/Ir Distance Rear"/>
</FinstructableGroup>
Note, that such files may also be edited with a text editor.
If you stop the part (press Ctrl-C) and start it again, the AddNoise Rear module should be instantiated
and connected.
Graphically create a part
As mentioned earlier, parts can also be created using finstruct.
Terminate the finroc_crash_course part.
Make sure, the $FINROC_PROJECT_HOME environment variable is set
to/user/home/finroc/sources/cpp/projects/crash_course . You will probably have to call
~/finroc$ source scripts/setenv -p crash_course
once. You only need to add -p if you want to change the current project.
14
Changing the project is important so that the CrashCourse.finroc file containing the part will be loaded
from and saved relative to the project directory.
.finroc files can be instantiated and executed using the finroc_run command. If the file does not yet
exist, it will be created on saving.
~/finroc$ finroc_run CrashCourse.finroc
Since $FINROC_PROJECT_HOME/CrashCourse.finroc does not exist yet, an empty part will be loaded.
In finstruct right-click and select Create Element.... Our modules are not loaded yet. Therefore, click
on Load... and double-click on finroc_projects_crash_course.
Select the component Simulation (finroc_projects_crash_course) and click on Create & Edit.
Now, we have the same part.
If you right-click and save (Save "CrashCourse.finroc"), the
file$FINROC_PROJECT_HOME/CrashCourse.finroc will be written.
Note, that depending on where you are navigating, either the group or the part is saved: If
the part is selected in the left tree, the part is saved. If the group is selected, the group is saved. You
can see in the part's command line output which file was saved.
Fingui
Now, we will actually interact with our part using a graphical user interface.
GUI files, as well as config files and all other non-finroc code, is typically stored in an etcfolder below
our project directory.
~/finroc$ mkdir -p sources/cpp/projects/crash_course/etc
Start the gui.
~/finroc$ fingui
You will see an empty canvas where GUI widgets can be placed.
The Fingui allows to select your preferred Look & Feel in the Edit-menu. Here's a brief summary on the
default setting ("Office-like"):
Select widget: <Strg> + left mouse button or select with a box (you can drag it by clicking on
empty space and moving the mouse over the widgets)
Create a GeometryRenderer, a VirtualJoystick, and two LCD numeric displays. You can assign labels to
the LCDs in their properties dialog.
Download this .svg file and add it to the map objects in the GeometryRenderer properties.
As the coordinates are in mm, select artos.svg, press Edit..., and set the scale to 0.001.
artos.svg
15
Furthermore, you need to invert the X axis of the virtual Joystick: X Left should be 1 and X Right should
be -1.
Select File->Connect->TCP from the main menu and accept localhost:4444.
Now click on View->Connection Panel in the menu to open the connection panel you should already be
familiar with from finstruct.
Connect the widgets in this way:
16
You can now experiment with the application you just built.
More advanced visualization using tCanvas2D
Often, it is helpful to visualize internals of a robotic application. The class tCanvas2Dprovides a flexible
and powerful solution for this purpose.
The visual representation of the simulation in the geometry renderer is currently very basic. We will now
create a visualization of the simulation in a seperate module that displays the wall etc.
Create a plain (non-sense-control) module mVisualization ("Visualizes the simulated scene using a
tCanvas2D.").
Ports are
/*! Position and orientation of robot in the world coordinate system */
tInput<rrlib::math::tPose2D> pose;
/*! Pose of last collision */
tInput<rrlib::math::tPose2D> last_collision_pose;
/*! Counts the number of spawned robots */
tInput<int> robot_counter;
/*! Visualization of simulation */
tOutput<rrlib::canvas::tCanvas2D> visualization;
The includes for the specified port types need to be added:
#include "rrlib/math/tPose2D.h"
#include "rrlib/canvas/tCanvas2D.h"
Furthermore, we add a private variable that is to be initialized with false in the constructor.
/*! Was last collision destructive? */
bool last_collision_destructive;
The Update() method illustrates how geometry can be drawn to the canvas objects and how it can be
published via ports:
if (this->InputChanged())
{
// visualize using tCanvas2D
// obtain buffer
data_ports::tPortDataPointer<rrlib::canvas::tCanvas2D> canvas =
visualization.GetUnusedBuffer();
canvas->Clear();
// Draw wall
canvas->SetFill(true);
canvas->SetColor(128, 64, 0); // brown
canvas->DrawBox(2, -1000, 2000, 2000);
// Draw Robot Text
rrlib::math::tPose2D current_pose = pose.Get();
canvas->Translate(current_pose.X(), current_pose.Y());
canvas->SetColor(255, 255, 255);
char robot_text[128];
snprintf(robot_text, 128, "Tutorial Robot #%d", robot_counter.Get() + 1);
canvas->DrawText(0.0, 0.28, robot_text);
canvas->ResetTransformation();
// Draw Robot
canvas->Transform(current_pose);
canvas->SetEdgeColor(0, 0, 0);
canvas->SetFillColor(200, 200, 255);
17
18
Note that parameters and config files are an area, which we plan to significantly improve in the
near future. Especially the graphical tool support is still in an experimental and very basic state.
Nevertheless, let's create a simple config
file fragile_bot.xml insources/cpp/projects/crash_course/etc with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<value name="noiselevel">15 cm</value>
<node name="some structural element">
<value name="destructive_collision_speed">0.1</value>
</node>
</root>
Now start the part with this config file:
~/finroc$ finroc_run -c sources/cpp/projects/crash_course/etc/fragile_bot.xml
CrashCourse.finroc
In finstruct, navigate to the simulation group and select Parameter-Connect mode from the tree toolbar.
You can now see the config file entries on the right. They can be connected to the parameters.
Connect the Standard Deviation Parameter of both AddNoise modules tonoiselevel. Also connect the
main simulation module's Destructive Collision Speedparameter.
19
20
A simple filtering approach for this module is sufficient (e.g. sum up the last 5 values and calculate the
arithmetic mean).
If you need a list - similar to Java's ArrayList - you could try std::vector.
Visualize distance sensor values
Add two input ports to the mVisualization module:
/*! Simulated distance sensor values to the front and to the rear */
tInput<double> ir_distance_front, ir_distance_rear;
Connect the distance sensor input ports to the outputs of the two AddNoise modules.
Modify mVisualization's Update() method so that the sensor values are visualized using the Canvas2D
(possibly as lines).
Note that the simulated sensors measure the distance from the center of the robot.
21