iMSTK
Interactive Medical Simulation Toolkit
PbdRigidBodyGraspingExample.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkAxesModel.h"
8 #include "imstkCamera.h"
9 #include "imstkCapsule.h"
10 #include "imstkControllerForceText.h"
11 #include "imstkDeviceManager.h"
12 #include "imstkDeviceManagerFactory.h"
13 #include "imstkDirectionalLight.h"
14 #include "imstkIsometricMap.h"
15 #include "imstkKeyboardDeviceClient.h"
16 #include "imstkMeshIO.h"
17 #include "imstkObjectControllerGhost.h"
18 #include "imstkPbdModel.h"
19 #include "imstkPbdModelConfig.h"
20 #include "imstkPbdObject.h"
21 #include "imstkPbdObjectCollision.h"
22 #include "imstkPbdObjectController.h"
23 #include "imstkPbdRigidObjectGrasping.h"
24 #include "imstkPlane.h"
25 #include "imstkRenderMaterial.h"
26 #include "imstkScene.h"
27 #include "imstkSceneManager.h"
28 #include "imstkSimulationManager.h"
29 #include "imstkSimulationUtils.h"
30 #include "imstkSphere.h"
31 #include "imstkTextVisualModel.h"
32 #include "imstkVTKViewer.h"
33 
34 //#define USE_TWO_HAPTIC_DEVICES
35 
36 #ifndef USE_TWO_HAPTIC_DEVICES
37 #include "imstkDummyClient.h"
38 #include "imstkMouseDeviceClient.h"
39 #endif
40 
41 using namespace imstk;
42 
43 static std::shared_ptr<PbdObject>
44 makeCapsuleToolObj(std::shared_ptr<PbdModel> model, bool isLeft)
45 {
46  auto toolGeometry = std::make_shared<Capsule>();
47  toolGeometry->setRadius(0.005);
48  toolGeometry->setLength(0.1);
49  toolGeometry->setPosition(Vec3d(0.0, 0.0, 0.0));
50  toolGeometry->setOrientation(Quatd::FromTwoVectors(Vec3d(0.0, 1.0, 0.0), Vec3d(0.0, 0.0, 1.0)));
51 
52  auto toolObj = std::make_shared<PbdObject>("Tool");
53 
54  // Create the object
55  toolObj->setVisualGeometry(toolGeometry);
56  toolObj->setPhysicsGeometry(toolGeometry);
57  toolObj->setCollidingGeometry(toolGeometry);
58  toolObj->setDynamicalModel(model);
59  toolObj->getPbdBody()->setRigid(
60  Vec3d(0.0, 0.1, 0.0),
61  30.0,
62  Quatd::Identity(),
63  Mat3d::Identity() * 1.0);
64 
65  toolObj->getVisualModel(0)->getRenderMaterial()->setOpacity(0.9);
66 
67  // Add a component for controlling via another device
68  auto controller = toolObj->addComponent<PbdObjectController>();
69  controller->setControlledObject(toolObj);
70  controller->setLinearKs(500000.0);
71  controller->setAngularKs(10000.0);
72  controller->setUseCritDamping(true);
73  controller->setForceScaling(0.002);
74  controller->setSmoothingKernelSize(15);
75  controller->setUseForceSmoothening(true);
76 
77  auto axesModel = toolObj->addComponent<AxesModel>();
78  axesModel->setScale(Vec3d(0.05, 0.05, 0.05));
79 
80  auto axesUpdate = toolObj->addComponent<LambdaBehaviour>("AxesModelUpdate");
81  axesUpdate->setUpdate([ = ](const double& dt)
82  {
83  axesModel->setPosition((*toolObj->getPbdBody()->vertices)[0]);
84  axesModel->setOrientation((*toolObj->getPbdBody()->orientations)[0]);
85  });
86 
87  // Add extra component to tool for the ghost
88  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
89  controllerGhost->setController(controller);
90 
91  // Add a component to display controller force
92  auto controllerForceTxt = toolObj->addComponent<ControllerForceText>();
93  if (isLeft)
94  {
95  controllerForceTxt->getText()->setPosition(TextVisualModel::DisplayPosition::UpperLeft);
96  }
97  controllerForceTxt->setController(controller);
98 
99  return toolObj;
100 }
101 
106 int
107 main()
108 {
109  // Setup logger (write to file and stdout)
111 
112  // Setup the scene
113  auto scene = std::make_shared<Scene>("PbdRigidBodyGrasping");
114  scene->getActiveCamera()->setPosition(0.0, 0.5, 0.5);
115  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
116  scene->getActiveCamera()->setViewUp(0.0, 1.0, 0.0);
117 
118  auto pbdModel = std::make_shared<PbdModel>();
119  std::shared_ptr<PbdModelConfig> pbdParams = pbdModel->getConfig();
120  pbdParams->m_gravity = Vec3d(0.0, -9.8, 0.0);
121  //pbdParams->m_gravity = Vec3d::Zero();
122  pbdParams->m_dt = 0.002;
123  pbdParams->m_iterations = 8;
124  pbdParams->m_linearDampingCoeff = 0.01;
125  pbdParams->m_angularDampingCoeff = 0.01;
126 
127  // Make a plane
128  auto planeObj = std::make_shared<CollidingObject>("PlaneObj");
129  auto plane = std::make_shared<Plane>(Vec3d(0.0, 0.0, 0.0), Vec3d(0.0, 1.0, 0.0));
130  plane->setWidth(1.0);
131  planeObj->setCollidingGeometry(plane);
132  planeObj->setVisualGeometry(plane);
133  scene->addSceneObject(planeObj);
134 
135  // Make a pbd rigid body needle
136  auto needleObj = std::make_shared<PbdObject>();
137  {
138  auto needleMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Needles/c6_suture.stl");
139  auto needleLineMesh = MeshIO::read<LineMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Needles/c6_suture_hull.vtk");
140  // Transform so center of mass is in center of the needle
141  needleMesh->translate(Vec3d(0.0, -0.0047, -0.0087), Geometry::TransformType::ApplyToData);
142  needleLineMesh->translate(Vec3d(0.0, -0.0047, -0.0087), Geometry::TransformType::ApplyToData);
143  needleMesh->scale(2.0, Geometry::TransformType::ApplyToData);
144  needleLineMesh->scale(2.0, Geometry::TransformType::ApplyToData);
145  needleObj->setVisualGeometry(needleMesh);
146  needleObj->setCollidingGeometry(needleLineMesh);
147  needleObj->setPhysicsGeometry(needleLineMesh);
148  needleObj->setPhysicsToVisualMap(std::make_shared<IsometricMap>(needleLineMesh, needleMesh));
149  needleObj->setDynamicalModel(pbdModel);
150  needleObj->getPbdBody()->setRigid(
151  Vec3d(-0.1, 0.15, 0.0),
152  1.0,
153  Quatd::Identity(),
154  Mat3d::Identity() * 0.01);
155  needleObj->getVisualModel(0)->getRenderMaterial()->setColor(Color::Orange);
156  }
157  scene->addSceneObject(needleObj);
158 
159  // Make a pbd rigid body sphere
160  auto sphereObj = std::make_shared<PbdObject>();
161  {
162  auto sphereGeom = std::make_shared<Sphere>(Vec3d::Zero(), 0.01);
163  sphereObj->setVisualGeometry(sphereGeom);
164  sphereObj->setCollidingGeometry(sphereGeom);
165  sphereObj->setPhysicsGeometry(sphereGeom);
166  sphereObj->setDynamicalModel(pbdModel);
167  sphereObj->getPbdBody()->setRigid(
168  Vec3d(0.1, 0.15, 0.0),
169  1.0,
170  Quatd::Identity(),
171  Mat3d::Identity() * 0.01);
172  sphereObj->getVisualModel(0)->getRenderMaterial()->setColor(Color::Blood);
173  }
174  scene->addSceneObject(sphereObj);
175 
176  // Setup a tool to grasp with
177  std::shared_ptr<PbdObject> leftToolObj = makeCapsuleToolObj(pbdModel, true);
178  scene->addSceneObject(leftToolObj);
179  std::shared_ptr<PbdObject> rightToolObj = makeCapsuleToolObj(pbdModel, false);
180  scene->addSceneObject(rightToolObj);
181 
182  // Add collision between plane and objects
183  auto planeCollision0 = std::make_shared<PbdObjectCollision>(needleObj, planeObj);
184  planeCollision0->setRigidBodyCompliance(0.00001);
185  scene->addInteraction(planeCollision0);
186  auto planeCollision1 = std::make_shared<PbdObjectCollision>(sphereObj, planeObj);
187  planeCollision1->setRigidBodyCompliance(0.00001);
188  scene->addInteraction(planeCollision1);
189 
190  // Collision between needle and sphere
191  auto sphereNeedleCollision = std::make_shared<PbdObjectCollision>(sphereObj, needleObj, "PointSetToSphereCD");
192  sphereNeedleCollision->setRigidBodyCompliance(0.000001);
193  scene->addInteraction(sphereNeedleCollision);
194 
195  // Add grasping
196  auto leftGrasping0 = std::make_shared<PbdObjectGrasping>(needleObj, leftToolObj);
197  leftGrasping0->setCompliance(0.00001);
198  scene->addInteraction(leftGrasping0);
199  auto leftGrasping1 = std::make_shared<PbdObjectGrasping>(sphereObj, leftToolObj);
200  leftGrasping1->setCompliance(0.00001);
201  scene->addInteraction(leftGrasping1);
202 
203  auto rightGrasping0 = std::make_shared<PbdObjectGrasping>(needleObj, rightToolObj);
204  rightGrasping0->setCompliance(0.00001);
205  scene->addInteraction(rightGrasping0);
206  auto rightGrasping1 = std::make_shared<PbdObjectGrasping>(sphereObj, rightToolObj);
207  rightGrasping1->setCompliance(0.00001);
208  scene->addInteraction(rightGrasping1);
209 
210  // Light
211  auto light = std::make_shared<DirectionalLight>();
212  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
213  light->setIntensity(1.0);
214  scene->addLight("Light", light);
215 
216  // Run the simulation
217  {
218  // Setup a viewer to render
219  auto viewer = std::make_shared<VTKViewer>();
220  viewer->setActiveScene(scene);
221  viewer->setDebugAxesLength(0.05, 0.05, 0.05);
222 
223  // Setup a scene manager to advance the scene
224  auto sceneManager = std::make_shared<SceneManager>();
225  sceneManager->setActiveScene(scene);
226  sceneManager->pause(); // Start simulation paused
227 
228  // Setup default haptics manager
229  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
230 
231  auto driver = std::make_shared<SimulationManager>();
232  driver->addModule(viewer);
233  driver->addModule(sceneManager);
234  driver->addModule(hapticManager);
235  driver->setDesiredDt(0.002);
236 
237  auto leftController = leftToolObj->getComponent<PbdObjectController>();
238 
239  if (hapticManager->getTypeName() == "HaplyDeviceManager")
240  {
241  leftController->setTranslationOffset(Vec3d(0.1, 0.0, -0.1));
242  }
243  std::shared_ptr<DeviceClient> leftDeviceClient = hapticManager->makeDeviceClient();
244  leftController->setDevice(leftDeviceClient);
245 
246  connect<ButtonEvent>(leftDeviceClient, &DeviceClient::buttonStateChanged,
247  [&](ButtonEvent* e)
248  {
249  if (e->m_button == 1)
250  {
251  if (e->m_buttonState == BUTTON_PRESSED)
252  {
253  // Use a slightly larger capsule since collision prevents intersection
254  auto capsule = std::dynamic_pointer_cast<Capsule>(leftToolObj->getCollidingGeometry());
255  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
256  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
257  leftGrasping0->beginCellGrasp(dilatedCapsule);
258  leftGrasping1->beginCellGrasp(dilatedCapsule);
259  }
260  else if (e->m_buttonState == BUTTON_RELEASED)
261  {
262  leftGrasping0->endGrasp();
263  leftGrasping1->endGrasp();
264  }
265  }
266  });
267 #ifdef USE_TWO_HAPTIC_DEVICES
268  std::shared_ptr<DeviceClient> rightDeviceClient = hapticManager->makeDeviceClient("Device2");
269  auto rightController = rightToolObj->getComponent<PbdObjectController>();
270  rightController->setDevice(rightDeviceClient);
271 
272  connect<ButtonEvent>(rightDeviceClient, &DeviceClient::buttonStateChanged,
273  [&](ButtonEvent* e)
274  {
275  if (e->m_button == 1)
276  {
277  if (e->m_buttonState == BUTTON_PRESSED)
278  {
279  // Use a slightly larger capsule since collision prevents intersection
280  auto capsule = std::dynamic_pointer_cast<Capsule>(rightToolObj->getCollidingGeometry());
281  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
282  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
283  rightGrasping0->beginCellGrasp(dilatedCapsule);
284  rightGrasping1->beginCellGrasp(dilatedCapsule);
285  }
286  else if (e->m_buttonState == BUTTON_RELEASED)
287  {
288  rightGrasping0->endGrasp();
289  rightGrasping1->endGrasp();
290  }
291  }
292  });
293 #else
294  auto rightDeviceClient = std::make_shared<DummyClient>();
295  auto rightController = rightToolObj->getComponent<PbdObjectController>();
296  rightController->setDevice(rightDeviceClient);
297 
298  connect<Event>(sceneManager, &SceneManager::postUpdate,
299  [&](Event*)
300  {
301  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
302  const Vec3d worldPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 0.1;
303 
304  rightDeviceClient->setPosition(worldPos);
305  });
306  connect<MouseEvent>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonPress,
307  [&](MouseEvent* e)
308  {
309  // Use a slightly larger capsule since collision prevents intersection
310  auto capsule = std::dynamic_pointer_cast<Capsule>(rightToolObj->getCollidingGeometry());
311  auto dilatedCapsule = std::make_shared<Capsule>(*capsule);
312  dilatedCapsule->setRadius(capsule->getRadius() * 1.1);
313  rightGrasping0->beginCellGrasp(dilatedCapsule);
314  rightGrasping1->beginCellGrasp(dilatedCapsule);
315  });
316  connect<MouseEvent>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonRelease,
317  [&](MouseEvent* e)
318  {
319  rightGrasping0->endGrasp();
320  rightGrasping1->endGrasp();
321  });
322 #endif
323 
324  // Add default mouse and keyboard controls to the viewer
325  std::shared_ptr<Entity> mouseAndKeyControls =
326  SimulationUtils::createDefaultSceneControl(driver);
327  auto instructText = mouseAndKeyControls->getComponent<TextVisualModel>();
328  instructText->setText(instructText->getText() +
329  "\nMouse Click/Press Haptic Device Button to grasp");
330  scene->addSceneObject(mouseAndKeyControls);
331 
332  connect<Event>(sceneManager, &SceneManager::preUpdate, [&](Event*)
333  {
334  // Simulate in real time
335  pbdModel->getConfig()->m_dt = sceneManager->getDt();
336  });
337 
338  driver->start();
339  }
340 
341  return 0;
342 }
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
This class uses the provided device to control the provided rigid object via virtual coupling...
Compound Geometry.
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
void setText(const std::string &text)
Text to be plotted.
Defines an axes that should be rendered.
Provides the information of a mouse event, this includes button presses/releases and scrolling...
A SceneBehaviour that can update via a lambda function.
Renders text to the screen.
A behaviour that renders a second copy of the controlled object at a lower opacity in the physical po...
Capsule geometry, default configuration is centered at origin with length running up and down the y a...
Definition: imstkCapsule.h:21
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.
Displays virtual coupling force text in the top right.