1Load a STEP file and simulate a robot (demo_CAS_robot.cpp) {#tutorial_demo_robot} 2========================== 3 4 5Tutorial that teaches how to use the 6[CASCADE module](group__cascade__module.html) 7to load a 6-DOF robot saved in a STEP file as an assembly, exported from a CAD model. 8 9 10This is an advanced tutorial that shows how to load complex 3D models that have been saved in 11[STEP file format](http://en.wikipedia.org/wiki/ISO_10303) from professional 3D CAD software. 12 13Most high-end 3D CAD software can save assemblies and parts in STEP format. 14Among the most used CAD software, we cite 15[CATIA](http://www.3ds.com/products/catia/welcome/), 16[SolidEdge](http://www.solidedge.com), 17[SolidWorks](http://www.solidworks.com/), 18[PTC Pro/E](http://www.ptc.com), etc. 19The following tutorial is based on **SolidEdge**, but it can be adapted to other CADs with minor modifications. 20 21# Prepare a STEP file 22 23The CASCADE module is able to directly parse sub-assemblies and sub-parts that have been saved into a single STEP file. However, it is better to prepare the assembly with certain guidelines, before saving it: noticeably, we will put some 'auxiliary' objects in the assembly (the markers) that we will use from the C++ programming side in order to retrieve useful coordinates for building the constraints between the parts. This process is explained in the folowing example. 24 25<div class="ce-info"> 26Creating a demo mechanism (a car, a robot, etc.) might take hours. To make things simplier, we load a ready-to-use model that can be downloaded from the website of a manufaturer of industrial robots, see [download page](http://www.abb.com/product/seitp327/5356453900282c5cc1256efc0028d55d.aspx?productLanguage=us&country=00&tabKey=7 ) from the [ABB](www.abb.com) website. We downloaded the 3D model for the IRB 7600 robot: by the way it is already in STEP format, so it could be loaded directly by the CASCADE unit, but we prefer to mofify it in SolidEdge and save it again. 27</div> 28 29- Start SolidEdge (we use the v18 version in this example) 30 31- Menu File / Open... 32 33- Choose the STEP (.stp or .step) file of the robot, as downloaded from the ABB site. 34 35- In the 'New' window, in the 'General' tab, select 'Normal.asm' to let SolidEdge create an assembly from the STEP file 36 37- Press OK. After the conversion, SolidEdge created the assembly. Look into the directory of your STEP file: SolidEdge created its .asm and many .part files. The Assembly PathFinder panel, to the right, shows all the parts in the assembly: 38 39 ![](http://projectchrono.org/assets/manual/Tutorial_robot_01.jpg) 40 41- Note that the assembly is made of many parts. Maybe that there is not a one-to-one correspondence from rigid bodies for our simulation, and CAD parts, so we want to organize N sub-assemblies, that represent our N rigid bodies in the simulation. That is, it would be nice if each part stays in a separate sub-assebly that represents a 'rigid body', and such subassemblies can optionally contain some auxiliary objects (we call them 'markers') that can be used later on the C++ side to find the position of the joints. 42 43- To make the sub assembles: 44 - Select the 'Parts library' panel, 45 - press button 'Create in Place' to open the create window, 46 - set 'Normal.asm' as Template 47 - set 'Base' (or 'Forearm', or other meaningful name) in 'New file name', 48 - browse to your directory where you have all other assembly files, in 'New file location', 49 - menu File / Close and return, to go back to general assembly. 50 - Repeat the last steps to create the other subassemblies, named for example 'Bicep', 'Forearm', 'Wrist', etc. 51 52- The created subassemblies are still empty. So we must move the imported parts into them. Drag and drop a part from parent assembly to a child asembly does not work in SolidEdge v.18, yet a simple way to do this is to 53 - select the part in the parent assembly, 54 - press Ctrl+C to copy, 55 - then select the sublevel in the Assembly PathFinder, 56 - use 'Edit' from popup-menu, 57 - press Ctrl-V to paste it, 58 - then go back to parent assembly with menu 'File / Close and return'. 59 60- Repeat this until you have all the parts in their separate subassemblies. Some subassembly might have multiple parts. Look our example: 61 62 ![](http://projectchrono.org/assets/manual/Tutorial_robot_02.jpg) 63 64- Now we add an auxiliary part in all subassemblies, to represent the coordinates where you want the constraints (links, motors, ..) to be created using C++ functions. These are like 'placeholders'. To do so, we create a 'marker.par' part, such as the one in the following figure: 65 66 ![](http://projectchrono.org/assets/manual/Tutorial_robot_03.jpg) 67 [marker.par](http://projectchrono.org/assets/manual/marker.par) 68 69- We insert this marker part in all subassemblies, properly aligned to all the joints: 70 71 ![](http://projectchrono.org/assets/manual/Tutorial_robot_04.jpg) 72 73- At the end, the Assembly PathFinder panel should show something like the following: 74 75 ![](http://projectchrono.org/assets/manual/Tutorial_robot_05.jpg) 76 77- Select the base assembly, use the menu File / Save as.. and choose STEP as file format, then save the entire assembly. 78 79- Quit the SolidEdge software. 80 81 82# Write C++ code to load the model 83 84The key of the remaining process is the functionality of the ChCascadeDoc class. Such class has the functionality of loading sub-assemblies from a STEP file by using the function `mydoc.GetNamedShape(...)` that takes, as argument, the ASCII name of the subassembly (or sub part). 85 86A small inconvenience happens here: because of a SolidEdge-specific issue, the names of the subassemblies in the STEP file are not always the same names that you read in the Assembly PAthFinder window. In detail, all the names of the assemblies are automatically translated to `Assem1`, `Assem2`, etc., whereas you would expect the names of the assemblies that you created, such as `Base`, `Turret`, etc. 87 88A workaround to this inconvenience is the following: you use the `mydoc.Dump(GetLog())` function to print the hierarchy on the console and take note of the STEP names on a piece of paper (or just use the demo_converter.exe to show that hierarchy), you will see something like: 89 90~~~{.txt} 91 -Name :Assem10 (root) 92 pos at: 0 0 0 (absolute) 93 pos at: 0 0 0 (.Location) 94 -Name :Assem8 95 pos at: 0 0 0 (absolute) 96 pos at: 0 0 0 (.Location) 97 -Name :IRB7600_23_500_m2000_rev1_01-1 98 pos at: 0 0 0 (absolute) 99 pos at: 0 0 0 (.Location) 100 -Name :marker 101 pos at: 2.29901e-035 -2.99071e-036 0.2185 (absol 102 pos at: 2.29901e-035 -2.99071e-036 0.2185 (.Loca 103 -Name :Assem4 104 pos at: 0 0 0 (absolute) 105 pos at: 0 0 0 (.Location) 106 -Name :IRB7600_23_500_m2000_rev1_01-2 107 pos at: 0 0 0 (absolute) 108 pos at: 0 0 0 (.Location) 109 -Name :marker 110 pos at: 3.88578e-015 -2.66454e-015 0.2135 (absol 111 pos at: 3.88578e-015 -2.66454e-015 0.2135 (.Loca 112 -Name :marker 113 pos at: 0.41 -0.049 0.78 (absolute) 114 pos at: 0.41 -0.049 0.78 (.Location) 115 -Name :marker 116 pos at: -0.38 -0.03 0.6545 (absolute) 117 pos at: -0.38 -0.03 0.6545 (.Location) 118 -Name :Assem1 119 .... etc. etc. .... ........ 120~~~ 121 122From the example above, you see that `Base` has become `Assem8`, `Turret` has become `Assem4`, and so on. (luckily enough, SolidEdge did not change the name of the parts, only the assembly names were changed). Take note of this on a sheet of paper. 123 124Now, let's develop the C++ program that loads the robot model and simulates it. 125 126First of all, include the needed libraries (note the unit_CASCADE/... headers) and use the proper namespaces: 127 128~~~{.cpp} 129#include "physics/CHapidll.h" 130#include "core/CHrealtimeStep.h" 131#include "irrlicht_interface/CHirrAppInterface.h" 132#include "irrlicht_interface/CHbodySceneNodeTools.h" 133#include "unit_CASCADE/CHcascadeDoc.h" 134#include "unit_CASCADE/CHCascadeMeshTools.h" 135#include "unit_CASCADE/CHirrCascadeMeshTools.h" 136#include "unit_CASCADE/CHirrCascade.h" 137#include "irrlicht_interface/CHbodySceneNode.h" 138#include <irrlicht.h> 139 140 141// Use the namespace of Chrono 142using namespace chrono; 143 144// Use the main namespaces of Irlicht 145using namespace irr; 146using namespace core; 147using namespace scene; 148using namespace video; 149using namespace io; 150using namespace gui; 151 152// Use the namespace with OpenCascade stuff 153using namespace cascade; 154~~~ 155 156 157Here is the program (a simple demo where the robot is created from the STEP file, constraints are added, and the robot simulation is displayed while moving on a simple trajectory). 158 159 160~~~{.cpp} 161int main(int argc, char* argv[]) 162{ 163 164 ChGlobals* GLOBAL_Vars = DLL_CreateGlobals(); 165 166 // 1- Create a ChronoENGINE physical system: all bodies and constraints 167 // will be handled by this ChSystem object. 168 ChSystem my_system; 169 170 // Create the Irrlicht visualization (open the Irrlicht device, 171 // bind a simple user interface, etc. etc.) 172 ChIrrAppInterface application(&my_system, L"Load a robot model from STEP file",core::dimension2d<u32>(800,600),false, true, video::EDT_OPENGL); 173 174 // Easy shortcuts to add logo, camera, lights and sky in Irrlicht scene: 175 //ChIrrWizard::add_typical_Logo(application.GetDevice()); 176 ChIrrWizard::add_typical_Sky(application.GetDevice()); 177 ChIrrWizard::add_typical_Lights(application.GetDevice(), core::vector3df(30,100,30), core::vector3df(30,-80,-30),200,130); 178 ChIrrWizard::add_typical_Camera(application.GetDevice(), core::vector3df(0.2,1.6,-3.5)); 179 180~~~ 181 182Create the ChCascadeDoc, a container that loads the STEP model and manages its subassembles. Also, prepare some pointers for bodies that will be created. 183 184~~~{.cpp} 185 ChCascadeDoc mydoc; 186~~~ 187 188 189Also, prepare some pointers for bodies that will be created. 190 191~~~{.cpp} 192 ChBodySceneNodeAuxRef* mrigidBody_base = 0; 193 ChBodySceneNodeAuxRef* mrigidBody_turret = 0; 194 ChBodySceneNodeAuxRef* mrigidBody_bicep = 0; 195 ChBodySceneNodeAuxRef* mrigidBody_elbow = 0; 196 ChBodySceneNodeAuxRef* mrigidBody_forearm = 0; 197 ChBodySceneNodeAuxRef* mrigidBody_wrist = 0; 198 ChBodySceneNodeAuxRef* mrigidBody_hand = 0; 199 ChBodySceneNodeAuxRef* mrigidBody_cylinder = 0; 200 ChBodySceneNodeAuxRef* mrigidBody_rod = 0; 201~~~ 202 203Load the STEP model using this command: (be sure to have the STEP file on the hard disk) 204 205~~~{.cpp} 206 bool load_ok = mydoc.Load_STEP("..\\data\\cascade\\IRB7600_23_500_m2000_rev1_01_decorated.stp"); 207~~~ 208 209Print the contained shapes, showing the assembly hierarchy: 210 211~~~{.cpp} 212 mydoc.Dump(GetLog()); 213 214 ChCollisionModel::SetDefaultSuggestedEnvelope(0.002); 215 ChCollisionModel::SetDefaultSuggestedMargin(0.001); 216 217 // In most CADs the Y axis is horizontal, but we want it vertical. 218 // So define a root transformation for rotating all the imported objects. 219 ChQuaternion<> rotation1; 220 rotation1.Q_from_AngAxis(-CH_C_PI/2, VECT_X); // 1: rotate 90° on X axis 221 ChQuaternion<> rotation2; 222 rotation2.Q_from_AngAxis(CH_C_PI, VECT_Y); // 2: rotate 180° on vertical Y axis 223 ChQuaternion<> tot_rotation = rotation2 % rotation1; // rotate on 1 then on 2, using quaternion product 224 ChFrameMoving<> root_frame( ChVector<>(0,0,0), tot_rotation); 225~~~ 226 227 228Retrieve some sub shapes from the loaded model, using the GetNamedShape() function, that can use path/subpath/subsubpath/part syntax and * or ? wldcards, etc. 229Using the / slash is like addressing a Unix directory (in fact the STEP file is organized like a directory with subdirectory, each representing a subassembly). 230 231~~~{.cpp} 232 if (load_ok) 233 { 234 235 TopoDS_Shape shape_base; 236 if (mydoc.GetNamedShape(shape_base, "Assem10/Assem8" )) 237 { 238 // Add the shape to the Irrlicht system, to get also visualization. 239 mrigidBody_base = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 240 &my_system, application.GetSceneManager(), 241 shape_base); 242 243 // The base is fixed to the ground 244 mrigidBody_base->GetBody()->SetBodyFixed(true); 245 246 // Move the body as for global displacement/rotation by pre-transform its coords. 247 // Note, it could be written also as mrigidBody_base->GetBody() %= root_frame; 248 mrigidBody_base->GetBody()->ConcatenatePreTransformation(root_frame); 249 } 250 else GetLog() << "Warning. Desired object not found in document \n"; 251 252 253 TopoDS_Shape shape_turret; 254 if (mydoc.GetNamedShape(shape_turret, "Assem10/Assem4" )) 255 { 256 // Add the shape to the Irrlicht system, to get also visualization. 257 mrigidBody_turret = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 258 &my_system, application.GetSceneManager(), 259 shape_turret); 260 261 // Move the body as for global displacement/rotation 262 mrigidBody_turret->GetBody()->ConcatenatePreTransformation(root_frame); 263 } 264 else GetLog() << "Warning. Desired object not found in document \n"; 265 266 267 TopoDS_Shape shape_bicep; 268 if (mydoc.GetNamedShape(shape_bicep, "Assem10/Assem1" )) 269 { 270 // Add the shape to the Irrlicht system, to get also visualization. 271 mrigidBody_bicep = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 272 &my_system, application.GetSceneManager(), 273 shape_bicep); 274 275 // Move the body as for global displacement/rotation 276 mrigidBody_bicep->GetBody()->ConcatenatePreTransformation(root_frame); 277 } 278 else GetLog() << "Warning. Desired object not found in document \n"; 279 280 281 TopoDS_Shape shape_elbow; 282 if (mydoc.GetNamedShape(shape_elbow, "Assem10/Assem5" )) 283 { 284 // Add the shape to the Irrlicht system, to get also visualization. 285 mrigidBody_elbow = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 286 &my_system, application.GetSceneManager(), 287 shape_elbow); 288 289 // Move the body as for global displacement/rotation 290 mrigidBody_elbow->GetBody()->ConcatenatePreTransformation(root_frame); 291 } 292 else GetLog() << "Warning. Desired object not found in document \n"; 293 294 295 TopoDS_Shape shape_forearm; 296 if (mydoc.GetNamedShape(shape_forearm, "Assem10/Assem7" )) 297 { 298 // Add the shape to the Irrlicht system, to get also visualization. 299 mrigidBody_forearm = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 300 &my_system, application.GetSceneManager(), 301 shape_forearm); 302 303 // Move the body as for global displacement/rotation 304 mrigidBody_forearm->GetBody()->ConcatenatePreTransformation(root_frame); 305 } 306 else GetLog() << "Warning. Desired object not found in document \n"; 307 308 309 TopoDS_Shape shape_wrist; 310 if (mydoc.GetNamedShape(shape_wrist, "Assem10/Assem6" )) 311 { 312 // Add the shape to the Irrlicht system, to get also visualization. 313 mrigidBody_wrist = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 314 &my_system, application.GetSceneManager(), 315 shape_wrist); 316 317 // Move the body as for global displacement/rotation 318 mrigidBody_wrist->GetBody()->ConcatenatePreTransformation(root_frame); 319 } 320 else GetLog() << "Warning. Desired object not found in document \n"; 321 322 323 TopoDS_Shape shape_hand; 324 if (mydoc.GetNamedShape(shape_hand, "Assem10/Assem9" )) 325 { 326 // Add the shape to the Irrlicht system, to get also visualization. 327 mrigidBody_hand = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 328 &my_system, application.GetSceneManager(), 329 shape_hand); 330 331 //mrigidBody_hand->GetBody()->SetBodyFixed(true); 332 333 // Move the body as for global displacement/rotation 334 mrigidBody_hand->GetBody()->ConcatenatePreTransformation(root_frame); 335 } 336 else GetLog() << "Warning. Desired object not found in document \n"; 337 338 339 TopoDS_Shape shape_cylinder; 340 if (mydoc.GetNamedShape(shape_cylinder, "Assem10/Assem3" )) 341 { 342 // Add the shape to the Irrlicht system, to get also visualization. 343 mrigidBody_cylinder = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 344 &my_system, application.GetSceneManager(), 345 shape_cylinder); 346 347 // Move the body as for global displacement/rotation 348 mrigidBody_cylinder->GetBody()->ConcatenatePreTransformation(root_frame); 349 } 350 else GetLog() << "Warning. Desired object not found in document \n"; 351 352 353 TopoDS_Shape shape_rod; 354 if (mydoc.GetNamedShape(shape_rod, "Assem10/Assem2" )) 355 { 356 // Add the shape to the Irrlicht system, to get also visualization. 357 mrigidBody_rod = (ChBodySceneNodeAuxRef*)addChBodySceneNode_Cascade_C( 358 &my_system, application.GetSceneManager(), 359 shape_rod); 360 361 // Move the body as for global displacement/rotation 362 mrigidBody_rod->GetBody()->ConcatenatePreTransformation(root_frame); 363 } 364 else GetLog() << "Warning. Desired object not found in document \n"; 365 366 367 } 368 else GetLog() << "Warning. Desired STEP file could not be opened/parsed \n"; 369 370 371 372 if (!mrigidBody_base || 373 !mrigidBody_turret || 374 !mrigidBody_bicep || 375 !mrigidBody_elbow || 376 !mrigidBody_forearm || 377 !mrigidBody_wrist || 378 !mrigidBody_hand ) 379 { 380 DLL_DeleteGlobals(); 381 return 0; 382 } 383~~~ 384 385Create joints between two parts. 386To understand where is the axis of the joint, we can exploit the fact that in the STEP file that we prepared for this demo, we inserted some objects called 'marker' and we placed them aligned to the shafts, so now we can fetch them and get their position/rotation. 387 388Important! In the STEP file, some subassemblies have multiple instances of the marker, so there could be two or more shapes with the same name **marker**... how to select the desired one? The GetNamedShape() function has a feature for addressing this: you can use the # character followed by a number, so for example Assem10/Assem8/marker#2 means: get the 2nd instance of the shape called "marker" from the subassembly "Assem8" of assembly "Assem10". 389 390~~~{.cpp} 391 TopoDS_Shape shape_marker; 392 393 ChFrame<> frame_marker_base_turret; 394 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem8/marker#1" )) 395 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_base_turret); 396 else GetLog() << "Warning. Desired marker not found in document \n"; 397 // Transform the abs coordinates of the marker because everything was rotated/moved by 'root_frame' : 398 frame_marker_base_turret %= root_frame; 399 400 ChSharedPtr<ChLinkLockRevolute> my_link1(new ChLinkLockRevolute); 401 ChSharedBodyPtr mb1 = mrigidBody_base->GetBody(); 402 ChSharedBodyPtr mb2 = mrigidBody_turret->GetBody(); 403 my_link1->Initialize(mb1, mb2, frame_marker_base_turret.GetCoord() ); 404 my_system.AddLink(my_link1); 405 406 407 ChFrame<> frame_marker_turret_bicep; 408 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem4/marker#2" )) 409 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_turret_bicep); 410 else GetLog() << "Warning. Desired marker not found in document \n"; 411 frame_marker_turret_bicep %= root_frame; 412 413 ChSharedPtr<ChLinkLockRevolute> my_link2(new ChLinkLockRevolute); 414 mb1 = mrigidBody_turret->GetBody(); 415 mb2 = mrigidBody_bicep->GetBody(); 416 my_link2->Initialize(mb1, mb2, frame_marker_turret_bicep.GetCoord() ); 417 my_system.AddLink(my_link2); 418 419 420 ChFrame<> frame_marker_bicep_elbow; 421 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem1/marker#2" )) 422 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_bicep_elbow); 423 else GetLog() << "Warning. Desired marker not found in document \n"; 424 frame_marker_bicep_elbow %= root_frame; 425 426 ChSharedPtr<ChLinkLockRevolute> my_link3(new ChLinkLockRevolute); 427 mb1 = mrigidBody_bicep->GetBody(); 428 mb2 = mrigidBody_elbow->GetBody(); 429 my_link3->Initialize(mb1, mb2, frame_marker_bicep_elbow.GetCoord() ); 430 my_system.AddLink(my_link3); 431 432 433 ChFrame<> frame_marker_elbow_forearm; 434 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem5/marker#2" )) 435 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_elbow_forearm); 436 else GetLog() << "Warning. Desired marker not found in document \n"; 437 frame_marker_elbow_forearm %= root_frame; 438 439 ChSharedPtr<ChLinkLockRevolute> my_link4(new ChLinkLockRevolute); 440 mb1 = mrigidBody_elbow->GetBody(); 441 mb2 = mrigidBody_forearm->GetBody(); 442 my_link4->Initialize(mb1, mb2, frame_marker_elbow_forearm.GetCoord() ); 443 my_system.AddLink(my_link4); 444 445 446 ChFrame<> frame_marker_forearm_wrist; 447 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem7/marker#2" )) 448 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_forearm_wrist); 449 else GetLog() << "Warning. Desired marker not found in document \n"; 450 frame_marker_forearm_wrist %= root_frame; 451 452 ChSharedPtr<ChLinkLockRevolute> my_link5(new ChLinkLockRevolute); 453 mb1 = mrigidBody_forearm->GetBody(); 454 mb2 = mrigidBody_wrist->GetBody(); 455 my_link5->Initialize(mb1, mb2, frame_marker_forearm_wrist.GetCoord() ); 456 my_system.AddLink(my_link5); 457 458 459 ChFrame<> frame_marker_wrist_hand; 460 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem6/marker#2" )) 461 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_wrist_hand); 462 else GetLog() << "Warning. Desired marker not found in document \n"; 463 frame_marker_wrist_hand %= root_frame; 464 465 ChSharedPtr<ChLinkLockRevolute> my_link6(new ChLinkLockRevolute); 466 mb1 = mrigidBody_wrist->GetBody(); 467 mb2 = mrigidBody_hand->GetBody(); 468 my_link6->Initialize(mb1, mb2, frame_marker_wrist_hand.GetCoord() ); 469 my_system.AddLink(my_link6); 470 471 472 ChFrame<> frame_marker_turret_cylinder; 473 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem4/marker#3" )) 474 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_turret_cylinder); 475 else GetLog() << "Warning. Desired marker not found in document \n"; 476 frame_marker_turret_cylinder %= root_frame; 477 478 ChSharedPtr<ChLinkLockRevolute> my_link7(new ChLinkLockRevolute); 479 mb1 = mrigidBody_turret->GetBody(); 480 mb2 = mrigidBody_cylinder->GetBody(); 481 my_link7->Initialize(mb1, mb2, frame_marker_turret_cylinder.GetCoord() ); 482 my_system.AddLink(my_link7); 483 484 485 ChFrame<> frame_marker_cylinder_rod; 486 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem3/marker#2" )) 487 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_cylinder_rod); 488 else GetLog() << "Warning. Desired marker not found in document \n"; 489 frame_marker_cylinder_rod %= root_frame; 490 491 ChSharedPtr<ChLinkLockCylindrical> my_link8(new ChLinkLockCylindrical); 492 mb1 = mrigidBody_cylinder->GetBody(); 493 mb2 = mrigidBody_rod->GetBody(); 494 my_link8->Initialize(mb1, mb2, frame_marker_cylinder_rod.GetCoord() ); 495 my_system.AddLink(my_link8); 496 497 498 ChFrame<> frame_marker_rod_bicep; 499 if (mydoc.GetNamedShape(shape_marker, "Assem10/Assem2/marker#2" )) 500 ChCascadeDoc::FromCascadeToChrono(shape_marker.Location(), frame_marker_rod_bicep); 501 else GetLog() << "Warning. Desired marker not found in document \n"; 502 frame_marker_rod_bicep %= root_frame; 503 504 ChSharedPtr<ChLinkLockCylindrical> my_link9(new ChLinkLockCylindrical); 505 mb1 = mrigidBody_rod->GetBody(); 506 mb2 = mrigidBody_bicep->GetBody(); 507 my_link9->Initialize(mb1, mb2, frame_marker_rod_bicep.GetCoord() ); 508 my_system.AddLink(my_link9); 509~~~ 510 511 512Add a couple of markers for the 'lock' constraint between the hand and the absolute reference: when we will move the marker in absolute reference, the hand will follow it. 513 514This is a very simple way of performing the IK (Inverse Kinematics) of a robot, and it can be used to whatever type of robot, even parallel manipulators or complex kinematic chains, without the need of knowing the analytic expression of the IK. 515 516~~~{.cpp} 517 ChSharedMarkerPtr my_marker_hand(new ChMarker); 518 ChSharedMarkerPtr my_marker_move(new ChMarker); 519 520 mrigidBody_hand->GetBody()->AddMarker(my_marker_hand); 521 mrigidBody_base->GetBody()->AddMarker(my_marker_move); 522 523 ChQuaternion<> rot_on_x; rot_on_x.Q_from_AngAxis(CH_C_PI/2, VECT_X); 524 ChFrame<> frame_marker_move = ChFrame<>(VNULL, rot_on_x) >> frame_marker_wrist_hand ; 525 526 my_marker_hand->Impose_Abs_Coord( frame_marker_wrist_hand.GetCoord() ); 527 my_marker_move->Impose_Abs_Coord( frame_marker_move.GetCoord() ); 528 529 ChSharedPtr<ChLinkLockLock> my_link_teacher(new ChLinkLockLock); 530 my_link_teacher->Initialize(my_marker_hand, my_marker_move); 531 my_system.AddLink(my_link_teacher); 532~~~ 533 534Set motions for Z and Y coordinates of the 'my_link_teacher' marker, so that the hand will follow it. To do so, we create four segments of motion for Z coordinate and four for Y coordinate, we join them with ChFunction_Sequence and we repeat sequence by ChFunction_Repeat 535 536~~~{.cpp} 537 ChFunction_ConstAcc* motlaw_z1 = new ChFunction_ConstAcc(); 538 motlaw_z1->Set_h(-0.7); 539 motlaw_z1->Set_end(1); 540 ChFunction_Const* motlaw_z2 = new ChFunction_Const(); 541 ChFunction_ConstAcc* motlaw_z3 = new ChFunction_ConstAcc(); 542 motlaw_z3->Set_h( 0.7); 543 motlaw_z3->Set_end(1); 544 ChFunction_Const* motlaw_z4 = new ChFunction_Const(); 545 ChFunction_Sequence* motlaw_z_seq = new ChFunction_Sequence(); 546 motlaw_z_seq->InsertFunct(motlaw_z1, 1, 1, true); 547 motlaw_z_seq->InsertFunct(motlaw_z2, 1, 1, true); // true = force c0 continuity, traslating fx 548 motlaw_z_seq->InsertFunct(motlaw_z3, 1, 1, true); 549 motlaw_z_seq->InsertFunct(motlaw_z4, 1, 1, true); 550 ChFunction_Repeat* motlaw_z = new ChFunction_Repeat(); 551 motlaw_z->Set_fa(motlaw_z_seq); 552 motlaw_z->Set_window_length(4); 553 554 ChFunction_Const* motlaw_y1 = new ChFunction_Const(); 555 ChFunction_ConstAcc* motlaw_y2 = new ChFunction_ConstAcc(); 556 motlaw_y2->Set_h(-0.6); 557 motlaw_y2->Set_end(1); 558 ChFunction_Const* motlaw_y3 = new ChFunction_Const(); 559 ChFunction_ConstAcc* motlaw_y4 = new ChFunction_ConstAcc(); 560 motlaw_y4->Set_h(0.6); 561 motlaw_y4->Set_end(1); 562 ChFunction_Sequence* motlaw_y_seq = new ChFunction_Sequence(); 563 motlaw_y_seq->InsertFunct(motlaw_y1, 1, 1, true); 564 motlaw_y_seq->InsertFunct(motlaw_y2, 1, 1, true); // true = force c0 continuity, traslating fx 565 motlaw_y_seq->InsertFunct(motlaw_y3, 1, 1, true); 566 motlaw_y_seq->InsertFunct(motlaw_y4, 1, 1, true); 567 ChFunction_Repeat* motlaw_y = new ChFunction_Repeat(); 568 motlaw_y->Set_fa(motlaw_y_seq); 569 motlaw_y->Set_window_length(4); 570 571 my_marker_move->SetMotion_Z(motlaw_z); 572 my_marker_move->SetMotion_Y(motlaw_y); 573 574 575 // Create a large cube as a floor. 576 577 ChBodySceneNode* mfloor = (ChBodySceneNode*)addChBodySceneNode_easyBox( 578 &my_system, application.GetSceneManager(), 579 1000.0, 580 ChVector<>(0,-0.6,0), 581 ChQuaternion<>(1,0,0,0), 582 ChVector<>(20,1,20) ); 583 mfloor->GetBody()->SetBodyFixed(true); 584 mfloor->GetBody()->SetCollide(true); 585 video::ITexture* cubeMap = application.GetVideoDriver()->getTexture("../data/blu.png"); 586 mfloor->setMaterialTexture(0, cubeMap); 587~~~ 588 589 590 591Modify the settings of the solver. 592By default, the solver might not have sufficient precision to keep the robot joints 'mounted'. Expecially, the SOR, SSOR and other fixed point methods cannot simulate well this robot problem because the mass of the last body in the kinematic chain, i.e. the hand, is very low when compared to other bodies, so the convergence of the solver would be bad when 'pulling the hand' as in this 'teaching mode' IK. So switch to a more precise solver; this SOLVER_ITERATIVE_MINRES is fast and precise (although it is not fit for frictional collisions): 593 594~~~{.cpp} 595 my_system.SetLcpSolverType(ChSystem::SOLVER_MINRES); 596~~~ 597 598Now, finally, run the loop of the simulator: here are snapshots from the real-time simulator: 599 600![](http://projectchrono.org/assets/manual/Tutorial_robot_06.jpg) 601 602 603# Here is the full source code: 604 605\include demo_CAS_robot.cpp