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 
46 using namespace armarx;
47 using namespace MMM;
48 
50 MotionFileWrapper::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 
99 MotionFileWrapper::MotionFileWrapper(int butterworthFilterCutOffFreq) :
100  butterworthFilterCutOffFreq(butterworthFilterCutOffFreq)
101 {
102 }
103 
104 bool
105 MotionFileWrapper::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 
262 bool
263 MotionFileWrapper::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
348 MotionFileWrapper::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 
362 DoubleSeqSeq
363 MotionFileWrapper::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 
389 MotionFileWrapper::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 
402 MotionFileWrapper::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 }
armarx::Variant
The Variant class is described here: Variants.
Definition: Variant.h:223
armarx::MotionFileWrapperPtr
std::shared_ptr< MotionFileWrapper > MotionFileWrapperPtr
Definition: MotionFileWrapper.h:76
GfxTL::Matrix4f
MatrixXX< 4, 4, float > Matrix4f
Definition: MatrixXX.h:650
ButterworthFilter.h
armarx::VariantType::Quaternion
const VariantTypeId Quaternion
Definition: Pose.h:39
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:570
armarx::MotionFileWrapper::getMotionDataByModel
MotionDataPtr getMotionDataByModel(const std::string &modelName)
Definition: MotionFileWrapper.cpp:389
armarx::MotionDataPtr
std::shared_ptr< MotionData > MotionDataPtr
Definition: MotionFileWrapper.h:74
armarx::filters::ButterworthFilter
Definition: ButterworthFilter.h:40
armarx::MotionFileWrapper::getMotionData
MotionDataPtr getMotionData(const std::string &motionName)
Definition: MotionFileWrapper.cpp:402
armarx::MotionFileWrapper
Definition: MotionFileWrapper.h:80
filename
std::string filename
Definition: VisualizationRobot.cpp:86
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:196
q
#define q
armarx::channels::KinematicUnitObserver::jointAngles
const KinematicUnitDatafieldCreator jointAngles("jointAngles")
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
MotionFileWrapper.h
armarx::MotionFileWrapper::getFirstMotionData
MotionDataPtr getFirstMotionData()
Definition: MotionFileWrapper.cpp:376
Logging.h
armarx::MotionFileWrapper::getModelNames
::Ice::StringSeq getModelNames()
Definition: MotionFileWrapper.cpp:408
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::MotionFileWrapper::getMotionNames
::Ice::StringSeq getMotionNames()
Definition: MotionFileWrapper.cpp:419
armarx::MotionFileWrapper::create
static MotionFileWrapperPtr create(const std::string &motionFilePath, double butterworthFilterCutOffFreq=0.0, const std::string relativeModelRoot="mmm")
Definition: MotionFileWrapper.cpp:50
armarx::MotionData
Definition: MotionFileWrapper.h:32