Skip to content

Commit 6b04736

Browse files
authored
Merge pull request #56 from KIT-ISAS/feature/evaluation
Feature/evaluation
2 parents becddcd + 7f46e37 commit 6b04736

File tree

8 files changed

+241
-2
lines changed

8 files changed

+241
-2
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,17 @@ Both logging styles are fully compatible with *rosconsole*, including *rqt_logge
377377

378378
To monitor the performance of the deployed controllers, it is vital to measure the calculation time as well as the jitter of the `updateHook()`-invocations. For this reason, the *rt_runner* publishes a timestamp of the update invocation time as well as the calculation time on the topic `/rt_runner/iteration_info` after the completion of every iteration.
379379

380-
Additionally, some timing statistics are printed to the console each time the control loop is stopped.
380+
Additionally, some timing statistics are printed to the console each time the control loop is stopped. If a more detailed analysis is desired, a rosbag can be recorded using
381+
382+
```bash
383+
rosbag record /rt_runner/iteration_info -o bagname --duration 3h
384+
```
385+
386+
or a similar command. The resulting rosbag can then be analyzed conveniently with an in-built script that plots the latencies as well as the calculation durations. The necessary command for this is:
387+
388+
```bash
389+
rosrun rtcf evaluate_performance_rosbag path_to_rosbag frequency
390+
```
381391

382392
#### Miscellaneous
383393

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/python
2+
3+
import rosbag
4+
from rospy import Time
5+
import matplotlib.pyplot as plt
6+
import numpy as np
7+
import sys
8+
import os.path
9+
10+
11+
def printHelp():
12+
print("Evaluate a rosbag with performance data for the RTCF. The output file is located at the same path as the input file")
13+
print("")
14+
print("Syntax: rosrun rtcf evaluate_performance_rosbag path_to_rosbag frequency")
15+
print("")
16+
print("path_to_rosbag Path to the rosbag-file.")
17+
print("frequency The frequency of the control-loop in the rosbag-file.")
18+
print("")
19+
print("To record such a rosbag use the following command as a starting point:")
20+
print("rosbag record /rt_runner/iteration_info -o bagname --duration 3h")
21+
quit()
22+
23+
24+
# argument parsing
25+
if len(sys.argv) != 2+1:
26+
printHelp()
27+
try:
28+
frequency = float(sys.argv[2])
29+
except ValueError:
30+
printHelp()
31+
path = sys.argv[1]
32+
if not os.path.exists(path):
33+
printHelp()
34+
folder, file = os.path.split(path)
35+
folder = folder + '/'
36+
37+
38+
durations = []
39+
intervalls = []
40+
last_time = None
41+
42+
# open rosbag
43+
print("Opening rosbag. This may take a while...")
44+
bag = rosbag.Bag(folder+file)
45+
topics = ['/rt_runner/iteration_info']
46+
count = bag.get_message_count(topic_filters=topics)
47+
48+
49+
print("Reading messages. This may take a while...")
50+
i = 0
51+
for topic, msg, t in bag.read_messages(topics=topics):
52+
time = msg.stamp.to_nsec()
53+
i = i+1
54+
if i % int(frequency*60*10) == 0:
55+
print("10 minutes processed...")
56+
if last_time:
57+
durations.append(msg.duration_ns)
58+
intervalls.append(time-last_time)
59+
last_time = time
60+
61+
bag.close()
62+
63+
durations = np.array(durations)
64+
intervalls = np.array(intervalls)
65+
66+
durations = durations / 1000.0
67+
intervalls = intervalls / 1000.0
68+
69+
last_index = durations.size
70+
71+
# get min, max and 99.9 % percentile
72+
duration_min = np.min(durations)
73+
duration_max = np.max(durations)
74+
duration_percentile = np.percentile(durations, 99.9)
75+
76+
intervall_min = np.min(intervalls)
77+
intervall_max = np.max(intervalls)
78+
intervall_percentile = np.percentile(intervalls, 99.9)
79+
80+
t = np.arange(0, last_index, 1)
81+
t = t / (float(frequency)) / 60
82+
83+
print("Found {} samples.".format(t.size))
84+
print("Plotting the results. This may take a while...")
85+
86+
# TIME SERIES
87+
fig, axs = plt.subplots(2)
88+
axs[0].scatter(t, durations, marker='+')
89+
axs[0].set_ylim(0, 50)
90+
axs[0].set_xlabel("t in min")
91+
axs[0].set_ylabel("calculation duration in us")
92+
93+
axs[1].scatter(t, intervalls, marker='+')
94+
center = 1.0/frequency * 1e6
95+
axs[1].set_ylim(center - 100, center + 100)
96+
axs[1].set_xlabel("t in min")
97+
axs[1].set_ylabel("calculation intervall in us")
98+
99+
output_filename = folder+file+".evaluation.png"
100+
plt.savefig(output_filename, dpi=600)
101+
102+
print("Plot was written to: {}".format(folder+file+".evaluation.png"))

