ImageProviderGodotSimulation.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ArmarXSimulation::components::ImageProviderGodotSimulation
17 * @author Timo Birr
18 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19 * GNU General Public License
20 */
21
23
24#include <cmath>
25#include <cstddef>
26#include <cstdint>
27#include <string>
28
31
32namespace ag = arviz_interactive_project::components::arviz_interactive;
33
34namespace armarx
35{
38 {
41
42 defs->defineOptionalProperty<float>("FrameRate", 30.0f, "Frames per second")
43 .setMin(0.0f)
44 .setMax(60.0f);
45 defs->defineOptionalProperty<visionx::ImageDimension>(
46 "ImageSize", visionx::ImageDimension(640, 480), "Resolution of the rendered image.")
47 .setCaseInsensitive(true)
48 .map("320x240", visionx::ImageDimension(320, 240))
49 .map("640x480", visionx::ImageDimension(640, 480))
50 .map("800x600", visionx::ImageDimension(800, 600))
51 .map("1024x768", visionx::ImageDimension(1024, 768))
52 .map("1280x960", visionx::ImageDimension(1280, 960));
53 defs->defineOptionalProperty<std::string>(
54 "RobotNodeCamera",
55 "EyeLeftCameraSim",
56 "Robot node whose pose is used as the virtual camera.");
57 defs->defineOptionalProperty<std::string>(
58 "ReferenceFrame",
59 "EyeLeftCameraSim",
60 "Reference frame reported for the provided images.");
61 defs->defineOptionalProperty<float>(
62 "focalLength", 600.0f, "Focal length in pixels (used to derive the field of view).");
63 defs->defineOptionalProperty<float>(
64 "NearClip", 0.01f, "Near clipping plane distance in meters.");
65 defs->defineOptionalProperty<float>(
66 "FarClip", 100.0f, "Far clipping plane distance in meters.");
67 defs->defineOptionalProperty<std::string>(
68 "ArVizGodotName", "arviz_godot", "Name of the arviz_godot component to render with.");
69 defs->defineOptionalProperty<std::string>(
70 "RobotStateComponentName",
71 "RobotStateComponent",
72 "Name of the RobotStateComponent providing the robot pose.");
73 defs->defineOptionalProperty<float>(
74 "CameraAlignRollDeg",
75 180.0f,
76 "Roll of the camera-node -> OpenGL-camera alignment (rotation about local X), in "
77 "degrees. Defaults to 180 (camera node +Z optical axis -> Godot -Z forward).");
78 defs->defineOptionalProperty<float>(
79 "CameraAlignPitchDeg", 0.0f, "Pitch of the camera-node alignment (about local Y).");
80 defs->defineOptionalProperty<float>(
81 "CameraAlignYawDeg", 0.0f, "Yaw of the camera-node alignment (about local Z).");
82 defs->defineOptionalProperty<int>(
83 "RenderTimeoutMs", 2000, "Timeout in ms for a single render request to arviz_godot.");
84 defs->defineOptionalProperty<std::string>(
85 "RenderLayers",
86 "",
87 "Comma-separated list of ArViz layers to render. Each entry is either a component "
88 "name (renders all of its layers, e.g. 'ObjectMemory') or a fully-qualified "
89 "'component::layer'. Leave empty to render the whole scene.");
90
91 return defs;
92 }
93
94 void
96 {
97 setImageSyncMode(visionx::eFpsSynchronization);
98 frameRate = getProperty<float>("FrameRate").getValue();
99
100 const visionx::ImageDimension dimensions =
101 getProperty<visionx::ImageDimension>("ImageSize").getValue();
102 renderWidth_ = dimensions.width;
103 renderHeight_ = dimensions.height;
104
105 focalLength_ = getProperty<float>("focalLength").getValue();
106 nearClipMeters_ = getProperty<float>("NearClip").getValue();
107 farClipMeters_ = getProperty<float>("FarClip").getValue();
108 cameraNodeName_ = getProperty<std::string>("RobotNodeCamera").getValue();
109 referenceFrame_ = getProperty<std::string>("ReferenceFrame").getValue();
110 arvizGodotName_ = getProperty<std::string>("ArVizGodotName").getValue();
111 robotStateComponentName_ = getProperty<std::string>("RobotStateComponentName").getValue();
112 renderTimeoutMs_ = getProperty<int>("RenderTimeoutMs").getValue();
113
114 renderLayers_.clear();
115 const std::string renderLayersStr = getProperty<std::string>("RenderLayers").getValue();
116 for (std::size_t start = 0; start < renderLayersStr.size() || start == 0;)
117 {
118 const std::size_t comma = renderLayersStr.find(',', start);
119 std::string token = renderLayersStr.substr(
120 start, comma == std::string::npos ? std::string::npos : comma - start);
121 // Trim surrounding whitespace.
122 const std::size_t first = token.find_first_not_of(" \t");
123 const std::size_t last = token.find_last_not_of(" \t");
124 if (first != std::string::npos)
125 {
126 renderLayers_.push_back(token.substr(first, last - first + 1));
127 }
128 if (comma == std::string::npos)
129 {
130 break;
131 }
132 start = comma + 1;
133 }
134 if (!renderLayers_.empty())
135 {
136 ARMARX_INFO << "Restricting rendering to " << renderLayers_.size()
137 << " ArViz layer selector(s).";
138 }
139
140 const float deg2rad = static_cast<float>(M_PI) / 180.0f;
141 cameraAlignment_ =
142 Eigen::AngleAxisf(getProperty<float>("CameraAlignRollDeg").getValue() * deg2rad,
143 Eigen::Vector3f::UnitX()) *
144 Eigen::AngleAxisf(getProperty<float>("CameraAlignPitchDeg").getValue() * deg2rad,
145 Eigen::Vector3f::UnitY()) *
146 Eigen::AngleAxisf(getProperty<float>("CameraAlignYawDeg").getValue() * deg2rad,
147 Eigen::Vector3f::UnitZ());
148
149 setImageFormat(visionx::ImageDimension(renderWidth_, renderHeight_), visionx::eRgb);
151
152 // arviz_godot must be running (it renders the scene); the RobotStateComponent provides
153 // the camera pose. Declaring them here makes the component wait for them on connect.
154 usingProxy(arvizGodotName_);
155 usingProxy(robotStateComponentName_);
156 }
157
158 void
163
164 bool
165 ImageProviderGodotSimulation::ensureConnected()
166 {
167 try
168 {
169 if (!arvizGodot_)
170 {
171 arvizGodot_ = ag::ArVizInteractiveInterfacePrx::uncheckedCast(
173 ->ice_timeout(renderTimeoutMs_ + 1000));
174 }
175
176 if (!sharedRobot_ || !localRobot_)
177 {
178 auto robotStateComponent =
179 getProxy<RobotStateComponentInterfacePrx>(robotStateComponentName_);
180 sharedRobot_ = robotStateComponent->getSynchronizedRobot();
181 localRobot_ = RemoteRobot::createLocalClone(sharedRobot_);
182
183 if (localRobot_ && !localRobot_->hasRobotNode(cameraNodeName_))
184 {
185 ARMARX_ERROR << "Configured camera node '" << cameraNodeName_
186 << "' is not part of the robot model.";
187 }
188 }
189 }
190 catch (...)
191 {
193 }
194
195 return arvizGodot_ && localRobot_ && sharedRobot_;
196 }
197
198 void
200 {
201 ARMARX_VERBOSE << "start capture";
202 ensureConnected();
203 }
204
205 void
210
211 void
213 {
214 std::unique_lock<std::mutex> lock(captureMutex_);
215 arvizGodot_ = nullptr;
216 sharedRobot_ = nullptr;
217 localRobot_.reset();
218 }
219
220 bool
222 {
223 std::unique_lock<std::mutex> lock(captureMutex_);
224
225 if ((!arvizGodot_ || !localRobot_ || !sharedRobot_) && !ensureConnected())
226 {
227 return false;
228 }
229
230 try
231 {
232 // Update the local robot clone to the current robot state.
233 RemoteRobot::synchronizeLocalClone(localRobot_, sharedRobot_);
234
235 VirtualRobot::RobotNodePtr cameraNode = localRobot_->getRobotNode(cameraNodeName_);
236 if (!cameraNode)
237 {
238 ARMARX_WARNING << deactivateSpam(5) << "Camera node '" << cameraNodeName_
239 << "' not found in robot model.";
240 return false;
241 }
242
243 // Camera node global pose in ArmarX world coordinates (mm).
244 const Eigen::Matrix4f globalPose = cameraNode->getGlobalPose();
245 const Eigen::Matrix3f nodeRotation = globalPose.block<3, 3>(0, 0);
246 const Eigen::Quaternionf cameraRotation =
247 Eigen::Quaternionf(nodeRotation * cameraAlignment_.toRotationMatrix()).normalized();
248
249 ag::CameraRenderPose pose;
250 pose.px = globalPose(0, 3);
251 pose.py = globalPose(1, 3);
252 pose.pz = globalPose(2, 3);
253 pose.qx = cameraRotation.x();
254 pose.qy = cameraRotation.y();
255 pose.qz = cameraRotation.z();
256 pose.qw = cameraRotation.w();
257
258 const float verticalFovRad = 2.0f * std::atan(renderHeight_ / (2.0f * focalLength_));
259
260 const ag::StringSeq layerNames(renderLayers_.begin(), renderLayers_.end());
261 const ag::RenderedFrame frame = arvizGodot_->renderCameraImage(pose,
262 verticalFovRad,
263 renderWidth_,
264 renderHeight_,
265 nearClipMeters_,
266 farClipMeters_,
267 layerNames);
268
269 if (!frame.valid)
270 {
271 ARMARX_WARNING << deactivateSpam(5) << "arviz_godot returned an invalid frame.";
272 return false;
273 }
274
275 if (frame.width != renderWidth_ || frame.height != renderHeight_)
276 {
277 ARMARX_WARNING << deactivateSpam(5) << "Rendered frame size " << frame.width << "x"
278 << frame.height << " differs from expected " << renderWidth_ << "x"
279 << renderHeight_ << ".";
280 return false;
281 }
282
283 const std::size_t pixelCount =
284 static_cast<std::size_t>(renderWidth_) * static_cast<std::size_t>(renderHeight_);
285 if (frame.rgba.size() < pixelCount * 4)
286 {
287 ARMARX_WARNING << deactivateSpam(5) << "Rendered frame buffer too small.";
288 return false;
289 }
290
292 {
293 ARMARX_WARNING << "Shared memory provider is null (possibly shutting down).";
294 return false;
295 }
296
298 auto* dst = reinterpret_cast<std::uint8_t*>(ppImageBuffers[0]);
299 const auto& src = frame.rgba;
300 for (std::size_t i = 0; i < pixelCount; ++i)
301 {
302 dst[i * 3 + 0] = src[i * 4 + 0];
303 dst[i * 3 + 1] = src[i * 4 + 1];
304 dst[i * 3 + 2] = src[i * 4 + 2];
305 }
306 }
307 catch (...)
308 {
310 return false;
311 }
312
313 return true;
314 }
315
316 visionx::MonocularCalibration
318 {
319 visionx::MonocularCalibration calibration;
320 visionx::CameraParameters& params = calibration.cameraParam;
321 params.width = renderWidth_;
322 params.height = renderHeight_;
323 params.principalPoint = {renderWidth_ / 2.0f, renderHeight_ / 2.0f};
324 params.focalLength = {focalLength_, focalLength_};
325 params.distortion = {0.0f, 0.0f, 0.0f, 0.0f};
326 params.rotation = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
327 params.translation = {0.0f, 0.0f, 0.0f};
328 return calibration;
329 }
330
333} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define M_PI
Definition MathTools.h:17
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
A monocular image provider that renders the simulated scene using arviz_godot.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
visionx::MonocularCalibration getCalibration(const Ice::Current &c=Ice::emptyCurrent) override
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
VirtualRobot::RobotPtr createLocalClone()
Clones the structure of this remote robot to a local instance.
static bool synchronizeLocalClone(VirtualRobot::RobotPtr robot, RobotStateComponentInterfacePrx robotStatePrx)
void setImageSyncMode(ImageSyncMode imageSyncMode)
Sets the image synchronization mode.
armarx::SharedMemoryScopedWriteLockPtr getScopedWriteLock()
Retrieve scoped lock for writing to the memory.
armarx::IceSharedMemoryProvider< unsignedchar >::pointer_type sharedMemoryProvider
shared memory provider
void start(const Ice::Current &c) override
void setImageFormat(ImageDimension imageDimension, ImageType imageType, BayerPatternType bayerPatternType=visionx::eBayerPatternRg)
Sets the image basic format data.
void setNumberImages(int numberImages)
Sets the number of images on each capture.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
Quaternion< float, 0 > Quaternionf
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< SharedMemoryScopedWriteLock > SharedMemoryScopedWriteLockPtr
void handleExceptions()
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.