SimpleEpisodicMemory.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package MemoryX::ArmarXObjects::SimpleEpisodicMemory
17 * @author fabian.peller-konrad@kit.edu ( fabian dot peller-konrad at kit dot edu )
18 * @date 2020
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <filesystem>
26#include <fstream>
27
28#include <opencv2/opencv.hpp>
29
30namespace memoryx
31{
32
33 // In one of the implementation files
34 const std::string SimpleEpisodicMemory::NO_EPISODE = "NO_EPISODE";
35
36 const std::map<EpisodeStatus, std::string> SimpleEpisodicMemory::episode_status_descriptor = {
37 {EpisodeStatus::EPISODE_STARTED, "started"},
38 {EpisodeStatus::EPISODE_COMPLETED_SUCCESS, "success"},
39 {EpisodeStatus::EPISODE_COMPLETED_FAILURE, "failure"},
40 {EpisodeStatus::EPISODE_COMPLETED_ABORT, "abort"},
41 };
42
43 const std::map<ActionStatus, std::string> SimpleEpisodicMemory::action_status_descriptor = {
44 {ActionStatus::ACTION_STARTED, "started"},
45 {ActionStatus::ACTION_RUNNING, "running"},
46 {ActionStatus::ACTION_REPEATED, "repeated"},
47 {ActionStatus::ACTION_COMPLETED_SUCCESS, "success"},
48 {ActionStatus::ACTION_COMPLETED_FAILURE, "failure"},
49 };
50
51 const std::map<ObjectPoseEventType, std::string> SimpleEpisodicMemory::object_type_descriptor =
52 {
53 {ObjectPoseEventType::NEW_OBJECT_RECOGNIZED, "newly detected"},
54 {ObjectPoseEventType::OBJECT_POSE_UPDATE, "updated"},
55 };
56
57 std::string
59 {
60 return "SimpleEpisodicMemory";
61 }
62
63 void
65 {
66 m_enable_export = true; //getProperty<bool>("EnableExport");
67 m_export_folder = "/tmp/EMExport"; //getProperty<std::string>("ExportFolder");
68 }
69
70 void
72 {
73 clearAll();
74 }
75
76 void
80
81 void
85
86 void
87 SimpleEpisodicMemory::export_episode() const
88 {
89 ARMARX_IMPORTANT << "Exporting current episode!";
90 if (m_current_episode.episodeName == NO_EPISODE)
91 {
92 //return;
93 }
94 if (m_current_episode.imageEvents.size() + m_current_episode.actionEvents.size() +
95 m_current_episode.humanPoseEvents.size() + m_current_episode.speechEvents.size() +
96 m_current_episode.objectPoseEvents.size() +
97 m_current_episode.kinematicUnitEvents.size() +
98 m_current_episode.platformUnitEvents.size() +
99 m_current_episode.platformUnitTargetEvents.size() ==
100 0)
101 {
102 ARMARX_DEBUG << "No information found. Skip export.";
103 return;
104 }
105
106 if (std::filesystem::exists(m_export_folder))
107 {
108 const std::string episode_output =
109 m_export_folder + "/episode_" + std::to_string(m_current_episode.startedInMs) + "/";
110 if (!std::filesystem::create_directory(episode_output))
111 {
112 ARMARX_ERROR << "Couldn't create episode folder: " << episode_output;
113 return;
114 }
115
116 const std::string camera_output = episode_output + "camera/";
117 if (!std::filesystem::create_directory(camera_output))
118 {
119 ARMARX_ERROR << "Couldn't create camera folder";
120 return;
121 }
122
123 const std::string object_output = episode_output + "objects/";
124 if (!std::filesystem::create_directory(object_output))
125 {
126 ARMARX_ERROR << "Couldn't objects camera folder";
127 return;
128 }
129
130 const std::string action_output = episode_output + "actions/";
131 if (!std::filesystem::create_directory(action_output))
132 {
133 ARMARX_ERROR << "Couldn't create actions folder";
134 return;
135 }
136
137 const std::string human_poses_output = episode_output + "poses/";
138 if (!std::filesystem::create_directory(human_poses_output))
139 {
140 ARMARX_ERROR << "Couldn't create poses folder";
141 return;
142 }
143
144 const std::string speech_output = episode_output + "speech/";
145 if (!std::filesystem::create_directory(speech_output))
146 {
147 ARMARX_ERROR << "Couldn't create speech folder";
148 return;
149 }
150
151 const std::string platformUnit_output = episode_output + "platformUnit/";
152 if (!std::filesystem::create_directory(platformUnit_output))
153 {
154 ARMARX_ERROR << "Couldn't create platformUnit folder";
155 return;
156 }
157
158 const std::string platformUnitTarget_output = episode_output + "platformUnitTarget/";
159 if (!std::filesystem::create_directory(platformUnitTarget_output))
160 {
161 ARMARX_ERROR << "Couldn't create platformUnitTarget folder";
162 return;
163 }
164
165 const std::string kinematicUnit_output = episode_output + "kinematicUnit/";
166 if (!std::filesystem::create_directory(kinematicUnit_output))
167 {
168 ARMARX_ERROR << "Couldn't create kinematicUnit folder";
169 return;
170 }
171
172
173 // Successfully created folders
174 // Now export data
175 // Start with episode information
176 {
177 ARMARX_DEBUG << "Export Episode info";
178 std::filesystem::path path{episode_output + "episode.json"};
179
180 std::ofstream e_ofs(path);
181 e_ofs << "{\n";
182 e_ofs << "\t\"name\": \"" + m_current_episode.episodeName + "\",\n";
183 e_ofs << "\t\"status\": \"" +
185 m_current_episode.status) +
186 "\",\n";
187 e_ofs << "\t\"started\": \"" + std::to_string(m_current_episode.startedInMs) +
188 "\",\n";
189 e_ofs << "\t\"ended\": \"" + std::to_string(m_current_episode.endedInMs) + "\"\n";
190 e_ofs << "}";
191 e_ofs.close();
192 }
193
194 // Export object information
195 {
196 ARMARX_DEBUG << "Export Object info";
197 for (const auto& objectEvent : m_current_episode.objectPoseEvents)
198 {
199 const double timestamp = objectEvent.receivedInMs;
200
201 std::ofstream o_ofs(object_output + "obj_" + std::to_string(timestamp) +
202 ".json");
203 o_ofs << "{\n";
204 o_ofs << "\t\"name\": \"" + objectEvent.objectName + "\",\n";
205 o_ofs << "\t\"position\": [" + std::to_string(objectEvent.x) + ", " +
206 std::to_string(objectEvent.y) + ", " +
207 std::to_string(objectEvent.z) + "],\n";
208 o_ofs << "\t\"started\": \"" + std::to_string(timestamp) + "\",\n";
209 o_ofs << "\t\"frame\": \"" + objectEvent.frame + "\",\n";
210 o_ofs << "\t\"type\": \"" +
212 "\"\n";
213 o_ofs << "}";
214 o_ofs.close();
215 }
216 }
217
218 // Export action information
219 {
220 ARMARX_DEBUG << "Export Action info";
221 for (const auto& actionEvent : m_current_episode.actionEvents)
222 {
223 const double timestamp = actionEvent.receivedInMs;
224
225 std::ofstream a_ofs(action_output + "act_" + std::to_string(timestamp) +
226 ".json");
227 a_ofs << "{\n";
228 a_ofs << "\t\"name\": \"" + actionEvent.actionName + "\",\n";
229 a_ofs << "\t\"status\": \"" +
231 actionEvent.status) +
232 "\",\n";
233 a_ofs << "\t\"started\": \"" + std::to_string(timestamp) + "\"\n";
234 a_ofs << "}";
235 a_ofs.close();
236 }
237 }
238
239 // Export human poses
240 {
241 ARMARX_DEBUG << "Export Human pose info";
242 for (const auto& humanPose : m_current_episode.humanPoseEvents)
243 {
244 const double timestamp = humanPose.receivedInMs;
245
246 std::ofstream p_ofs(human_poses_output + "pose_" + std::to_string(timestamp) +
247 ".json");
248 p_ofs << "{\n";
249
250 for (const auto& [label, keypoint] : humanPose.keypoints)
251 {
252 p_ofs << "\t\"" + label + "\":\n";
253 p_ofs << "\t{\n";
254 p_ofs << "\t\t \"confidence\": " + std::to_string(keypoint.confidence) +
255 ",\n";
256 p_ofs << "\t\t \"local\": [" + std::to_string(keypoint.x) + ", " +
257 std::to_string(keypoint.y) + ", " +
258 std::to_string(keypoint.z) + " ],\n";
259 p_ofs << "\t\t \"global\": [" + std::to_string(keypoint.globalX) + ", " +
260 std::to_string(keypoint.globalY) + ", " +
261 std::to_string(keypoint.globalZ) + " ]\n";
262 p_ofs << "\t},\n";
263 }
264 p_ofs << "}";
265 p_ofs.close();
266 }
267 }
268
269 // Export speech
270 {
271 ARMARX_DEBUG << "Export Speech info";
272 for (const auto& speech : m_current_episode.speechEvents)
273 {
274 const double timestamp = speech.receivedInMs;
275
276 std::ofstream s_ofs(speech_output + "speech_" + std::to_string(timestamp) +
277 ".json");
278 s_ofs << "{\n";
279 s_ofs << "\t \"text\": " << speech.text << "\n";
280 s_ofs << "}";
281 s_ofs.close();
282 }
283 }
284
285 // Export kinematicUnit
286 {
287 ARMARX_DEBUG << "Export KinematicUnit info";
288 for (const auto& kinematicUnit : m_current_episode.kinematicUnitEvents)
289 {
290 const double timestamp = kinematicUnit.receivedInMs;
291
292 std::ofstream k_ofs(kinematicUnit_output + "kinematicUnit_" +
293 std::to_string(timestamp) + ".json");
294 k_ofs << "{\n";
295 for (const auto& [key, value] : kinematicUnit.data)
296 {
297 k_ofs << "\t \"" + key + "\": {\n";
298 k_ofs << "\t\t \"jointAngle\": \"" << value.jointAngle << "\",\n";
299 k_ofs << "\t\t \"jointVelocity\": \"" << value.jointVelocity << "\",\n";
300 k_ofs << "\t\t \"jointTorque\": \"" << value.jointTorque << "\",\n";
301 k_ofs << "\t\t \"jointAcceleration\": \"" << value.jointAcceleration
302 << "\",\n";
303 k_ofs << "\t\t \"current\": \"" << value.current << "\",\n";
304 k_ofs << "\t\t \"temperature\": \"" << value.temperature << "\",\n";
305 k_ofs << "\t\t \"enabled\": \"" << value.enabled << "\"\n";
306 k_ofs << "\t },\n";
307 }
308 k_ofs << "}";
309 k_ofs.close();
310 }
311 }
312
313 // Export platformUnit
314 {
315 ARMARX_DEBUG << "Export PlatformUnit info";
316 for (const auto& platformUnit : m_current_episode.platformUnitEvents)
317 {
318 const double timestamp = platformUnit.receivedInMs;
319
320 std::ofstream p_ofs(platformUnit_output + "platformUnit_" +
321 std::to_string(timestamp) + ".json");
322 p_ofs << "{\n";
323 p_ofs << "\t \"x\": \"" << platformUnit.x << "\",\n";
324 p_ofs << "\t \"y\": \"" << platformUnit.y << "\",\n";
325 p_ofs << "\t \"rot\": \"" << platformUnit.rot << "\",\n";
326 p_ofs << "\t \"acc_x\": \"" << platformUnit.acc_x << "\",\n";
327 p_ofs << "\t \"acc_y\": \"" << platformUnit.acc_y << "\",\n";
328 p_ofs << "\t \"acc_rot\": \"" << platformUnit.acc_rot << "\"\n";
329 p_ofs << "}";
330 p_ofs.close();
331 }
332 }
333
334 // Export platformUnitTarget
335 {
336 ARMARX_DEBUG << "Export PlatformUnitTarget info";
337 for (const auto& platformUnitTarget : m_current_episode.platformUnitTargetEvents)
338 {
339 const double timestamp = platformUnitTarget.receivedInMs;
340
341 std::ofstream t_ofs(platformUnitTarget_output + "platformUnitTarget_" +
342 std::to_string(timestamp) + ".json");
343 t_ofs << "{\n";
344 t_ofs << "\t \"x\": \"" << platformUnitTarget.target_x << "\",\n";
345 t_ofs << "\t \"y\": \"" << platformUnitTarget.target_y << "\",\n";
346 t_ofs << "\t \"rot\": \"" << platformUnitTarget.target_rot << "\"\n";
347 t_ofs << "}";
348 t_ofs.close();
349 }
350 }
351
352
353 // Export image information
354 {
355 ARMARX_DEBUG << "Export Image info";
356 for (const auto& [imageProvider, imageEventList] : m_current_episode.imageEvents)
357 {
358 if (imageEventList.size() == 0)
359 {
360 continue;
361 }
362 const std::string image_provider_output = camera_output + imageProvider + "/";
363 if (!std::filesystem::create_directory(image_provider_output))
364 {
365 ARMARX_ERROR << "Couldn't create image provider folder: " + imageProvider;
366 return;
367 }
368
369 for (const auto& imageEvent : imageEventList)
370 {
371 const double timestamp = imageEvent.receivedInMs;
372
373 auto mode = CV_8UC3;
374 if (imageEvent.colourType == memoryx::ColourSpace::GRAYSCALE)
375 {
376 mode = CV_8UC1;
377 }
378 std::vector<uchar> data(imageEvent.data);
379 ARMARX_DEBUG << "Image size is " << imageEvent.width << ", "
380 << imageEvent.height << ", " << imageEvent.colourType << " => "
381 << imageEvent.data.size();
382 cv::Mat cv_image =
383 cv::Mat(imageEvent.height, imageEvent.width, mode, data.data());
384
385 cv::cvtColor(cv_image, cv_image, cv::COLOR_RGB2BGR);
386 cv::imwrite(image_provider_output + "img_" + std::to_string(timestamp) +
387 ".jpg",
388 cv_image);
390 << "Exporting image to: img_" + std::to_string(timestamp) + ".jpg";
391 }
392 }
393 }
394 }
395 else
396 {
397 ARMARX_ERROR << "Cannot export files because folder does not exist: "
398 << m_export_folder;
399 }
400 }
401
402 void
403 SimpleEpisodicMemory::clearAll()
404 {
405 ARMARX_DEBUG << "Resetting episode info to default.";
406 m_current_episode.episodeName = NO_EPISODE;
407 m_current_episode.startedInMs = IceUtil::Time::now().toMilliSecondsDouble();
408 m_current_episode.status = EpisodeStatus::EPISODE_STARTED;
409 }
410
411 void
412 SimpleEpisodicMemory::registerEpisodeEvent(const EpisodeEvent& e, const Ice::Current& c)
413 {
414 std::lock_guard<std::mutex> l(episodeEventMutex);
415 if (m_current_episode.episodeName != e.episodeName && e.status != EPISODE_STARTED)
416 {
417 ARMARX_ERROR << "Received an episode unequal to current one with non-starting status: "
418 << e.episodeName;
419 }
420 if (m_current_episode.episodeName != e.episodeName && m_current_episode.endedInMs != 0 &&
421 e.status == EPISODE_STARTED)
422 {
423 ARMARX_WARNING << "Received a new starting episode without ending the last one. Last "
424 "episodes name is "
425 << m_current_episode.episodeName << ". Finishing it now.";
426 EpisodeEvent abort;
427 abort.episodeName = m_current_episode.episodeName;
428 abort.status = EpisodeStatus::EPISODE_COMPLETED_ABORT;
429 abort.receivedInMs = IceUtil::Time::now().toMilliSecondsDouble();
430 registerEpisodeEvent(abort, c);
431 }
432
433 if (e.status == EPISODE_COMPLETED_SUCCESS || e.status == EPISODE_COMPLETED_FAILURE ||
434 e.status == EPISODE_COMPLETED_ABORT)
435 {
436 ARMARX_DEBUG << "Received a terminating episode (" << e.episodeName << ")";
437 m_current_episode.status = e.status;
438 m_current_episode.endedInMs = e.receivedInMs;
439 }
440 else //if (e.status == EPISODE_STARTED)
441 {
442 ARMARX_DEBUG << "Received a starting episode (" << e.episodeName << ")";
443 m_current_episode.episodeName = e.episodeName;
444 m_current_episode.status = e.status;
445 m_current_episode.startedInMs = e.receivedInMs;
446 }
447
448 std::lock_guard<std::mutex> l2(imageEventMutex);
449 std::lock_guard<std::mutex> l3(humanPoseEventMutex);
450 std::lock_guard<std::mutex> l4(speechEventMutex);
451 std::lock_guard<std::mutex> l5(objectPoseEventMutex);
452 std::lock_guard<std::mutex> l6(kinematicUnitEventMutex);
453 std::lock_guard<std::mutex> l7(platformUnitEventMutex);
454 std::lock_guard<std::mutex> l8(platformUnitTargetEventMutex);
455 std::lock_guard<std::mutex> l9(actionEventMutex);
456 if (m_enable_export && m_current_episode.endedInMs != 0)
457 {
458 ARMARX_DEBUG << "Exporting episode...";
459 export_episode();
460 clearAll();
461 }
462 }
463
464 void
465 SimpleEpisodicMemory::registerImageEvent(const ImageEvent& i, const Ice::Current& c)
466 {
467 std::lock_guard<std::mutex> l(imageEventMutex);
468 ARMARX_DEBUG << "Received an image. Current number of images of provider " << i.providerName
469 << " in episode: "
470 << this->m_current_episode.imageEvents[i.providerName].size();
471 this->m_current_episode.imageEvents[i.providerName].push_back(i);
472 }
473
474 void
475 SimpleEpisodicMemory::registerObjectPoseEvent(const ObjectPoseEvent& o, const Ice::Current& c)
476 {
477 std::lock_guard<std::mutex> l(objectPoseEventMutex);
478 ARMARX_DEBUG << "Received an objectPose (" << o.objectName
479 << "). Current number of objectPoses in episode: "
480 << this->m_current_episode.objectPoseEvents.size();
481 this->m_current_episode.objectPoseEvents.push_back(o);
482 }
483
484 void
485 SimpleEpisodicMemory::registerActionEvent(const ActionEvent& a, const Ice::Current& c)
486 {
487 std::lock_guard<std::mutex> l(actionEventMutex);
488 ARMARX_DEBUG << "Received an action (" << a.actionName << " with status "
490 << ")Current number of actions in episode: "
491 << this->m_current_episode.actionEvents.size();
492 this->m_current_episode.actionEvents.push_back(a);
493 }
494
495 void
496 SimpleEpisodicMemory::registerHumanPoseEvent(const Body25HumanPoseEvent& p, const Ice::Current&)
497 {
498 std::lock_guard<std::mutex> l(humanPoseEventMutex);
499 ARMARX_DEBUG << "Received a human pose. Current number of poses in episode: "
500 << this->m_current_episode.humanPoseEvents.size();
501 this->m_current_episode.humanPoseEvents.push_back(p);
502 }
503
504 void
505 SimpleEpisodicMemory::registerSpeechEvent(const SpeechEvent& s, const Ice::Current&)
506 {
507 if (s.text.find("export now") != std::string::npos)
508 {
509 ARMARX_IMPORTANT << "Received export token!. Terminating current episode and create an "
510 "empty new one.";
511 EpisodeEvent terminate;
512 terminate.episodeName = m_current_episode.episodeName;
513 terminate.status = EpisodeStatus::EPISODE_COMPLETED_ABORT;
514 terminate.receivedInMs = IceUtil::Time::now().toMilliSecondsDouble();
516 return;
517 }
518
519 std::lock_guard<std::mutex> l(speechEventMutex);
520 ARMARX_DEBUG << "Received spoken text (" << s.text
521 << "). Current number of speeches in episode: "
522 << this->m_current_episode.speechEvents.size();
523 this->m_current_episode.speechEvents.push_back(s);
524 }
525
526 void
528 const Ice::Current&)
529 {
530 std::lock_guard<std::mutex> l(kinematicUnitEventMutex);
531 ARMARX_DEBUG << "Received a kinematicUnitEvent. Current number of events in episode: "
532 << this->m_current_episode.kinematicUnitEvents.size();
533 this->m_current_episode.kinematicUnitEvents.push_back(k);
534 }
535
536 void
537 SimpleEpisodicMemory::registerPlatformUnitEvent(const PlatformUnitEvent& p, const Ice::Current&)
538 {
539 std::lock_guard<std::mutex> l(platformUnitEventMutex);
540 ARMARX_DEBUG << "Received a platformUnitEvent. Current number of events in episode: "
541 << this->m_current_episode.platformUnitEvents.size();
542 this->m_current_episode.platformUnitEvents.push_back(p);
543 }
544
545 void
547 const Ice::Current&)
548 {
549 std::lock_guard<std::mutex> l(platformUnitTargetEventMutex);
550 ARMARX_DEBUG << "Received a platformUnitTarget. Current number of events in episode: "
551 << this->m_current_episode.platformUnitTargetEvents.size();
552 this->m_current_episode.platformUnitTargetEvents.push_back(t);
553 }
554
555 void
557 {
558 }
559
562 {
565
566 //def->bool(m_enable_export, "EnableExport");
567 return def;
568 }
569
570} // namespace memoryx
std::string timestamp()
uint8_t data[1]
constexpr T c
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
void terminate()
Initiates termination of this IceManagedObject.
void onInitComponent() override
Pure virtual hook for the subclass.
void onDisconnectComponent() override
Hook for subclass.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void registerHumanPoseEvent(const Body25HumanPoseEvent &, const Ice::Current &=Ice::emptyCurrent) override
static const std::map< ActionStatus, std::string > action_status_descriptor
void registerObjectPoseEvent(const ObjectPoseEvent &, const Ice::Current &=Ice::emptyCurrent) override
void registerKinematicUnitEvent(const KinematicUnitEvent &, const Ice::Current &=Ice::emptyCurrent) override
void registerActionEvent(const ActionEvent &, const Ice::Current &=Ice::emptyCurrent) override
void registerSpeechEvent(const SpeechEvent &, const Ice::Current &=Ice::emptyCurrent) override
void onConnectComponent() override
Pure virtual hook for the subclass.
void registerImageEvent(const ImageEvent &, const Ice::Current &=Ice::emptyCurrent) override
void notifyKeyframe(const Ice::Current &=Ice::emptyCurrent) override
void onExitComponent() override
Hook for subclass.
void registerPlatformUnitEvent(const PlatformUnitEvent &, const Ice::Current &=Ice::emptyCurrent) override
void registerPlatformUnitTargetEvent(const PlatformUnitTargetEvent &, const Ice::Current &=Ice::emptyCurrent) override
std::string getDefaultName() const override
Retrieve default name of component.
static const std::map< EpisodeStatus, std::string > episode_status_descriptor
void registerEpisodeEvent(const EpisodeEvent &, const Ice::Current &=Ice::emptyCurrent) override
static const std::map< ObjectPoseEventType, std::string > object_type_descriptor
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855
VirtualRobot headers.