rtcf_examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ add_subdirectory(src/identity)
3232
add_subdirectory(src/mimo)
3333
add_subdirectory(src/parameter_handling)
3434
add_subdirectory(src/logging)
35+
add_subdirectory(src/paper_example)
3536

3637
# finalize the OROCOS package
3738
orocos_generate_package()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<launch>
2+
<node name="rt_runner" pkg="rtcf" type="rt_runner" output="screen">
3+
<rosparam param="mode">"wait_for_components"</rosparam>
4+
<rosparam param="ros_mapping_whitelist">".*R3_G2H1|.*R1_H1R2|.*G2_R2"</rosparam>
5+
<rosparam param="num_components_expected">5</rosparam>
6+
<rosparam param="frequency">2000.0</rosparam>
7+
<rosparam param="lock_memory">True</rosparam>
8+
<rosparam param="safe_heap_size_kB">4096</rosparam>
9+
<rosparam param="safe_stack_size_kB">512</rosparam>
10+
<rosparam param="cpu_affinity_mask">02</rosparam>
11+
</node>
12+
13+
<node name="H1" pkg="rtcf" type="rt_launcher" args="rtcf_examples Mimo">
14+
<remap from="out1" to="/H1_F1G1"/>
15+
<remap from="out2" to="/H1_R1R2"/>
16+
<remap from="in1" to="/R3_G2H1"/>
17+
<remap from="in2" to="/R1_H1R2"/>
18+
<rosparam param="is_first">True</rosparam>
19+
</node>
20+
21+
<node name="F1" pkg="rtcf" type="rt_launcher" args="rtcf_examples Mimo">
22+
<remap from="out1" to="/F1_R2R3"/>
23+
<remap from="out2" to="/F1_out2"/>
24+
<remap from="in1" to="/H1_F1G1"/>
25+
</node>
26+
27+
<node name="R1" pkg="rtcf" type="rt_launcher" args="rtcf_examples Mimo">
28+
<remap from="out1" to="/R1_H1R2"/>
29+
<remap from="out2" to="/R1_out2"/>
30+
<remap from="in1" to="/H1_R1R2"/>
31+
</node>
32+
33+
<node name="R2" pkg="rtcf" type="rt_launcher" args="rtcf_examples Mimo">
34+
<remap from="out1" to="/R2_R3"/>
35+
<remap from="out2" to="/R2_out2"/>
36+
<remap from="in1" to="/F1_R2R3"/>
37+
<remap from="in2" to="/H1_R1R2"/>
38+
<remap from="in3" to="/R1_H1R2"/>
39+
<remap from="in4" to="/G2_R2"/>
40+
</node>
41+
42+
<node name="R3" pkg="rtcf" type="rt_launcher" args="rtcf_examples Mimo">
43+
<remap from="out1" to="/R3_G2H1"/>
44+
<remap from="out2" to="/R3_out2"/>
45+
<remap from="in1" to="/F1_R2R3"/>
46+
<remap from="in2" to="/R2_R3"/>
47+
</node>
48+
49+
</launch>
50+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<launch>
2+
<node name="rt_runner" pkg="rtcf" type="rt_runner" output="screen">
3+
<rosparam param="mode">"wait_for_components"</rosparam>
4+
<rosparam param="ros_mapping_whitelist">".*ros.*"</rosparam>
5+
<rosparam param="num_components_expected">2</rosparam>
6+
<rosparam param="frequency">1000.0</rosparam>
7+
</node>
8+
<node name="example1" pkg="rtcf" type="rt_launcher" args="rtcf_examples PaperExample">
9+
<remap from="out_port" to="/tmp"/>
10+
<remap from="in_port" to="/ros/in"/>
11+
<rosparam param="is_first">True</rosparam>
12+
</node>
13+
<node name="example2" pkg="rtcf" type="rt_launcher" args="rtcf_examples PaperExample">
14+
<remap from="out_port" to="/ros/out"/>
15+
<remap from="in_port" to="/tmp"/>
16+
</node>
17+
</launch>
18+

