virtual_object_handler.cpp
Go to the documentation of this file.
2 
4 
9 
12 
14 
15 #include <fstream>
16 #include <sstream>
17 #include <stdexcept>
18 
19 #include "nxLib.h"
20 
21 using namespace ensenso_camera;
22 
23 namespace
24 {
25 std::string readFile(const std::string& filename)
26 {
27  std::ifstream file{ filename };
28  if (!file.is_open() || !file.rdbuf())
29  {
30  throw std::runtime_error("Unable to read objects file");
31  }
32 
33  std::stringstream buffer;
34  buffer << file.rdbuf();
35  return buffer.str();
36 }
37 
39 struct VirtualObjectMarkerPublisher
40 {
41  VirtualObjectMarkerPublisher(ensenso::ros::NodeHandle _nh, const std::string& topic, double publishRate,
42  const NxLibItem& objects, const std::string& frame, std::atomic_bool& stop_)
43  : nh(std::move(_nh)), rate(publishRate), stop(stop_)
44  {
45  // Create output topic
46  publisher = ensenso::ros::create_publisher<visualization_msgs::msg::MarkerArray>(nh, topic, 1);
47 
48  // Collect markers
49  for (int i = 0; i < objects.count(); ++i)
50  {
51  auto& object = objects[i];
52 
53  visualization_msgs::msg::Marker marker;
54 
55  marker.ns = ensenso::ros::get_node_name(nh);
56  marker.id = i;
57  marker.header.stamp = ensenso::ros::Time(0);
58  marker.header.frame_id = frame;
59  marker.action = visualization_msgs::msg::Marker::MODIFY; // Note: ADD = MODIFY
60 
61  // Set color
62  const auto color = object[itmLighting][itmColor];
63  if (color.exists() && color.count() == 3)
64  {
65  marker.color.r = static_cast<float>(color[0].asDouble());
66  marker.color.g = static_cast<float>(color[1].asDouble());
67  marker.color.b = static_cast<float>(color[2].asDouble());
68  marker.color.a = 1.0f;
69  }
70  else
71  {
72  // Default color (gray)
73  marker.color.r = 0.5;
74  marker.color.g = 0.5;
75  marker.color.b = 0.5;
76  marker.color.a = 1.0;
77  }
78 
79  // Set pose
80  marker.pose = poseFromTransform(transformFromNxLib(object[itmLink]));
81 
82  auto type = object[itmType];
83  auto filename = object[itmFilename];
84 
85  // Set type and deal with object-specific properties
86  if (filename.exists())
87  {
88  marker.type = visualization_msgs::msg::Marker::MESH_RESOURCE;
89  marker.mesh_resource = "file://" + filename.asString();
90 
91  // Scale from mm to m
92  marker.scale.x = 1.0 / 1000.0;
93  marker.scale.y = 1.0 / 1000.0;
94  marker.scale.z = 1.0 / 1000.0;
95  }
96  else if (type.asString() == valSphere)
97  {
98  marker.type = visualization_msgs::msg::Marker::SPHERE;
99 
100  // Sphere scale is diameter in meters
101  marker.scale.x = object[itmRadius].asDouble() * 2.0 / 1000.0;
102  marker.scale.y = marker.scale.x;
103  marker.scale.z = marker.scale.x;
104  }
105  else if (type.asString() == valCylinder)
106  {
107  marker.type = visualization_msgs::msg::Marker::CYLINDER;
108 
110  marker.scale.x = object[itmRadius].asDouble() * 2.0 / 1000.0;
111  marker.scale.y = marker.scale.x;
112  marker.scale.z = object[itmHeight].asDouble() / 1000.0;
113 
116  // marker.pose.position.z += marker.scale.z / 2.0;
117  }
118  else if (type.asString() == valCube)
119  {
120  marker.type = visualization_msgs::msg::Marker::CUBE;
121 
122  marker.scale.x = object[itmWidth].asDouble() / 1000.0;
123  marker.scale.y = object[itmWidth].asDouble() / 1000.0;
124  marker.scale.z = object[itmWidth].asDouble() / 1000.0;
125  }
126  else if (type.asString() == valCuboid)
127  {
128  marker.type = visualization_msgs::msg::Marker::CUBE;
129 
130  marker.scale.x = object[itmWidth].asDouble() / 1000.0;
131  marker.scale.z = object[itmHeight].asDouble() / 1000.0;
132  marker.scale.y = object[itmDepth].asDouble() / 1000.0;
133  }
134  else
135  {
136  // Unsupported type, skip it
137  continue;
138  }
139 
140  markers.markers.push_back(marker);
141  }
142  }
143 
144  void run()
145  {
146  while (ensenso::ros::ok() && !stop)
147  {
148  publisher->publish(markers);
149  rate.sleep();
150  }
151  }
152 
155  ensenso::ros::Rate rate;
156  visualization_msgs::msg::MarkerArray markers;
157  std::atomic_bool& stop;
158 };
159 } // namespace
160 
162  const std::string& objectsFrame, const std::string& linkFrame,
163  const std::string& markerTopic, double markerPublishRate)
164  : objectsFrame(objectsFrame), linkFrame(linkFrame), tfBuffer(TF2_BUFFER_CTOR_ARGS(nh))
165 {
166  // Read the file contents and assign it to the objects tag
167  auto objects = NxLibItem{ itmObjects };
168  objects.setJson(readFile(filename));
169 
170  // Get the original transforms from file
171  for (int i = 0; i < objects.count(); ++i)
172  {
173  originalTransforms.push_back(transformFromNxLib(objects[i][itmLink]));
174  }
175 
176  // Create publisher thread
177  if (!markerTopic.empty())
178  {
179  markerThread = std::thread([=]() {
180  VirtualObjectMarkerPublisher publisher{ nh, markerTopic, markerPublishRate,
181  objects, objectsFrame, stopMarkerThread };
182  publisher.run();
183  });
184  }
185 }
186 
188 {
189  stopMarkerThread = true;
190 
191  if (markerThread.joinable())
192  {
193  markerThread.join();
194  }
195 }
196 
198 {
199  // Exit early if we have no work to do
200  if (originalTransforms.empty())
201  {
202  return;
203  }
204 
205  // Find transform from the frame in which the objects were defined to the current optical frame
206  tf2::Transform cameraTransform;
207  try
208  {
209  cameraTransform = fromMsg(tfBuffer.lookupTransform(linkFrame, objectsFrame, ensenso::ros::Time(0)).transform);
210  }
211  catch (const tf2::TransformException& e)
212  {
213  ENSENSO_WARN("Could not look up the virtual object pose due to the TF error: %s", e.what());
214  return;
215  }
216 
217  // Apply the transform to all of the original transforms
218  for (size_t i = 0; i < originalTransforms.size(); ++i)
219  {
220  tf2::Transform objectTransform = cameraTransform * originalTransforms[i];
221  NxLibItem objectLink = NxLibItem{ itmObjects }[static_cast<int>(i)][itmLink];
222  writeTransformToNxLib(objectTransform.inverse(), objectLink);
223  }
224 }
void writeTransformToNxLib(Transform const &transform, NxLibItem const &node)
::ros::Time Time
Definition: time.h:67
#define TF2_BUFFER_CTOR_ARGS(nh)
Definition: tf2.h:18
void run(class_loader::ClassLoader *loader)
std::vector< tf2::Transform > originalTransforms
Original object transforms in the base frame.
Transform inverse() const
Transform fromMsg(TransformMsg const &transform)
std::string objectsFrame
Frame in which objects are defined.
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
::ros::NodeHandle NodeHandle
Definition: node.h:214
bool ok()
Definition: core.h:152
void ENSENSO_WARN(T... args)
Definition: logging.h:210
::std::unique_ptr< ::ros::Publisher > Publisher
Definition: core.h:115
StampedPoseMsg poseFromTransform(StampedTransformMsg const &transform)
inline ::std::string get_node_name(NodeHandle &nh)
Definition: core.h:134
void move(std::vector< T > &a, std::vector< T > &b)
Transform transformFromNxLib(NxLibItem const &node)
std::string linkFrame
Link frame where NxLib expects the objects to be defined.
::ros::Rate Rate
Definition: time.h:66
std::thread markerThread
Background thread used to publish object markers.
VirtualObjectHandler(ensenso::ros::NodeHandle &nh, const std::string &filename, const std::string &objectsFrame, const std::string &linkFrame, const std::string &markerTopic, double markerPublishRate)
std::atomic_bool stopMarkerThread
Flag to indicate markerThread should stop.


ensenso_camera
Author(s): Ensenso
autogenerated on Sat Jun 3 2023 02:17:04