pxmlw6n2f/Gazebo_Distributed_TCP/test/integration/visual.cc

594 lines
18 KiB
C++

/*
* Copyright (C) 2017 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 <mutex>
#include <functional>
#include <ignition/math/Rand.hh>
#include "gazebo/physics/physics.hh"
#include "gazebo/sensors/sensors.hh"
#include "gazebo/common/Timer.hh"
#include "gazebo/rendering/Camera.hh"
#include "gazebo/sensors/CameraSensor.hh"
#include "gazebo/test/ServerFixture.hh"
using namespace gazebo;
class VisualProperty : public ServerFixture
{
};
std::mutex mutex;
unsigned char* img = nullptr;
unsigned char* img2 = nullptr;
int imageCount = 0;
int imageCount2 = 0;
/////////////////////////////////////////////////
void OnNewCameraFrame(int* _imageCounter, unsigned char* _imageDest,
const unsigned char *_image,
unsigned int _width, unsigned int _height,
unsigned int _depth,
const std::string &/*_format*/)
{
std::lock_guard<std::mutex> lock(mutex);
memcpy(_imageDest, _image, _width * _height * _depth);
*_imageCounter += 1;
}
/////////////////////////////////////////////////
TEST_F(VisualProperty, CastShadows)
{
Load("worlds/visual_shadows.world");
// Make sure the render engine is available.
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
rendering::RenderEngine::NONE)
{
gzerr << "No rendering engine, unable to run camera test"
<< std::endl;
return;
}
physics::WorldPtr world = physics::get_world();
unsigned int width = 320;
unsigned int height = 240;
double updateRate = 10;
// spawn first camera sensor
std::string modelName = "camera_model";
std::string cameraName = "camera_sensor";
ignition::math::Pose3d testPose(
ignition::math::Vector3d(0, 0, 0.5),
ignition::math::Quaterniond(0, 1.57, 0));
SpawnCamera(modelName, cameraName, testPose.Pos(),
testPose.Rot().Euler(), width, height, updateRate);
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
sensors::CameraSensorPtr camSensor =
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
physics::ModelPtr model = world->GetModel(modelName);
EXPECT_EQ(model->GetWorldPose().Ign(), testPose);
imageCount = 0;
img = new unsigned char[width * height * 3];
event::ConnectionPtr c =
camSensor->Camera()->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
common::Timer timer;
timer.Start();
// wait for images
int totalImages = 20;
while (imageCount < totalImages && timer.GetElapsed().Double() < 5)
common::Time::MSleep(10);
EXPECT_GE(imageCount, totalImages);
camSensor->Camera()->DisconnectNewImageFrame(c);
// spawn second camera sensor
ignition::math::Pose3d testPose2(
ignition::math::Vector3d(0, 10, 0.5),
ignition::math::Quaterniond(0, 1.57, 0));
std::string modelName2 = "camera_model2";
std::string cameraName2 = "camera_sensor2";
SpawnCamera(modelName2, cameraName2, testPose2.Pos(),
testPose2.Rot().Euler(), width, height, updateRate);
sensors::SensorPtr sensor2 = sensors::get_sensor(cameraName2);
sensors::CameraSensorPtr camSensor2 =
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor2);
physics::ModelPtr model2 = world->GetModel(modelName2);
EXPECT_EQ(model2->GetWorldPose().Ign(), testPose2);
imageCount2 = 0;
img2 = new unsigned char[width * height * 3];
event::ConnectionPtr c2 =
camSensor2->Camera()->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
common::Timer timer2;
timer2.Start();
while (imageCount2 < totalImages && timer2.GetElapsed().Double() < 5)
common::Time::MSleep(10);
EXPECT_GE(imageCount2, totalImages);
camSensor2->Camera()->DisconnectNewImageFrame(c2);
unsigned int colorSum = 0;
unsigned int colorSum2 = 0;
for (unsigned int y = 0; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
colorSum += r + g + b;
unsigned int r2 = img2[(y*width*3) + x];
unsigned int g2 = img2[(y*width*3) + x + 1];
unsigned int b2 = img2[(y*width*3) + x + 2];
colorSum2 += r2 + g2 + b2;
}
}
// camera1 image should be darker than camera2 image
// because the mesh below camera1 is casting shadows
EXPECT_LT(colorSum, colorSum2);
double colorRatio = static_cast<double>(colorSum2-colorSum) /
static_cast<double>(colorSum2);
EXPECT_GT(colorRatio, 0.05)
<< " colorSum [" << colorSum << "], "
<< " colorSum2 [" << colorSum2 << "]";
delete [] img;
delete [] img2;
}
bool g_shaderParamSet = false;
/////////////////////////////////////////////////
void SetShaderParam(const std::string &_visualName,
const std::string &_paramName, const std::string &_shaderType,
const std::string &_value)
{
gazebo::rendering::ScenePtr scene = gazebo::rendering::get_scene();
ASSERT_NE(nullptr, scene);
rendering::VisualPtr visual = scene->GetVisual(_visualName);
ASSERT_NE(nullptr, visual);
// change shader param value
visual->SetMaterialShaderParam(_paramName, _shaderType, _value);
g_shaderParamSet = true;
}
/////////////////////////////////////////////////
TEST_F(VisualProperty, MaterialShaderParam)
{
// Load a world with a camera facing a red box
// This test verifies the box visual color can be changed to green by
// setting the `color` uniform parameter exposed by the fragment shader
// The box visual's material and shader files are shader_test.material,
// shader_test_vp.glsl, shader_test_fp.glsl in test/media/materials/scripts
// directory.
Load("worlds/shader_test.world");
// Make sure the render engine is available.
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
rendering::RenderEngine::NONE)
{
gzerr << "No rendering engine, unable to run camera test"
<< std::endl;
return;
}
gazebo::rendering::ScenePtr scene = gazebo::rendering::get_scene();
ASSERT_NE(nullptr, scene);
// 1 camera in scene
rendering::CameraPtr cam = scene->GetCamera(0);
ASSERT_NE(nullptr, cam);
int totalImages = 20;
imageCount = 0;
unsigned int width = cam->ImageWidth();
unsigned int height = cam->ImageHeight();
img = new unsigned char[width * height * 3];
event::ConnectionPtr c =
cam->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
unsigned int sleep = 0;
unsigned int maxSleep = 50;
while (imageCount < totalImages && sleep++ < maxSleep)
common::Time::MSleep(100);
EXPECT_GE(imageCount, totalImages);
cam->DisconnectNewImageFrame(c);
// check initial color
for (unsigned int y = 0; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
EXPECT_EQ(255u, r);
EXPECT_EQ(0u, g);
EXPECT_EQ(0u, b);
}
}
// now set shader material param in rendering thread
g_shaderParamSet = false;
std::string visualName = "box::link::visual";
std::string paramName = "color";
std::string shaderType = "fragment";
std::string value = "0 1 0 1";
// Connect to the render signal
auto c2 =
event::Events::ConnectPreRender(std::bind(&::SetShaderParam,
visualName, paramName, shaderType, value));
// wait for the param to be set
sleep = 0u;
while (!g_shaderParamSet && sleep++ < maxSleep)
common::Time::MSleep(100);
EXPECT_TRUE(g_shaderParamSet);
c2.reset();
// get more images
sleep = 0;
imageCount = 0;
c = cam->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
while (imageCount < totalImages && sleep++ < maxSleep)
common::Time::MSleep(100);
EXPECT_GE(imageCount, totalImages);
cam->DisconnectNewImageFrame(c);
// verify new color has been set
for (unsigned int y = 0; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
EXPECT_EQ(0u, r);
EXPECT_EQ(255u, g);
EXPECT_EQ(0u, b);
}
}
delete [] img;
}
/////////////////////////////////////////////////
// normal map is not work on OSX yet
#ifndef __APPLE__
TEST_F(VisualProperty, NormalMap)
{
// Load a world with two red box visuals: one without normal map and and one
// with normal map. Spawn a camera in front of each visual. Verify the visual
// with normal map is darker than visual without normal map and has irregular
// pattern
Load("worlds/normal_map.world");
// Make sure the render engine is available.
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
rendering::RenderEngine::NONE)
{
gzerr << "No rendering engine, unable to run camera test"
<< std::endl;
return;
}
physics::WorldPtr world = physics::get_world();
ASSERT_TRUE(world != nullptr);
unsigned int width = 320;
unsigned int height = 240;
double updateRate = 10;
// spawn first camera sensor
std::string modelName = "camera_model";
std::string cameraName = "camera_sensor";
ignition::math::Pose3d testPose(
ignition::math::Vector3d(0, -1, 0.5),
ignition::math::Quaterniond(0, 0, 1.57));
SpawnCamera(modelName, cameraName, testPose.Pos(),
testPose.Rot().Euler(), width, height, updateRate);
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
sensors::CameraSensorPtr camSensor =
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
physics::ModelPtr model = world->GetModel(modelName);
EXPECT_EQ(model->GetWorldPose().Ign(), testPose);
imageCount = 0;
img = new unsigned char[width * height * 3];
event::ConnectionPtr c =
camSensor->Camera()->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
common::Timer timer;
timer.Start();
// wait for images
int totalImages = 20;
while (imageCount < totalImages && timer.GetElapsed().Double() < 5)
common::Time::MSleep(10);
EXPECT_GE(imageCount, totalImages);
camSensor->Camera()->DisconnectNewImageFrame(c);
// spawn second camera sensor
ignition::math::Pose3d testPose2(
ignition::math::Vector3d(3, -1, 0.5),
ignition::math::Quaterniond(0, 0, 1.57));
std::string modelName2 = "camera_model2";
std::string cameraName2 = "camera_sensor2";
SpawnCamera(modelName2, cameraName2, testPose2.Pos(),
testPose2.Rot().Euler(), width, height, updateRate);
sensors::SensorPtr sensor2 = sensors::get_sensor(cameraName2);
sensors::CameraSensorPtr camSensor2 =
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor2);
physics::ModelPtr model2 = world->GetModel(modelName2);
EXPECT_EQ(model2->GetWorldPose().Ign(), testPose2);
imageCount2 = 0;
img2 = new unsigned char[width * height * 3];
event::ConnectionPtr c2 =
camSensor2->Camera()->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
common::Timer timer2;
timer2.Start();
while (imageCount2 < totalImages && timer2.GetElapsed().Double() < 5)
common::Time::MSleep(10);
EXPECT_GE(imageCount2, totalImages);
camSensor2->Camera()->DisconnectNewImageFrame(c2);
// check color of visuals with and without normal map
std::set<unsigned int> rSet;
std::set<unsigned int> gSet;
std::set<unsigned int> bSet;
std::set<unsigned int> rSet2;
std::set<unsigned int> gSet2;
std::set<unsigned int> bSet2;
unsigned int colorSum = 0;
unsigned int colorSum2 = 0;
for (unsigned int y = 0; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
// visual without normal map
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
rSet.insert(r);
gSet.insert(g);
bSet.insert(b);
colorSum += r + g + b;
// verify color is predominantly red
EXPECT_GT(r, g);
EXPECT_GT(r, b);
EXPECT_EQ(g, b);
// visual with normal map
unsigned int r2 = img2[(y*width*3) + x];
unsigned int g2 = img2[(y*width*3) + x + 1];
unsigned int b2 = img2[(y*width*3) + x + 2];
rSet2.insert(r2);
gSet2.insert(g2);
bSet2.insert(b2);
colorSum2 += r2+ g2+ b2;
// verify color is predominantly red
EXPECT_GT(r2, g2);
EXPECT_GT(r2, b2);
EXPECT_EQ(g2, b2);
}
}
// verify the rgb components of pixel are somewhat consistent throughout the
// the image
EXPECT_LE(rSet.size(), 3u);
EXPECT_LE(gSet.size(), 3u);
EXPECT_LE(bSet.size(), 3u);
// check that the r component of pixel varies throughout the image for
// visual with normal map. The variation of b and g should still be small
EXPECT_GT(rSet2.size(), 100u);
EXPECT_LE(gSet2.size(), 5u);
EXPECT_LE(bSet2.size(), 5u);
// the visual with normal map should be darker than the visual without
// normal map
EXPECT_GT(colorSum, colorSum2);
delete [] img;
delete [] img2;
}
#endif
/////////////////////////////////////////////////
TEST_F(VisualProperty, VisualMessage)
{
// Load a world with a camera facing a red box
// There was a problem that a duplicate visual object was created if a Visual
// message is sent to the ~/visual topic just after the simulator starts.
// This test verifies the box visual transparency can be correctly changed.
// It first sends messages to make the box invisible. After that, it sends
// messages to change it back to the default.
Load("worlds/shader_test.world");
// Prepare a publisher to update a visual object.
auto world = physics::get_world();
auto node = transport::NodePtr(new transport::Node());
#if GAZEBO_MAJOR_VERSION < 8
node->Init(world->GetName());
#else
node->Init(world->Name());
#endif
auto model = world->GetModel("box");
ASSERT_TRUE(model != nullptr);
auto link = model->GetLink("link");
ASSERT_TRUE(link != nullptr);
auto pubVisual
= node->Advertise<gazebo::msgs::Visual>("~/visual");
pubVisual->WaitForConnection();
msgs::Visual msg;
msg.set_name("box::link::visual");
msg.set_parent_name("box::link");
uint32_t id;
link->VisualId("visual", id);
msg.set_id(id);
// Publish a message to make it transparent at the beginning.
msg.set_transparency(1.0);
pubVisual->Publish(msg);
// Make sure the render engine is available.
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
rendering::RenderEngine::NONE)
{
gzerr << "No rendering engine, unable to run camera test"
<< std::endl;
return;
}
gazebo::rendering::ScenePtr scene = gazebo::rendering::get_scene();
ASSERT_NE(nullptr, scene);
// 1 camera in scene
rendering::CameraPtr cam = scene->GetCamera(0);
ASSERT_NE(nullptr, cam);
int totalImages = 20;
imageCount = 0;
unsigned int width = cam->ImageWidth();
unsigned int height = cam->ImageHeight();
img = new unsigned char[width * height * 3];
event::ConnectionPtr c =
cam->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
unsigned int sleep = 0;
unsigned int maxSleep = 50;
while (imageCount < totalImages && sleep++ < maxSleep)
{
// Keep sending a command.
pubVisual->Publish(msg);
common::Time::MSleep(100);
}
EXPECT_GE(imageCount, totalImages);
cam->DisconnectNewImageFrame(c);
// Now the ground surface, which is not red, should be visible to the camera.
for (unsigned int y = height/2; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
EXPECT_FALSE(r == 255u && g == 0u && b == 0u);
}
}
// Next, publish a message to change the transparency back to the default.
msg.set_transparency(0.0);
pubVisual->Publish(msg);
// get more images
sleep = 0;
imageCount = 0;
c = cam->ConnectNewImageFrame(
std::bind(&::OnNewCameraFrame, &imageCount, img,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5));
while (imageCount < totalImages && sleep++ < maxSleep)
{
// keep sending a command.
pubVisual->Publish(msg);
common::Time::MSleep(100);
}
EXPECT_GE(imageCount, totalImages);
cam->DisconnectNewImageFrame(c);
// verify the object reappeared.
for (unsigned int y = 0; y < height; ++y)
{
for (unsigned int x = 0; x < width*3; x+=3)
{
unsigned int r = img[(y*width*3) + x];
unsigned int g = img[(y*width*3) + x + 1];
unsigned int b = img[(y*width*3) + x + 2];
EXPECT_EQ(255u, r);
EXPECT_EQ(0u, g);
EXPECT_EQ(0u, b);
}
}
delete [] img;
}
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();
}