rtcf_examples/src/mimo/component.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,25 @@ bool Mimo::startHook() {
3737
return true;
3838
}
3939

40-
void Mimo::updateHook() {}
40+
void Mimo::updateHook() {
41+
// read will copy old data, which is fine
42+
std_msgs::Float64 in_msg_1_, in_msg_2_, in_msg_3_, in_msg_4_;
43+
in_msg_1_.data = 1.0;
44+
port_in1_.read(in_msg_1_, false);
45+
port_in2_.read(in_msg_2_, false);
46+
port_in3_.read(in_msg_3_, false);
47+
port_in4_.read(in_msg_4_, false);
48+
49+
std_msgs::Float64 out_msg_;
50+
// just do a plain simple addition with some overflow checking
51+
out_msg_.data = in_msg_1_.data + in_msg_2_.data + in_msg_3_.data + in_msg_4_.data;
52+
// make sure float does not overflow (pure addition will cause an overflow)
53+
if (out_msg_.data > 1e20) {
54+
out_msg_.data = 1;
55+
}
56+
port_out1_.write(out_msg_);
57+
port_out2_.write(out_msg_);
58+
}
4159

4260
void Mimo::stopHook() { NON_RT_INFO("Mimo executes stopping !"); }
4361

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
orocos_component(paper_example component.cpp)
2+
target_link_libraries(paper_example ${catkin_LIBRARIES})
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <std_msgs/Float32.h>
2+
3+
#include <rtcf/macros.hpp>
4+
#include <rtcf/rtcf_extension.hpp>
5+
OROCOS_HEADERS_BEGIN
6+
#include <rtt/Component.hpp>
7+
#include <rtt/Port.hpp>
8+
#include <rtt/RTT.hpp>
9+
OROCOS_HEADERS_END
10+
11+
class PaperExample : public RTT::TaskContext, public RtcfExtension {
12+
public:
13+
PaperExample(std::string const& name) : TaskContext(name), port_out_("out_port"), port_in_("in_port") {}
14+
15+
bool configureHook() {
16+
this->ports()->addPort(port_in_);
17+
this->ports()->addPort(port_out_);
18+
double param = this->getNodeHandle().param("/test_parameter", 0.0);
19+
NON_RT_INFO_STREAM("Fetched param with value " << param);
20+
return true;
21+
}
22+
void updateHook() {
23+
RT_INFO("Update hook called!");
24+
port_in_.read(msg_);
25+
port_out_.write(msg_);
26+
}
27+
28+
bool startHook() { return true; }
29+
void stopHook() {}
30+
void cleanupHook() {}
31+
32+
private:
33+
RTT::OutputPort<std_msgs::Float32> port_out_;
34+
RTT::InputPort<std_msgs::Float32> port_in_;
35+
std_msgs::Float32 msg_;
36+
};
37+
38+
ORO_CREATE_COMPONENT(PaperExample)

0 commit comments

Comments
 (0)