MotionFileWrapper.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2020, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package RobotComponents::Libraries::MMM
19 * @author Andre Meixner
20 * @date 2020
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25
26#include "MotionFileWrapper.h"
27
28#include <Eigen/LU>
29
30#include <SimoxUtility/math/convert.h>
31#include <SimoxUtility/xml/rapidxml/RapidXMLWrapper.h>
32#include <VirtualRobot/MathTools.h>
33#include <VirtualRobot/Robot.h>
34
37
38#include <MMM/Model/ModelProcessorWinter.h>
39#include <MMM/Motion/Legacy/LegacyMotion.h>
40#include <MMM/Motion/Legacy/LegacyMotionReaderXML.h>
41#include <MMM/Motion/MotionReaderXML.h>
42#include <MMM/Motion/Plugin/KinematicPlugin/KinematicSensor.h>
43#include <MMM/Motion/Plugin/ModelPosePlugin/ModelPoseSensor.h>
44#include <MMM/Motion/XMLTools.h>
45
46using namespace armarx;
47using namespace MMM;
48
50MotionFileWrapper::create(const std::string& motionFilePath,
51 double butterworthFilterCutOffFreq,
52 const std::string relativeModelRoot)
53{
54 MotionFileWrapperPtr wrapper(new MotionFileWrapper(butterworthFilterCutOffFreq));
55
56 simox::xml::RapidXMLWrapperRootNodePtr root =
57 simox::xml::RapidXMLWrapperRootNode::FromFile(motionFilePath);
58 if (root && root->name() == xml::tag::MMM_ROOT)
59 {
60 if (!root->has_attribute(xml::attribute::VERSION))
61 {
62 if (!wrapper->loadLegacyMotion(motionFilePath))
63 {
64 return nullptr;
65 }
66 }
67 else
68 {
69 std::string version = root->attribute_value(xml::attribute::VERSION);
70 if (version == "2.0")
71 {
72 if (!wrapper->loadMotion(motionFilePath, relativeModelRoot))
73 {
74 return nullptr;
75 }
76 }
77 else if (version == "1.0 ")
78 {
79 if (!wrapper->loadLegacyMotion(motionFilePath, relativeModelRoot))
80 {
81 return nullptr;
82 }
83 }
84 else
85 {
86 ARMARX_ERROR << "Could not load mmm motion with version '" << version << "'";
87 return nullptr;
88 }
89 }
90 }
91 else
92 {
93 return nullptr;
94 }
95
96 return wrapper;
97}
98
99MotionFileWrapper::MotionFileWrapper(int butterworthFilterCutOffFreq) :
100 butterworthFilterCutOffFreq(butterworthFilterCutOffFreq)
101{
102}
103
104bool
105MotionFileWrapper::loadMotion(const std::string& motionFilePath,
106 const std::string relativeModelRoot)
107{
108 ARMARX_INFO << "Loading motion";
109 MotionRecordingPtr motions;
110 try
111 {
112 MotionReaderXMLPtr motionReader(new MotionReaderXML());
113 motions = motionReader->loadMotionRecording(motionFilePath);
114 }
115 catch (Exception::MMMException& e)
116 {
117 ARMARX_ERROR << "Could not load mmm motion! " << e.what();
118 return false;
119 }
120
121 Eigen::Matrix4f modelRootTransformation = Eigen::Matrix4f::Identity();
122 for (auto motion : *motions)
123 {
124 ModelPtr model = motion->getModel();
125 if (model)
126 {
127 if (!model->getFilename().empty() &&
128 std::filesystem::path(model->getFilename()).stem() == relativeModelRoot)
129 {
130 auto modelPoseSensor = motion->getSensorByType<ModelPoseSensor>();
131 if (modelPoseSensor && modelPoseSensor->getTimesteps().size() > 0)
132 {
133 auto modelPoseSensorMeasurement = modelPoseSensor->getDerivedMeasurement(
134 modelPoseSensor->getTimesteps().at(0));
135 Eigen::Matrix4f rootPose = modelPoseSensorMeasurement->getRootPose();
136 Eigen::Vector3f orientation = simox::math::mat4f_to_rpy(rootPose);
137 modelRootTransformation.block<3,3>(0, 0) =
138 simox::math::rpy_to_mat3f(0, 0, -orientation(2));
139 modelRootTransformation.block<3,1>(0, 3) =
140 Eigen::Vector3f(-rootPose(0, 3), -rootPose(1, 3), 0);
141 }
142 }
143 }
144 }
145
146 for (auto motion : *motions)
147 {
148 MotionDataPtr m(new MotionData());
149
150 ModelPtr model = motion->getModel();
151 if (model)
152 {
153 m->modelPath = model->getFilename(); // TODO if model could not be loaded!
154 }
155
156 ModelPoseSensorPtr modelPoseSensor = motion->getSensorByType<ModelPoseSensor>();
157 if (!modelPoseSensor)
158 {
159 ARMARX_ERROR << "Ignoring motion with name '" << motion->getName()
160 << "', because it has no model pose sensor!";
161 break;
162 }
163
164 std::vector<float> t = modelPoseSensor->getTimesteps();
165 m->timestamp = ::Ice::DoubleSeq(t.begin(), t.end());
166 m->numberOfFrames = m->timestamp.size();
167 for (float timestep : m->timestamp)
168 {
169 ModelPoseSensorMeasurementPtr modelPoseSensorMeasurement =
170 modelPoseSensor->getDerivedMeasurement(timestep);
171 Eigen::Matrix4f pose =
172 modelRootTransformation * modelPoseSensorMeasurement->getRootPose();
173 m->poseTrajData[timestep] = getTrajData(pose);
174 }
175
176 KinematicSensorList kinematicSensors = motion->getSensorsByType<KinematicSensor>();
177 for (KinematicSensorPtr kinematicSensor : kinematicSensors)
178 {
179 KinematicSensorPtr kSensor = kinematicSensor;
180 std::vector<std::string> jointNames = kinematicSensor->getJointNames();
181 m->jointNames.insert(m->jointNames.end(), jointNames.begin(), jointNames.end());
182 if (butterworthFilterCutOffFreq > 0.0f)
183 {
184 kSensor = KinematicSensorPtr(new KinematicSensor(jointNames));
185 std::vector<filters::ButterworthFilter> filters;
186 for (size_t j = 0; j < jointNames.size(); j++)
187 {
188 filters.push_back(
189 filters::ButterworthFilter(butterworthFilterCutOffFreq, 100, Lowpass, 5));
190 }
191
192 bool initialized = false;
193 for (float timestep : m->timestamp)
194 {
195 KinematicSensorMeasurementPtr kinematicSensorMeasurement =
196 kinematicSensor->getDerivedMeasurement(timestep); // TODO nullptr
197 if (!kinematicSensorMeasurement)
198 {
199 MMM_ERROR << "No kinematic measurement at timestep " << timestep
200 << " for motion with name '" << motion->getName() << "'";
201 goto end;
202 }
203 Eigen::VectorXf jointAngles = kinematicSensorMeasurement->getJointAngles();
204 for (size_t j = 0; j < jointNames.size(); j++)
205 {
206 if (!initialized)
207 {
208 filters[j].setInitialValue(jointAngles[j]);
209 initialized = true;
210 }
211 filters[j].update(0, new Variant(jointAngles[j]));
212 jointAngles[j] = filters[j].getValue()->getDouble();
213 }
214 kSensor->addSensorMeasurement(KinematicSensorMeasurementPtr(
215 new KinematicSensorMeasurement(timestep, jointAngles)));
216 }
217 }
218
219 for (float timestep : m->timestamp)
220 {
221 KinematicSensorMeasurementPtr kinematicSensorMeasurement =
222 kSensor->getDerivedMeasurement(timestep);
223 if (kinematicSensorMeasurement)
224 {
225 m->jointTrajData.push_back(
226 getTrajData(kinematicSensorMeasurement->getJointAngles()));
227 }
228 else
229 {
230 MMM_ERROR << "No kinematic measurement at timestep " << timestep
231 << " for motion with name '" << motion->getName() << "'";
232 goto end;
233 }
234 }
235 end:
236 continue;
237 }
238 if (model)
239 {
240 m->scaling = model->getScaling();
241 }
242 else
243 {
244 auto modelProcessor =
245 std::dynamic_pointer_cast<MMM::ModelProcessorWinter>(motion->getModelProcessor());
246 if (modelProcessor)
247 {
248 m->scaling = modelProcessor->getHeight();
249 }
250 else
251 {
252 m->scaling = 1.0;
253 }
254 }
255
256 motionNames.push_back(motion->getName());
257 motionData[motion->getName()] = m;
258 }
259 return motionData.size();
260}
261
262bool
263MotionFileWrapper::loadLegacyMotion(const std::string& motionFilePath,
264 const std::string relativeModelRoot)
265{
266 ARMARX_INFO << "Loading legacy motion";
267 LegacyMotionReaderXML reader;
268 LegacyMotionList motions = reader.loadAllMotions(motionFilePath);
269
270 Eigen::Matrix4f modelRootTransformation = Eigen::Matrix4f::Identity();
271 for (auto motion : motions)
272 {
273 ModelPtr model = motion->getModel();
274 std::filesystem::path filename =
275 model ? std::filesystem::path(model->getFilename()) : motion->getModelFilePath();
276 if (std::filesystem::path(filename).stem() == relativeModelRoot)
277 {
278 auto motionFrames = motion->getMotionFrames();
279 if (motionFrames.size() > 0)
280 {
281 Eigen::Matrix4f rootPose = motionFrames.at(0)->getRootPose();
282 Eigen::Vector3f orientation = simox::math::mat4f_to_rpy(rootPose);
283 modelRootTransformation.block<3,3>(0, 0) =
284 simox::math::rpy_to_mat3f(0, 0, -orientation(2));
285 modelRootTransformation.block<3,1>(0, 3) =
286 Eigen::Vector3f(-rootPose(0, 3), -rootPose(1, 3), 0);
287 }
288 }
289 }
290
291 for (auto motion : motions)
292 {
293 MotionDataPtr m(new MotionData());
294 ModelPtr model = motion->getModel();
295 m->modelPath = model ? model->getFilename() : motion->getModelFilePath().c_str();
296 m->numberOfFrames = motion->getNumFrames();
297 m->jointNames = motion->getJointNames();
298
299 if (butterworthFilterCutOffFreq > 0.0f)
300 {
301 for (size_t j = 0; j < motion->getJointNames().size(); j++)
302 {
303 filters::ButterworthFilter filter(butterworthFilterCutOffFreq, 100, Lowpass, 5);
304 filter.setInitialValue(motion->getMotionFrame(0)->joint[j]);
305 for (size_t i = 0; i < motion->getNumFrames(); i++)
306 {
307 filter.update(0, new Variant(motion->getMotionFrame(i)->joint[j]));
308 motion->getMotionFrame(i)->joint[j] = filter.getValue()->getDouble();
309 }
310 }
311 }
312
313
314 for (size_t i = 0; i < motion->getNumFrames(); ++i)
315 {
316 MMM::MotionFramePtr frame = motion->getMotionFrame(i);
317 Eigen::Matrix4f pose = modelRootTransformation * frame->getRootPose();
318 m->poseTrajData[frame->timestep] = getTrajData(pose);
319 m->timestamp.push_back(frame->timestep);
320 m->jointTrajData.push_back(getTrajData(frame->joint));
321 }
322
323 if (model)
324 {
325 m->scaling = model->getScaling();
326 }
327 else
328 {
329 auto modelProcessor =
330 std::dynamic_pointer_cast<MMM::ModelProcessorWinter>(motion->getModelProcessor());
331 if (modelProcessor)
332 {
333 m->scaling = modelProcessor->getHeight();
334 }
335 else
336 {
337 m->scaling = 1.0;
338 }
339 }
340
341 motionNames.push_back(motion->getName());
342 motionData[motion->getName()] = m;
343 }
344 return motionData.size();
345}
346
347::Ice::DoubleSeq
348MotionFileWrapper::getTrajData(const Eigen::Matrix4f& rootPose)
349{
350 VirtualRobot::MathTools::Quaternion q = VirtualRobot::MathTools::eigen4f2quat(rootPose);
351 ::Ice::DoubleSeq curData;
352 curData.push_back(rootPose(0, 3));
353 curData.push_back(rootPose(1, 3));
354 curData.push_back(rootPose(2, 3));
355 curData.push_back(q.w);
356 curData.push_back(q.x);
357 curData.push_back(q.y);
358 curData.push_back(q.z);
359 return curData;
360}
361
362DoubleSeqSeq
363MotionFileWrapper::getTrajData(const Eigen::VectorXf& jointAngles)
364{
365 DoubleSeqSeq curdata;
366 for (int j = 0; j < jointAngles.rows(); j++)
367 {
368 ::Ice::DoubleSeq dimData;
369 dimData.push_back(jointAngles[j]);
370 curdata.push_back(dimData);
371 }
372 return curdata;
373}
374
377{
378 if (motionNames.size() == 0)
379 {
380 return nullptr;
381 }
382 else
383 {
384 return motionData[motionNames.at(0)];
385 }
386}
387
389MotionFileWrapper::getMotionDataByModel(const std::string& modelName)
390{
391 for (auto m : motionData)
392 {
393 if (std::filesystem::path(m.second->modelPath).stem() == modelName)
394 {
395 return m.second;
396 }
397 }
398 return nullptr;
399}
400
402MotionFileWrapper::getMotionData(const std::string& motionName)
403{
404 return motionData[motionName];
405}
406
407::Ice::StringSeq
409{
410 ::Ice::StringSeq modelNames;
411 for (auto m : motionData)
412 {
413 modelNames.push_back(std::filesystem::path(m.second->modelPath).stem());
414 }
415 return modelNames;
416}
417
418::Ice::StringSeq
420{
421 return motionNames;
422}
static MotionFileWrapperPtr create(const std::string &motionFilePath, double butterworthFilterCutOffFreq=0.0, const std::string relativeModelRoot="mmm")
MotionDataPtr getMotionData(const std::string &motionName)
MotionDataPtr getMotionDataByModel(const std::string &modelName)
::Ice::StringSeq getMotionNames()
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define q
const KinematicUnitDatafieldCreator jointAngles("jointAngles")
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< MotionData > MotionDataPtr
std::shared_ptr< MotionFileWrapper > MotionFileWrapperPtr