Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Triggered Camera #194

Merged
merged 21 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0ab1b8d
Added test sdf for triggered camera
WilliamLewww Feb 4, 2022
60e7648
Added topic to listen for triggered messages
WilliamLewww Feb 8, 2022
cdbaa18
Added triggered camera functionality
WilliamLewww Feb 8, 2022
dac70f4
Finished triggered camera integration test
WilliamLewww Feb 8, 2022
b17c601
Added code suggestions
WilliamLewww Feb 10, 2022
cdef769
Added use of TriggerTopic SDF
WilliamLewww Feb 28, 2022
55d41c3
Fixed unused parameter warning
WilliamLewww Feb 28, 2022
e0ae9f7
Applied code suggestions
WilliamLewww Mar 11, 2022
e29a80b
Return on invalid trigger topic
WilliamLewww Mar 11, 2022
e0a0f1e
Merge branch 'ign-sensors6' into wlew/camera_trigger
WilliamLewww Mar 15, 2022
b62cfce
Updated sdformat required version
WilliamLewww Mar 15, 2022
3fbeaeb
Merge branch 'wlew/camera_trigger' of github.com:WilliamLewww/ign-sen…
WilliamLewww Mar 15, 2022
a0f9832
Retrigger CI
WilliamLewww Mar 16, 2022
e4bb669
Retrigger CI
WilliamLewww Mar 23, 2022
481cba6
Merge branch 'ign-sensors6' into wlew/camera_trigger
WilliamLewww Mar 23, 2022
62fb8e6
Added code suggestions
WilliamLewww Mar 29, 2022
4039fc4
Merge branch 'ign-sensors6' into wlew/camera_trigger
iche033 Mar 29, 2022
b4dd298
Merge branch 'ign-sensors6' into wlew/camera_trigger
WilliamLewww Mar 29, 2022
eaea644
Only use compatible render engines on test
WilliamLewww Mar 30, 2022
22c3879
Merge branch 'wlew/camera_trigger' of github.com:WilliamLewww/ign-sen…
WilliamLewww Mar 30, 2022
24704be
Merge branch 'ign-sensors6' into wlew/camera_trigger
ahcorde Mar 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ set(IGN_MSGS_VER ${ignition-msgs8_VERSION_MAJOR})

#--------------------------------------
# Find SDFormat
ign_find_package(sdformat12 REQUIRED)
ign_find_package(sdformat12 REQUIRED VERSION 12.4.0)
set(SDF_VER ${sdformat12_VERSION_MAJOR})

