ElevationMap.cpp
Go to the documentation of this file.
00001 /*+-------------------------------------------------------------------------+
00002   |                       MultiVehicle simulator (libmvsim)                 |
00003   |                                                                         |
00004   | Copyright (C) 2014  Jose Luis Blanco Claraco (University of Almeria)    |
00005   | Copyright (C) 2017  Borys Tymchenko (Odessa Polytechnic University)     |
00006   | Distributed under GNU General Public License version 3                  |
00007   |   See <http://www.gnu.org/licenses/>                                    |
00008   +-------------------------------------------------------------------------+ */
00009 
00010 #include <mvsim/WorldElements/ElevationMap.h>
00011 #include <mvsim/World.h>
00012 #include <mvsim/VehicleBase.h>
00013 #include "xml_utils.h"
00014 
00015 #include <mrpt/opengl/COpenGLScene.h>
00016 #include <mrpt/opengl/CPointCloud.h>
00017 #include <mrpt/tfest.h>  // least-squares methods
00018 #include <rapidxml.hpp>
00019 
00020 using namespace rapidxml;
00021 using namespace mvsim;
00022 using namespace std;
00023 
00024 ElevationMap::ElevationMap(World* parent, const rapidxml::xml_node<char>* root)
00025         : WorldElementBase(parent), m_first_scene_rendering(true), m_resolution(1.0)
00026 {
00027         loadConfigFrom(root);
00028 }
00029 
00030 ElevationMap::~ElevationMap() {}
00031 void ElevationMap::loadConfigFrom(const rapidxml::xml_node<char>* root)
00032 {
00033         // Other general params:
00034         std::map<std::string, TParamEntry> params;
00035 
00036         std::string sElevationImgFile;
00037         params["elevation_image"] = TParamEntry("%s", &sElevationImgFile);
00038         std::string sTextureImgFile;
00039         params["texture_image"] = TParamEntry("%s", &sTextureImgFile);
00040 
00041         double img_min_z = 0.0, img_max_z = 5.0;
00042         params["elevation_image_min_z"] = TParamEntry("%lf", &img_min_z);
00043         params["elevation_image_max_z"] = TParamEntry("%lf", &img_max_z);
00044 
00045         mrpt::utils::TColor mesh_color(0xa0, 0xe0, 0xa0);
00046         params["mesh_color"] = TParamEntry("%color", &mesh_color);
00047 
00048         params["resolution"] = TParamEntry("%lf", &m_resolution);
00049 
00050         parse_xmlnode_children_as_param(*root, params);
00051 
00052         // Load elevation data:
00053         mrpt::math::CMatrixFloat elevation_data;
00054         if (!sElevationImgFile.empty())
00055         {
00056                 mrpt::utils::CImage imgElev;
00057                 if (!imgElev.loadFromFile(
00058                                 sElevationImgFile, 0 /*force load grayscale*/))
00059                         throw std::runtime_error(
00060                                 mrpt::format(
00061                                         "[ElevationMap] ERROR: Cannot read elevation image '%s'",
00062                                         sElevationImgFile.c_str()));
00063 
00064                 // Scale: [0,1] => [min_z,max_z]
00065                 imgElev.getAsMatrix(
00066                         elevation_data);  // Get image normalized in range [0,1]
00067                 ASSERT_(img_min_z != img_max_z)
00068                 elevation_data.adjustRange(img_min_z, img_max_z);
00069         }
00070         else
00071         {
00072                 MRPT_TODO("Imgs or txt matrix")
00073         }
00074 
00075         // Load texture (optional):
00076         mrpt::utils::CImage mesh_image;
00077         bool has_mesh_image = false;
00078         if (!sTextureImgFile.empty())
00079         {
00080                 if (!mesh_image.loadFromFile(sTextureImgFile))
00081                         throw std::runtime_error(
00082                                 mrpt::format(
00083                                         "[ElevationMap] ERROR: Cannot read texture image '%s'",
00084                                         sTextureImgFile.c_str()));
00085                 has_mesh_image = true;
00086         }
00087 
00088         // Build mesh:
00089         m_gl_mesh = mrpt::make_aligned_shared<mrpt::opengl::CMesh>();
00090 
00091         m_gl_mesh->enableTransparency(false);
00092 
00093         if (has_mesh_image)
00094         {
00095                 ASSERT_EQUAL_(mesh_image.getWidth(), (size_t)elevation_data.cols())
00096                 ASSERT_EQUAL_(mesh_image.getHeight(), (size_t)elevation_data.rows())
00097 
00098                 m_gl_mesh->assignImage(mesh_image);
00099                 m_gl_mesh->setZ(elevation_data);
00100         }
00101         else
00102         {
00103                 m_gl_mesh->setZ(elevation_data);
00104                 m_gl_mesh->setColor_u8(mesh_color);
00105         }
00106 
00107         // Save copy for calcs:
00108         m_mesh_z_cache = elevation_data;
00109 
00110         // Extension: X,Y
00111         const double LX = (elevation_data.cols() - 1) * m_resolution;
00112         const double LY = (elevation_data.rows() - 1) * m_resolution;
00113         m_gl_mesh->setGridLimits(-0.5 * LX, 0.5 * LX, -0.5 * LY, 0.5 * LY);
00114 }
00115 
00116 void ElevationMap::gui_update(mrpt::opengl::COpenGLScene& scene)
00117 {
00118         using namespace mrpt::math;
00119 
00120         ASSERTMSG_(
00121                 m_gl_mesh,
00122                 "ERROR: Can't render Mesh before loading it! Have you called "
00123                 "loadConfigFrom() first?")
00124 
00125         // 1st time call?? -> Create objects
00126         if (m_first_scene_rendering)
00127         {
00128                 m_first_scene_rendering = false;
00129                 SCENE_INSERT_Z_ORDER(scene, 0, m_gl_mesh);
00130         }
00131 }
00132 
00133 void ElevationMap::simul_pre_timestep(const TSimulContext& context)
00134 {
00135         // For each vehicle:
00136         // 1) Compute its 3D pose according to the mesh tilt angle.
00137         // 2) Apply gravity force
00138         const double gravity = getWorldObject()->get_gravity();
00139 
00140         ASSERT_(m_gl_mesh)
00141         // const mrpt::opengl::CMesh * mesh = m_gl_mesh.pointer();
00142 
00143         const World::TListVehicles& lstVehs = this->m_world->getListOfVehicles();
00144         for (World::TListVehicles::const_iterator itVeh = lstVehs.begin();
00145                  itVeh != lstVehs.end(); ++itVeh)
00146         {
00147                 m_world->getTimeLogger().enter("elevationmap.handle_vehicle");
00148 
00149                 const size_t nWheels = itVeh->second->getNumWheels();
00150 
00151                 // 1) Compute its 3D pose according to the mesh tilt angle.
00152                 // Idea: run a least-squares method to find the best
00153                 // SE(3) transformation that map the wheels contact point,
00154                 // as seen in local & global coordinates.
00155                 // (For large tilt angles, may have to run it iteratively...)
00156                 // -------------------------------------------------------------
00157                 mrpt::math::TPoint3D dir_down;  // the final downwards direction (unit
00158                                                                                 // vector (0,0,-1)) as seen in vehicle
00159                                                                                 // local frame.
00160                 for (int iter = 0; iter < 3; iter++)
00161                 {
00162                         const mrpt::math::TPose3D& cur_pose = itVeh->second->getPose();
00163                         const mrpt::poses::CPose3D cur_cpose(cur_pose);  // This object is
00164                                                                                                                          // faster for
00165                                                                                                                          // repeated point
00166                                                                                                                          // projections
00167 
00168                         mrpt::math::TPose3D new_pose = cur_pose;
00169 
00170                         // See mrpt::scanmatching::leastSquareErrorRigidTransformation6D()
00171                         // docs
00172                         corrs.clear();
00173 
00174                         bool out_of_area = false;
00175                         for (size_t iW = 0; !out_of_area && iW < nWheels; iW++)
00176                         {
00177                                 const Wheel& wheel = itVeh->second->getWheelInfo(iW);
00178 
00179                                 // Local frame
00180                                 mrpt::utils::TMatchingPair corr;
00181 
00182                                 corr.other_idx = iW;
00183                                 corr.other_x = wheel.x;
00184                                 corr.other_y = wheel.y;
00185                                 corr.other_z = 0;
00186 
00187                                 // Global frame
00188                                 mrpt::math::TPoint3D gPt;
00189                                 cur_cpose.composePoint(
00190                                         wheel.x, wheel.y, 0.0, gPt.x, gPt.y, gPt.z);
00191                                 float z;
00192                                 if (!getElevationAt(gPt.x /*in*/, gPt.y /*in*/, z /*out*/))
00193                                 {
00194                                         out_of_area = true;
00195                                         continue;  // vehicle is out of bounds!
00196                                 }
00197 
00198                                 corr.this_idx = iW;
00199                                 corr.this_x = gPt.x;
00200                                 corr.this_y = gPt.y;
00201                                 corr.this_z = z;
00202 
00203                                 corrs.push_back(corr);
00204                         }
00205                         if (out_of_area) continue;
00206 
00207                         // Register:
00208                         double transf_scale;
00209                         mrpt::poses::CPose3DQuat tmpl;
00210 
00211                         mrpt::tfest::se3_l2(
00212                                 corrs, tmpl, transf_scale, true /*force scale unity*/);
00213 
00214                         m_optimal_transf = mrpt::poses::CPose3D(tmpl);
00215                         new_pose.z = m_optimal_transf.z();
00216                         new_pose.yaw = m_optimal_transf.yaw();
00217                         new_pose.pitch = m_optimal_transf.pitch();
00218                         new_pose.roll = m_optimal_transf.roll();
00219 
00220                         // cout << new_pose << endl;
00221 
00222                         itVeh->second->setPose(new_pose);
00223 
00224                 }  // end iters
00225 
00226                 {
00227                         mrpt::poses::CPose3D rot_only;
00228                         rot_only.setRotationMatrix(m_optimal_transf.getRotationMatrix());
00229                         rot_only.inverseComposePoint(
00230                                 .0, .0, -1.0, dir_down.x, dir_down.y, dir_down.z);
00231                 }
00232 
00233                 // 2) Apply gravity force
00234                 // -------------------------------------------------------------
00235                 {
00236                         // To chassis:
00237                         const double chassis_weight =
00238                                 itVeh->second->getChassisMass() * gravity;
00239                         const mrpt::math::TPoint2D chassis_com =
00240                                 itVeh->second->getChassisCenterOfMass();
00241                         itVeh->second->apply_force(
00242                                 dir_down.x * chassis_weight, dir_down.y * chassis_weight,
00243                                 chassis_com.x, chassis_com.y);
00244 
00245                         // To wheels:
00246                         for (size_t iW = 0; iW < nWheels; iW++)
00247                         {
00248                                 const Wheel& wheel = itVeh->second->getWheelInfo(iW);
00249                                 const double wheel_weight = wheel.mass * gravity;
00250                                 itVeh->second->apply_force(
00251                                         dir_down.x * wheel_weight, dir_down.y * wheel_weight,
00252                                         wheel.x, wheel.y);
00253                         }
00254                 }
00255 
00256                 m_world->getTimeLogger().leave("elevationmap.handle_vehicle");
00257         }
00258 }
00259 
00260 void ElevationMap::simul_post_timestep(const TSimulContext& context)
00261 {
00262         MRPT_TODO(
00263                 "Save all elements positions in prestep, then here scale their "
00264                 "movements * cos(angle)")
00265 }
00266 float calcz(
00267         const mrpt::math::TPoint3Df& p1, const mrpt::math::TPoint3Df& p2,
00268         const mrpt::math::TPoint3Df& p3, float x, float y)
00269 {
00270         const float det =
00271                 (p2.x - p3.x) * (p1.y - p3.y) + (p3.y - p2.y) * (p1.x - p3.x);
00272         ASSERT_(det != 0.0f)
00273 
00274         const float l1 =
00275                 ((p2.x - p3.x) * (y - p3.y) + (p3.y - p2.y) * (x - p3.x)) / det;
00276         const float l2 =
00277                 ((p3.x - p1.x) * (y - p3.y) + (p1.y - p3.y) * (x - p3.x)) / det;
00278         const float l3 = 1.0f - l1 - l2;
00279 
00280         return l1 * p1.z + l2 * p2.z + l3 * p3.z;
00281 }
00282 
00283 bool ElevationMap::getElevationAt(double x, double y, float& z) const
00284 {
00285         const mrpt::opengl::CMesh* mesh = m_gl_mesh.get();
00286         const float x0 = mesh->getXMin();
00287         const float y0 = mesh->getYMin();
00288         const size_t nCellsX = m_mesh_z_cache.rows();
00289         const size_t nCellsY = m_mesh_z_cache.cols();
00290 
00291         // Discretize:
00292         const int cx00 = ::floor((x - x0) / m_resolution);
00293         const int cy00 = ::floor((y - y0) / m_resolution);
00294         if (cx00 < 1 || cx00 >= int(nCellsX - 1) || cy00 < 1 ||
00295                 cy00 >= int(nCellsY - 1))
00296                 return false;
00297 
00298         // Linear interpolation:
00299         const float z00 = m_mesh_z_cache(cx00, cy00);
00300         const float z01 = m_mesh_z_cache(cx00, cy00 + 1);
00301         const float z10 = m_mesh_z_cache(cx00 + 1, cy00);
00302         const float z11 = m_mesh_z_cache(cx00 + 1, cy00 + 1);
00303 
00304         //
00305         //   p01 ---- p11
00306         //    |        |
00307         //   p00 ---- p10
00308         //
00309         const mrpt::math::TPoint3Df p00(.0f, .0f, z00);
00310         const mrpt::math::TPoint3Df p01(.0f, m_resolution, z01);
00311         const mrpt::math::TPoint3Df p10(m_resolution, .0f, z10);
00312         const mrpt::math::TPoint3Df p11(m_resolution, m_resolution, z11);
00313 
00314         const float lx = x - (x0 + cx00 * m_resolution);
00315         const float ly = y - (y0 + cy00 * m_resolution);
00316 
00317         if (ly >= lx)
00318                 z = calcz(p00, p01, p11, lx, ly);
00319         else
00320                 z = calcz(p00, p10, p11, lx, ly);
00321 
00322         return true;
00323 }


mvsim
Author(s):
autogenerated on Thu Sep 7 2017 09:27:48