pxmlw6n2f/Gazebo_Distributed/test/integration/laser.cc

608 lines
18 KiB
C++

/*
* Copyright (C) 2012 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <ignition/math/Rand.hh>
#include "gazebo/test/ServerFixture.hh"
#include "gazebo/physics/physics.hh"
#include "gazebo/sensors/sensors.hh"
#include "gazebo/common/common.hh"
#include "scans_cmp.h"
#include "gazebo/test/helper_physics_generator.hh"
#define LASER_TOL 1e-5
#define DOUBLE_TOL 1e-6
using namespace gazebo;
class LaserTest : public ServerFixture,
public testing::WithParamInterface<const char*>
{
public: void Stationary_EmptyWorld(const std::string &_physicsEngine);
public: void GroundPlane(const std::string &_physicsEngine);
public: void LaserUnitBox(const std::string &_physicsEngine);
public: void LaserUnitNoise(const std::string &_physicsEngine);
public: void LaserVertical(const std::string &_physicsEngine);
public: void LaserScanResolution(const std::string &_physicsEngine);
};
void LaserTest::Stationary_EmptyWorld(const std::string &_physicsEngine)
{
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
double hMinAngle = -2.27;
double hMaxAngle = 2.27;
double minRange = 0.0;
double maxRange = 10.0;
double rangeResolution = 0.01;
unsigned int samples = 640;
math::Pose testPose(math::Vector3(0, 0, 0.5),
math::Quaternion(0, 0, 0));
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, 0, 0,
minRange, maxRange, rangeResolution, samples, 1, 1, 1);
sensors::RaySensorPtr laser =
std::static_pointer_cast<sensors::RaySensor>(
sensors::SensorManager::Instance()->GetSensor(raySensorName));
ASSERT_TRUE(laser != NULL);
laser->Init();
laser->Update(true);
EXPECT_EQ(640, laser->RayCount());
EXPECT_EQ(640, laser->RangeCount());
EXPECT_NEAR(laser->AngleMin().Radian(), -2.27, DOUBLE_TOL);
EXPECT_NEAR(laser->AngleMax().Radian(), 2.27, DOUBLE_TOL);
EXPECT_NEAR(laser->RangeMin(), 0, DOUBLE_TOL);
EXPECT_NEAR(laser->RangeMax(), 10, DOUBLE_TOL);
EXPECT_NEAR(laser->RangeResolution(), 0.01, DOUBLE_TOL);
for (int i = 0; i < laser->RangeCount(); ++i)
{
EXPECT_DOUBLE_EQ(GZ_DBL_INF, laser->Range(i));
}
// Spawn a box and test for proper laser scan
{
SpawnBox("test_box", math::Vector3(1, 1, 1),
math::Vector3(2, 0, 0.5), math::Vector3(0, 0, 0));
common::Time::MSleep(1000);
laser->Update(true);
std::vector<double> scan;
laser->Ranges(scan);
// run test against pre-recorded range data only in ode
if (_physicsEngine == "ode")
{
double diffMax, diffSum, diffAvg;
DoubleCompare(box_scan, &scan[0], 640, diffMax, diffSum, diffAvg);
EXPECT_LT(diffMax, 2e-6);
EXPECT_LT(diffSum, 1e-4);
EXPECT_LT(diffAvg, 2e-6);
}
// This line will print the current scan. Use this to generate
// a new test scan sample
// PrintScan("box_scan", &scan[0], 640);
}
// Move the laser to point down on the ground plane,
{
common::Time prevTime;
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
physics::ModelPtr model = world->GetModel(modelName);
prevTime = laser->LastUpdateTime();
model->SetWorldPose(math::Pose(0, 0, 1.0, 0, M_PI*0.5, 0));
double diffMax, diffSum, diffAvg;
std::vector<double> scan, scan2;
laser->Update(false);
for (unsigned int j = 0; j < 5; ++j)
{
laser->Update(true);
laser->Ranges(scan);
laser->Update(true);
laser->Ranges(scan2);
DoubleCompare(&scan[0], &scan2[0], 640, diffMax, diffSum, diffAvg);
EXPECT_LT(diffMax, 1e-6);
EXPECT_LT(diffSum, 1e-6);
EXPECT_LT(diffAvg, 1e-6);
}
laser->Update(true);
// run test against pre-recorded range data only in ode
if (_physicsEngine == "ode")
{
DoubleCompare(plane_scan, &scan[0], 640, diffMax, diffSum, diffAvg);
EXPECT_LT(diffMax, 1e-6);
EXPECT_LT(diffSum, 1e-6);
EXPECT_LT(diffAvg, 1e-6);
}
// This line will print the current scan. Use this to generate
// a new test scan sample
// PrintScan("plane_scan", &scan[0], 640);
}
}
TEST_P(LaserTest, EmptyWorld)
{
Stationary_EmptyWorld(GetParam());
}
void LaserTest::LaserUnitBox(const std::string &_physicsEngine)
{
if (_physicsEngine == "simbody")
{
gzerr << "Abort test since simbody does not support ray sensor, "
<< "Please see issue #867.\n";
return;
}
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape and sensor, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
// Test ray sensor with 3 boxes in the world.
// First place 2 of 3 boxes within range and verify range values, one of them
// being a static model to verify collision filtering is working,
// then move all 3 boxes out of range and verify range values
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
double hMinAngle = -M_PI/2.0;
double hMaxAngle = M_PI/2.0;
double minRange = 0.1;
double maxRange = 5.0;
double rangeResolution = 0.02;
unsigned int samples = 320;
math::Pose testPose(math::Vector3(0, 0, 0),
math::Quaternion(0, 0, 0));
if (_physicsEngine == "bullet" && LIBBULLET_VERSION >= 2.82)
{
testPose.pos.z = 0.1;
gzwarn << "Raising sensor for bullet as workaround for #934" << std::endl;
}
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, 0, 0, minRange, maxRange,
rangeResolution, samples, 1, 1, 1);
std::string box01 = "box_01";
std::string box02 = "box_02";
std::string box03 = "box_03";
// box in front of ray sensor
math::Pose box01Pose(math::Vector3(1, 0, 0.5), math::Quaternion(0, 0, 0));
// box on the right of ray sensor
math::Pose box02Pose(math::Vector3(0, -1, 0.5), math::Quaternion(0, 0, 0));
// box on the left of the ray sensor but out of range
math::Pose box03Pose(math::Vector3(0, maxRange + 1, 0.5),
math::Quaternion(0, 0, 0));
SpawnBox(box01, math::Vector3(1, 1, 1), box01Pose.pos,
box01Pose.rot.GetAsEuler());
// box02 is static
SpawnBox(box02, math::Vector3(1, 1, 1), box02Pose.pos,
box02Pose.rot.GetAsEuler(), true);
SpawnBox(box03, math::Vector3(1, 1, 1), box03Pose.pos,
box03Pose.rot.GetAsEuler());
sensors::SensorPtr sensor = sensors::get_sensor(raySensorName);
sensors::RaySensorPtr raySensor =
std::dynamic_pointer_cast<sensors::RaySensor>(sensor);
raySensor->Init();
raySensor->Update(true);
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
EXPECT_TRUE(world->GetModel(box02)->IsStatic());
int mid = samples / 2;
double unitBoxSize = 1.0;
double expectedRangeAtMidPoint = box01Pose.pos.x - unitBoxSize/2;
EXPECT_NEAR(raySensor->Range(mid), expectedRangeAtMidPoint, LASER_TOL);
EXPECT_NEAR(raySensor->Range(0), expectedRangeAtMidPoint, LASER_TOL);
EXPECT_DOUBLE_EQ(raySensor->Range(samples-1), GZ_DBL_INF);
// Move all boxes out of range
world->GetModel(box01)->SetWorldPose(
math::Pose(math::Vector3(maxRange + 1, 0, 0), math::Quaternion(0, 0, 0)));
world->GetModel(box02)->SetWorldPose(
math::Pose(math::Vector3(0, -(maxRange + 1), 0),
math::Quaternion(0, 0, 0)));
world->Step(1);
raySensor->Update(true);
for (int i = 0; i < raySensor->RayCount(); ++i)
{
EXPECT_DOUBLE_EQ(raySensor->Range(i), GZ_DBL_INF);
}
}
TEST_P(LaserTest, LaserBox)
{
LaserUnitBox(GetParam());
}
void LaserTest::LaserVertical(const std::string &_physicsEngine)
{
if (_physicsEngine == "simbody")
{
gzerr << "Abort test since simbody does not support ray sensor, "
<< "Please see issue #867.\n";
return;
}
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape and sensor, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
// Test a ray sensor that has a vertical range component.
// Place a box within range and verify range values,
// then move the box out of range and verify range values
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
double hMinAngle = -M_PI/2.0;
double hMaxAngle = M_PI/2.0;
double vMinAngle = -0.1;
double vMaxAngle = 0.1;
double minRange = 0.0;
double maxRange = 5.0;
double rangeResolution = 0.02;
unsigned int samples = 640;
unsigned int vSamples = 3;
double vAngleStep = (vMaxAngle - vMinAngle) / (vSamples-1);
math::Pose testPose(math::Vector3(0.25, 0, 0.5),
math::Quaternion(0, 0, 0));
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, vMinAngle, vMaxAngle,
minRange, maxRange, rangeResolution, samples, vSamples, 1, 1);
std::string box01 = "box_01";
// box in front of ray sensor
math::Pose box01Pose(math::Vector3(1, 0, 0.5), math::Quaternion(0, 0, 0));
SpawnBox(box01, math::Vector3(1, 1, 1), box01Pose.pos,
box01Pose.rot.GetAsEuler());
sensors::SensorPtr sensor = sensors::get_sensor(raySensorName);
sensors::RaySensorPtr raySensor =
std::dynamic_pointer_cast<sensors::RaySensor>(sensor);
raySensor->Init();
raySensor->Update(true);
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
unsigned int mid = samples / 2;
double unitBoxSize = 1.0;
double angleStep = vMinAngle;
// all vertical laser planes should sense box
for (unsigned int i = 0; i < vSamples; ++i)
{
double expectedRangeAtMidPoint = box01Pose.pos.x - unitBoxSize/2
- testPose.pos.x;
expectedRangeAtMidPoint = expectedRangeAtMidPoint / cos(angleStep);
EXPECT_NEAR(raySensor->Range(i*samples + mid),
expectedRangeAtMidPoint, LASER_TOL);
angleStep += vAngleStep;
EXPECT_DOUBLE_EQ(raySensor->Range(i*samples), GZ_DBL_INF);
EXPECT_DOUBLE_EQ(raySensor->Range(i*samples + samples-1), GZ_DBL_INF);
}
// Move box out of range
world->GetModel(box01)->SetWorldPose(
math::Pose(math::Vector3(maxRange + 1, 0, 0), math::Quaternion(0, 0, 0)));
world->Step(1);
raySensor->Update(true);
for (int j = 0; j < raySensor->VerticalRayCount(); ++j)
{
for (int i = 0; i < raySensor->RayCount(); ++i)
{
EXPECT_DOUBLE_EQ(raySensor->Range(j*raySensor->RayCount() + i),
GZ_DBL_INF);
}
}
}
TEST_P(LaserTest, LaserVertical)
{
LaserVertical(GetParam());
}
void LaserTest::LaserScanResolution(const std::string &_physicsEngine)
{
if (_physicsEngine == "simbody")
{
gzerr << "Abort test since simbody does not support ray sensor, "
<< "Please see issue #867.\n";
return;
}
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape and sensor, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
// Test ray sensor scan resolution.
// Orient the sensor to face downwards and verify that the interpolated
// range values all intersect with ground plane at z = 0;
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
// use asymmetric horizontal angles to make test more difficult
double hMinAngle = -M_PI/4.0;
double hMaxAngle = M_PI/8.0;
double vMinAngle = -0.1;
double vMaxAngle = 0.1;
double vMidAngle = M_PI/2.0;
double minRange = 0.0;
double maxRange = 5.0;
double rangeResolution = 0.02;
unsigned int hSamples = 641;
unsigned int vSamples = 5;
double hResolution = 3;
double vResolution = 2;
double hAngleStep = (hMaxAngle - hMinAngle) / (hSamples*hResolution-1);
double vAngleStep = (vMaxAngle - vMinAngle) / (vSamples*vResolution-1);
double z0 = 0.5;
math::Pose testPose(math::Vector3(0.25, 0, z0),
math::Quaternion(0, vMidAngle, 0));
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, vMinAngle, vMaxAngle,
minRange, maxRange, rangeResolution, hSamples, vSamples,
hResolution, vResolution);
sensors::SensorPtr sensor = sensors::get_sensor(raySensorName);
sensors::RaySensorPtr raySensor =
std::dynamic_pointer_cast<sensors::RaySensor>(sensor);
raySensor->Init();
raySensor->Update(true);
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
unsigned int h, v;
for (v = 0; v < vSamples*vResolution; ++v)
{
for (h = 0; h < hSamples*hResolution; ++h)
{
// pitch angle
double p = vMinAngle + v*vAngleStep;
// yaw angle
double y = hMinAngle + h*hAngleStep;
double R = raySensor->Range(v*hSamples*hResolution + h);
math::Quaternion rot(0.0, -p, y);
math::Vector3 axis = testPose.rot * rot * math::Vector3::UnitX;
math::Vector3 intersection = (axis * R) + testPose.pos;
EXPECT_NEAR(intersection.z, 0.0, rangeResolution);
}
}
}
TEST_P(LaserTest, LaserScanResolution)
{
LaserScanResolution(GetParam());
}
void LaserTest::GroundPlane(const std::string &_physicsEngine)
{
if (_physicsEngine == "simbody")
{
gzerr << "Abort test since simbody does not support ray sensor, "
<< "Please see issue #867.\n";
return;
}
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape and sensor, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
// Test a ray sensor that has a vertical range component.
// Aim the sensor toward the ground and verify correct ranges.
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
// use asymmetric horizontal angles to make test more difficult
double hMinAngle = -M_PI/4.0;
double hMaxAngle = M_PI/8.0;
double vMinAngle = -0.1;
double vMaxAngle = 0.1;
double vMidAngle = 0.3;
double minRange = 0.0;
double maxRange = 5.0;
double rangeResolution = 0.02;
unsigned int hSamples = 641;
unsigned int vSamples = 5;
double hAngleStep = (hMaxAngle - hMinAngle) / (hSamples-1);
double vAngleStep = (vMaxAngle - vMinAngle) / (vSamples-1);
double z0 = 0.5;
math::Pose testPose(math::Vector3(0.25, 0, z0),
math::Quaternion(0, vMidAngle, 0));
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, vMinAngle, vMaxAngle,
minRange, maxRange, rangeResolution, hSamples, vSamples, 1, 1);
sensors::SensorPtr sensor = sensors::get_sensor(raySensorName);
sensors::RaySensorPtr raySensor =
std::dynamic_pointer_cast<sensors::RaySensor>(sensor);
raySensor->Init();
raySensor->Update(true);
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
unsigned int h, v;
for (v = 0; v < vSamples; ++v)
{
for (h = 0; h < hSamples; ++h)
{
// pitch angle
double p = vMinAngle + v*vAngleStep;
// yaw angle
double y = hMinAngle + h*hAngleStep;
double R = raySensor->Range(v*hSamples + h);
math::Quaternion rot(0.0, -p, y);
math::Vector3 axis = testPose.rot * rot * math::Vector3::UnitX;
math::Vector3 intersection = (axis * R) + testPose.pos;
EXPECT_NEAR(intersection.z, 0.0, rangeResolution);
}
}
}
TEST_P(LaserTest, GroundPlane)
{
GroundPlane(GetParam());
}
void LaserTest::LaserUnitNoise(const std::string &_physicsEngine)
{
if (_physicsEngine == "dart")
{
gzerr << "Abort test since dart does not support ray shape and sensor, "
<< "Please see issue #911. "
<< "(https://bitbucket.org/osrf/gazebo/issue/911).\n";
return;
}
// Test ray sensor with noise applied
Load("worlds/empty.world", true, _physicsEngine);
std::string modelName = "ray_model";
std::string raySensorName = "ray_sensor";
double hMinAngle = -M_PI/2.0;
double hMaxAngle = M_PI/2.0;
double minRange = 0.0;
double maxRange = 5.0;
double rangeResolution = 0.02;
unsigned int samples = 320;
std::string noiseType = "gaussian";
// Give negative bias so that we can see the effect (positive bias
// would be removed by clamp(minRange,maxRange).
double noiseMean = -1.0;
double noiseStdDev = 0.01;
math::Pose testPose(math::Vector3(0, 0, 0),
math::Quaternion(0, 0, 0));
SpawnRaySensor(modelName, raySensorName, testPose.pos,
testPose.rot.GetAsEuler(), hMinAngle, hMaxAngle, 0, 0,
minRange, maxRange, rangeResolution, samples, 1, 1, 1,
noiseType, noiseMean, noiseStdDev);
sensors::SensorPtr sensor = sensors::get_sensor(raySensorName);
sensors::RaySensorPtr raySensor =
std::dynamic_pointer_cast<sensors::RaySensor>(sensor);
EXPECT_TRUE(raySensor != NULL);
raySensor->Init();
raySensor->Update(true);
physics::WorldPtr world = physics::get_world("default");
ASSERT_TRUE(world != NULL);
bool foundNoise = false;
for (int i = 0; i < raySensor->RayCount(); ++i)
{
if (fabs(raySensor->Range(i) - maxRange) > LASER_TOL)
foundNoise = true;
}
EXPECT_TRUE(foundNoise);
}
TEST_P(LaserTest, LaserNoise)
{
LaserUnitNoise(GetParam());
}
INSTANTIATE_TEST_CASE_P(PhysicsEngines, LaserTest, PHYSICS_ENGINE_VALUES);
int main(int argc, char **argv)
{
// Set a specific seed to avoid occasional test failures due to
// statistically unlikely, but possible results.
ignition::math::Rand::Seed(42);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}