set(IGN_SENSORS_PLUGIN_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
Expand Down
55 changes: 55 additions & 0 deletions src/CameraSensor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ using namespace sensors;
/// \brief Private data for CameraSensor
class ignition::sensors::CameraSensorPrivate
{
/// \brief Callback for triggered subscription
/// \param[in] _msg Boolean message
public: void OnTrigger(const ignition::msgs::Boolean &_msg);

/// \brief Save an image
/// \param[in] _data the image data to be saved
/// \param[in] _width width of image in pixels
Expand Down Expand Up @@ -98,6 +102,15 @@ class ignition::sensors::CameraSensorPrivate
/// \brief Just a mutex for thread safety
public: std::mutex mutex;

/// \brief True if camera is triggered by a topic
public: bool isTriggeredCamera = false;

/// \brief True if camera has been triggered by a topic
public: bool isTriggered = false;

/// \brief Topic for camera trigger
public: std::string triggerTopic = "";

/// \brief True to save images
public: bool saveImage = false;

Expand Down Expand Up @@ -281,6 +294,30 @@ bool CameraSensor::Load(const sdf::Sensor &_sdf)
igndbg << "Camera images for [" << this->Name() << "] advertised on ["
<< this->Topic() << "]" << std::endl;

if (_sdf.CameraSensor()->Triggered())
{
if (!_sdf.CameraSensor()->TriggerTopic().empty())
{
this->dataPtr->triggerTopic = _sdf.CameraSensor()->TriggerTopic();
}
else
{
this->dataPtr->triggerTopic =
transport::TopicUtils::AsValidTopic(this->dataPtr->triggerTopic);

if (this->dataPtr->triggerTopic.empty())
{
ignerr << "Invalid trigger topic name" << std::endl;
WilliamLewww marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
}

WilliamLewww marked this conversation as resolved.
Show resolved Hide resolved
this->dataPtr->node.Subscribe(this->dataPtr->triggerTopic,
&CameraSensorPrivate::OnTrigger, this->dataPtr.get());

this->dataPtr->isTriggeredCamera = true;
}

if (!this->AdvertiseInfo())
return false;

Expand Down Expand Up @@ -347,6 +384,12 @@ bool CameraSensor::Update(const std::chrono::steady_clock::duration &_now)
this->dataPtr->camera->SetLocalPose(this->Pose());

// render only if necessary
if (this->dataPtr->isTriggeredCamera &&
!this->dataPtr->isTriggered)
{
return true;
}

if (!this->dataPtr->pub.HasConnections() &&
this->dataPtr->imageEvent.ConnectionCount() <= 0 &&
!this->dataPtr->saveImage)
Expand Down Expand Up @@ -443,9 +486,21 @@ bool CameraSensor::Update(const std::chrono::steady_clock::duration &_now)
this->dataPtr->SaveImage(data, width, height, format);
}

if (this->dataPtr->isTriggeredCamera)
{
return this->dataPtr->isTriggered = false;
}

return true;
}

//////////////////////////////////////////////////
void CameraSensorPrivate::OnTrigger(const ignition::msgs::Boolean &/*_msg*/)
{
std::lock_guard<std::mutex> lock(this->mutex);
this->isTriggered = true;
}

//////////////////////////////////////////////////
bool CameraSensorPrivate::SaveImage(const unsigned char *_data,
unsigned int _width, unsigned int _height,
Expand Down
3 changes: 2 additions & 1 deletion test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ set(dri_tests
distortion_camera.cc
gpu_lidar_sensor.cc
rgbd_camera.cc
thermal_camera.cc
segmentation_camera.cc
thermal_camera.cc
triggered_camera.cc
)

set(tests
Expand Down
176 changes: 176 additions & 0 deletions test/integration/triggered_camera.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright (C) 2022 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 <gtest/gtest.h>

#include <ignition/common/Console.hh>
#include <ignition/common/Filesystem.hh>
#include <ignition/sensors/CameraSensor.hh>
#include <ignition/sensors/Manager.hh>

// TODO(louise) Remove these pragmas once ign-rendering is disabling the
// warnings
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4251)
#endif
#include <ignition/rendering/RenderEngine.hh>
#include <ignition/rendering/RenderingIface.hh>
#include <ignition/rendering/Scene.hh>
#ifdef _WIN32
#pragma warning(pop)
#endif

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4005)
#pragma warning(disable: 4251)
#endif
#include <ignition/msgs.hh>
#ifdef _WIN32
#pragma warning(pop)
#endif

#include "test_config.h" // NOLINT(build/include)
#include "TransportTestTools.hh"

using namespace std::chrono_literals;

class CameraSensorTest: public testing::Test,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this should be renamed to TriggeredCameraTest to distinguish it from the tests in test/integration/camera.cc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #215

public testing::WithParamInterface<const char *>
{
// Documentation inherited
protected: void SetUp() override
{
ignition::common::Console::SetVerbosity(4);
}

// Create a Camera sensor from a SDF and gets a image message
public: void ImagesWithBuiltinSDF(const std::string &_renderEngine);
};

void CameraSensorTest::ImagesWithBuiltinSDF(const std::string &_renderEngine)
{
std::string path = ignition::common::joinPaths(PROJECT_SOURCE_PATH, "test",
"sdf", "triggered_camera_sensor_builtin.sdf");
sdf::SDFPtr doc(new sdf::SDF());
sdf::init(doc);
ASSERT_TRUE(sdf::readFile(path, doc));
ASSERT_NE(nullptr, doc->Root());
ASSERT_TRUE(doc->Root()->HasElement("model"));
auto modelPtr = doc->Root()->GetElement("model");
ASSERT_TRUE(modelPtr->HasElement("link"));
auto linkPtr = modelPtr->GetElement("link");
ASSERT_TRUE(linkPtr->HasElement("sensor"));
auto sensorPtr = linkPtr->GetElement("sensor");

// If ogre is not the engine, don't run the test
if (_renderEngine.compare("ogre") != 0)
{
igndbg << "Engine '" << _renderEngine
<< "' doesn't support segmentation cameras" << std::endl;
return;
}

// Setup ign-rendering with an empty scene
auto *engine = ignition::rendering::engine(_renderEngine);
if (!engine)
{
igndbg << "Engine '" << _renderEngine
<< "' is not supported" << std::endl;
return;
}

ignition::rendering::ScenePtr scene = engine->CreateScene("scene");

ignition::sensors::Manager mgr;

ignition::sensors::CameraSensor *sensor =
mgr.CreateSensor<ignition::sensors::CameraSensor>(sensorPtr);
ASSERT_NE(sensor, nullptr);
sensor->SetScene(scene);

sdf::Sensor sdfSensor;
sdfSensor.Load(sensorPtr);
EXPECT_EQ(true, sdfSensor.CameraSensor()->Triggered());

ASSERT_NE(sensor->RenderingCamera(), nullptr);
EXPECT_NE(sensor->Id(), sensor->RenderingCamera()->Id());
EXPECT_EQ(256u, sensor->ImageWidth());
EXPECT_EQ(257u, sensor->ImageHeight());

// check camera image before trigger
{
std::string imageTopic =
"/test/integration/TriggeredCameraPlugin_imagesWithBuiltinSDF";
WaitForMessageTestHelper<ignition::msgs::Image> helper(imageTopic);
mgr.RunOnce(std::chrono::steady_clock::duration::zero(), true);
EXPECT_FALSE(helper.WaitForMessage(3s)) << helper;
}

// trigger camera through topic
ignition::transport::Node triggerNode;
std::string triggerTopic =
"/test/integration/TriggeredCameraPlugin_imagesWithBuiltinSDF/trigger";

auto pub = triggerNode.Advertise<ignition::msgs::Boolean>(triggerTopic);
ignition::msgs::Boolean msg;
msg.set_data(true);
pub.Publish(msg);

// check camera image after trigger
{
std::string imageTopic =
"/test/integration/TriggeredCameraPlugin_imagesWithBuiltinSDF";
WaitForMessageTestHelper<ignition::msgs::Image> helper(imageTopic);
mgr.RunOnce(std::chrono::steady_clock::duration::zero(), true);
EXPECT_TRUE(helper.WaitForMessage(3s)) << helper;
}

// test removing sensor
// first make sure the sensor objects do exist
auto sensorId = sensor->Id();
auto cameraId = sensor->RenderingCamera()->Id();
EXPECT_EQ(sensor, mgr.Sensor(sensorId));
EXPECT_EQ(sensor->RenderingCamera(), scene->SensorById(cameraId));
// remove and check sensor objects no longer exist in both sensors and
// rendering
EXPECT_TRUE(mgr.Remove(sensorId));
EXPECT_EQ(nullptr, mgr.Sensor(sensorId));
EXPECT_EQ(nullptr, scene->SensorById(cameraId));

// Clean up
engine->DestroyScene(scene);
ignition::rendering::unloadEngine(engine->Name());
}

//////////////////////////////////////////////////
TEST_P(CameraSensorTest, ImagesWithBuiltinSDF)
{
ImagesWithBuiltinSDF(GetParam());
}

INSTANTIATE_TEST_CASE_P(CameraSensor, CameraSensorTest,
RENDER_ENGINE_VALUES, ignition::rendering::PrintToStringParam());

//////////////////////////////////////////////////
int main(int argc, char **argv)
{
ignition::common::Console::SetVerbosity(4);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
23 changes: 23 additions & 0 deletions test/sdf/triggered_camera_sensor_builtin.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<sdf version="1.6">
<model name="m1">
<link name="link1">
<sensor name="camera1" type="camera">
<update_rate>10</update_rate>
<topic>/test/integration/TriggeredCameraPlugin_imagesWithBuiltinSDF</topic>
<camera>
<triggered>true</triggered>
<horizontal_fov>1.05</horizontal_fov>
<image>
<width>256</width>
<height>257</height>
</image>
<clip>
<near>0.1</near>
<far>10.0</far>
</clip>
</camera>
</sensor>
</link>
</model>
</sdf>