From 9e66e4b114c8c8b4a8a0a3c3881238d795c9fe81 Mon Sep 17 00:00:00 2001 From: Ivolga Dmitriy Date: Mon, 28 Nov 2022 15:58:49 +0300 Subject: [PATCH 1/6] simplified version of smart interaction explorer --- environment.yml | 5 +- ...intexp_external_forces_on_testee_object.py | 67 ++ .../intexp_load_and_show_testee_object.py | 61 ++ examples/models/custom/body1.mtl | 5 + examples/models/custom/body1.obj | 867 ++++++++++++++++++ examples/models/custom/body1.xml | 7 + rostok.egg-info/PKG-INFO | 10 + rostok.egg-info/SOURCES.txt | 6 + rostok.egg-info/dependency_links.txt | 1 + rostok.egg-info/top_level.txt | 1 + rostok/intexp/__init__.py | 4 + rostok/intexp/chrono_api.py | 172 ++++ rostok/intexp/poses_generator.py | 99 ++ rostok/intexp/readme.md | 30 + rostok/intexp/testee.py | 370 ++++++++ 15 files changed, 1704 insertions(+), 1 deletion(-) create mode 100644 examples/intexp_external_forces_on_testee_object.py create mode 100644 examples/intexp_load_and_show_testee_object.py create mode 100644 examples/models/custom/body1.mtl create mode 100644 examples/models/custom/body1.obj create mode 100644 examples/models/custom/body1.xml create mode 100644 rostok.egg-info/PKG-INFO create mode 100644 rostok.egg-info/SOURCES.txt create mode 100644 rostok.egg-info/dependency_links.txt create mode 100644 rostok.egg-info/top_level.txt create mode 100644 rostok/intexp/__init__.py create mode 100644 rostok/intexp/chrono_api.py create mode 100644 rostok/intexp/poses_generator.py create mode 100644 rostok/intexp/readme.md create mode 100644 rostok/intexp/testee.py diff --git a/environment.yml b/environment.yml index 56b515b9..42cde703 100644 --- a/environment.yml +++ b/environment.yml @@ -5,13 +5,16 @@ channels: - defaults dependencies: - python=3.9.12 - - irrlicht=1.8.5 + - irrlicht - numpy - yapf - mypy - pylint + - pip - pip: - networkx==2.8.6 - matplotlib==3.6.0 - scipy - mcts==1.0.4 + - open3d + - lxml diff --git a/examples/intexp_external_forces_on_testee_object.py b/examples/intexp_external_forces_on_testee_object.py new file mode 100644 index 00000000..03e6508e --- /dev/null +++ b/examples/intexp_external_forces_on_testee_object.py @@ -0,0 +1,67 @@ +import pychrono as chrono +import pychrono.irrlicht as irr +from rostok import intexp + +""" Testee object is dumbbell """ +obj_db = intexp.chrono_api.ChTesteeObject() # Create Chrono Testee Object +obj_db.createChronoBodyMeshFromFile('./examples/models/custom/body1.obj', + './examples/models/custom/body1.xml') # create 3D mesh and parameters (grasping poses read from file) +obj_db.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), chrono.ChQuaternionD(1,0,0,0))) + +''' Impact on Testee Object''' +impact = chrono.ChForce() # Create Impact +obj_db.chrono_body_mesh.AddForce(impact) # Attach to ChBody + +IMPACTS_M = [1, 1, 1, 2, 2, 2] # Exam with 6 constant forces and different directions +IMPACTS_DIR = [chrono.ChVectorD(1,0,0), + chrono.ChVectorD(0,1,0), + chrono.ChVectorD(0,0,1), + chrono.ChVectorD(-1,0,0), + chrono.ChVectorD(0,-1,0), + chrono.ChVectorD(0,0,-1)] +IMPACTS_APPLICATION_POINT = [chrono.ChVectorD(0, 0, 0), + chrono.ChVectorD(0, 0, 0), + chrono.ChVectorD(0, 0, 0), + chrono.ChVectorD(0, 0, 0), + chrono.ChVectorD(0, 0, 0), + chrono.ChVectorD(0, 0, 0)] + +tracking_timer = intexp.chrono_api.ImpactTimerParameters(test_bound=len(IMPACTS_DIR), + clock_bound=2.0, step = 1e-3) # Switch timer of impact every 2 sec + + +''' Floor added for clarity ''' +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceNSC()) +floor.SetPos(chrono.ChVectorD(0,-0.005,0)) +floor.SetBodyFixed(True) +floor.SetName('Floor') +floor.GetVisualShape(0).SetColor(chrono.ChColor(80/255, 80/255, 80/255)) + +''' Simulation Solver ''' +system = chrono.ChSystemNSC() +system.Set_G_acc(chrono.ChVectorD(0,0,0)) +system.Add(obj_db.chrono_body_mesh) # Chrono Testee Object added to simulation +system.Add(floor) + +''' PyChrono Visualisation ''' +vis = irr.ChVisualSystemIrrlicht() +vis.AttachSystem(system) +vis.SetWindowSize(1028,768) +vis.SetWindowTitle('Impacts') +vis.SetSymbolScale(3) +vis.Initialize() +vis.AddLight(chrono.ChVectorD(0,10,0), 15) +vis.AddSkyBox() +vis.AddCamera(chrono.ChVectorD(1,0.5,1)) +vis.EnableCollisionShapeDrawing(True) +vis.EnableBodyFrameDrawing(True) + +while vis.Run(): + system.Update() + vis.BeginScene() + vis.Render() + vis.EndScene() + system.DoStepDynamics(1e-3) + + intexp.chrono_api.updateImpact(obj_db, impact, tracking_timer, + IMPACTS_APPLICATION_POINT, IMPACTS_DIR, IMPACTS_M) # Update timer and go through the list of impacts \ No newline at end of file diff --git a/examples/intexp_load_and_show_testee_object.py b/examples/intexp_load_and_show_testee_object.py new file mode 100644 index 00000000..3111fb8a --- /dev/null +++ b/examples/intexp_load_and_show_testee_object.py @@ -0,0 +1,61 @@ +import pychrono as chrono +import pychrono.irrlicht as irr + +from rostok import intexp + +''' Testee object is dumbbell ''' +obj_db = intexp.chrono_api.ChTesteeObject() # Create Chrono Testee Object +error = obj_db.createChronoBodyMeshFromFile('./examples/models/custom/body1.obj', + './examples/models/custom/body1.xml') # create 3D mesh and parameters (grasping poses read from file) +print('Uploded mesh with code error:', error) +# Uncomment the line below for generate new poses +# obj_db.rewriteGraspingPosesList(intexp.poses_generator.genRandomPosesAroundLine(20, 0.04, 0.06, 0.015, 0.18)) +obj_db.rewriteGraspingPosesList(intexp.poses_generator.genCylindricalSurfaceFromPoses(20, 0.07, 0.015, 0.18)) +obj_db.showObjectWithGraspingPoses() +desired_poses = obj_db.getChronoGraspingPosesList() # List of all grasping poses +hand_power_grasp_frame = chrono.ChFrameD(chrono.ChVectorD(0, 0, 0), chrono.Q_ROTATE_Z_TO_Y) # Power grasp point in the ABS +obj_db.setChronoBodyMeshOnPose(desired_poses[0], hand_power_grasp_frame) # Position object for grasp pose into point + +''' Floor added for clarity ''' +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, False, chrono.ChMaterialSurfaceNSC()) +floor.SetPos(chrono.ChVectorD(0,-0.005,0)) +floor.SetBodyFixed(True) +floor.SetName('Floor') +floor.GetVisualShape(0).SetColor(chrono.ChColor(80/255, 80/255, 80/255)) + +''' Simulation Solver ''' +system = chrono.ChSystemNSC() +system.Set_G_acc(chrono.ChVectorD(0,0,0)) +system.Add(obj_db.chrono_body_mesh) # Chrono Testee Object added to simulation +system.Add(floor) +''' PyChrono Visualisation ''' +vis = irr.ChVisualSystemIrrlicht() +vis.AttachSystem(system) +vis.SetWindowSize(1028,768) +vis.SetWindowTitle('Grasping positions generation example') +vis.SetSymbolScale(3) +vis.Initialize() +vis.AddLight(chrono.ChVectorD(0,10,0), 15) +vis.AddSkyBox() +vis.AddCamera(chrono.ChVectorD(1,0.5,1)) +vis.EnableCollisionShapeDrawing(True) +vis.EnableBodyFrameDrawing(True) + +counter = 0 +i = 1 + +while vis.Run(): + system.Update() + vis.BeginScene() + vis.Render() + vis.EndScene() + system.DoStepDynamics(1e-3) + + if counter > 0.5: #Demonstration all poses every 0.5 seconds + obj_db.setChronoBodyMeshOnPose(desired_poses[i], hand_power_grasp_frame) + counter = 0 + i += 1 + if i >= len(desired_poses): + i = 0 + else: + counter += 1e-3 \ No newline at end of file diff --git a/examples/models/custom/body1.mtl b/examples/models/custom/body1.mtl new file mode 100644 index 00000000..68afc6fe --- /dev/null +++ b/examples/models/custom/body1.mtl @@ -0,0 +1,5 @@ +# WaveFront *.mtl file (generated by Autodesk ATF) + +newmtl Steel_-_Satin +Kd 0.627451 0.627451 0.627451 + diff --git a/examples/models/custom/body1.obj b/examples/models/custom/body1.obj new file mode 100644 index 00000000..185bc969 --- /dev/null +++ b/examples/models/custom/body1.obj @@ -0,0 +1,867 @@ +# WaveFront *.obj file (generated by Autodesk ATF) + +mtllib Body1.mtl + +g Body1 + +v 0.015000 0.015000 0.150000 +v 0.015000 -0.015000 0.150000 +v -0.015000 -0.015000 0.150000 +v -0.015000 0.015000 0.150000 +v 0.010000 0.000000 0.150000 +v 0.009709 0.002393 0.150000 +v 0.008855 0.004647 0.150000 +v 0.007485 0.006631 0.150000 +v 0.005681 0.008230 0.150000 +v 0.003546 0.009350 0.150000 +v 0.001205 0.009927 0.150000 +v -0.001205 0.009927 0.150000 +v -0.003546 0.009350 0.150000 +v -0.005681 0.008230 0.150000 +v -0.007485 0.006631 0.150000 +v -0.008855 0.004647 0.150000 +v -0.009709 0.002393 0.150000 +v -0.010000 -0.000000 0.150000 +v -0.009709 -0.002393 0.150000 +v -0.008855 -0.004647 0.150000 +v -0.007485 -0.006631 0.150000 +v -0.005681 -0.008230 0.150000 +v -0.003546 -0.009350 0.150000 +v -0.001205 -0.009927 0.150000 +v 0.001205 -0.009927 0.150000 +v 0.003546 -0.009350 0.150000 +v 0.005681 -0.008230 0.150000 +v 0.007485 -0.006631 0.150000 +v 0.008855 -0.004647 0.150000 +v 0.009709 -0.002393 0.150000 +v 0.015000 -0.015000 0.180000 +v -0.015000 -0.015000 0.180000 +v -0.015000 0.015000 0.180000 +v 0.015000 0.015000 0.180000 +v 0.010000 0.000000 0.050000 +v 0.009709 0.002393 0.050000 +v 0.008855 0.004647 0.050000 +v 0.007485 0.006631 0.050000 +v 0.005681 0.008230 0.050000 +v 0.003546 0.009350 0.050000 +v 0.001205 0.009927 0.050000 +v -0.001205 0.009927 0.050000 +v -0.003546 0.009350 0.050000 +v -0.005681 0.008230 0.050000 +v -0.007485 0.006631 0.050000 +v -0.008855 0.004647 0.050000 +v -0.009709 0.002393 0.050000 +v -0.010000 -0.000000 0.050000 +v -0.009709 -0.002393 0.050000 +v -0.008855 -0.004647 0.050000 +v -0.007485 -0.006631 0.050000 +v -0.005681 -0.008230 0.050000 +v -0.003546 -0.009350 0.050000 +v -0.001205 -0.009927 0.050000 +v 0.001205 -0.009927 0.050000 +v 0.003546 -0.009350 0.050000 +v 0.005681 -0.008230 0.050000 +v 0.007485 -0.006631 0.050000 +v 0.008855 -0.004647 0.050000 +v 0.009709 -0.002393 0.050000 +v -0.010000 -0.000000 0.100000 +v 0.009709 -0.002393 0.100000 +v 0.010000 0.000000 0.100000 +v 0.008855 -0.004647 0.100000 +v 0.007485 -0.006631 0.100000 +v 0.005681 -0.008230 0.100000 +v 0.003546 -0.009350 0.100000 +v 0.001205 -0.009927 0.100000 +v -0.001205 -0.009927 0.100000 +v -0.003546 -0.009350 0.100000 +v -0.005681 -0.008230 0.100000 +v -0.007485 -0.006631 0.100000 +v -0.008855 -0.004647 0.100000 +v -0.009709 -0.002393 0.100000 +v -0.009709 0.002393 0.100000 +v -0.008855 0.004647 0.100000 +v -0.007485 0.006631 0.100000 +v -0.005681 0.008230 0.100000 +v -0.003546 0.009350 0.100000 +v -0.001205 0.009927 0.100000 +v 0.001205 0.009927 0.100000 +v 0.003546 0.009350 0.100000 +v 0.005681 0.008230 0.100000 +v 0.007485 0.006631 0.100000 +v 0.008855 0.004647 0.100000 +v 0.009709 0.002393 0.100000 +v 0.025000 0.000000 0.000000 +v 0.024707 -0.003816 0.000000 +v 0.023835 -0.007543 0.000000 +v 0.022404 -0.011093 0.000000 +v 0.020448 -0.014383 0.000000 +v 0.018013 -0.017336 0.000000 +v 0.015156 -0.019882 0.000000 +v 0.011943 -0.021963 0.000000 +v 0.008450 -0.023529 0.000000 +v 0.004760 -0.024543 0.000000 +v 0.000958 -0.024982 0.000000 +v -0.002867 -0.024835 0.000000 +v -0.006625 -0.024106 0.000000 +v -0.010227 -0.022813 0.000000 +v -0.013589 -0.020984 0.000000 +v -0.016633 -0.018664 0.000000 +v -0.019287 -0.015906 0.000000 +v -0.021489 -0.012775 0.000000 +v -0.023188 -0.009345 0.000000 +v -0.024342 -0.005696 0.000000 +v -0.024927 -0.001914 0.000000 +v -0.024927 0.001914 0.000000 +v -0.024342 0.005696 0.000000 +v -0.023188 0.009345 0.000000 +v -0.021489 0.012775 0.000000 +v -0.019287 0.015906 0.000000 +v -0.016633 0.018664 0.000000 +v -0.013589 0.020984 0.000000 +v -0.010227 0.022813 0.000000 +v -0.006625 0.024106 0.000000 +v -0.002867 0.024835 0.000000 +v 0.000958 0.024982 0.000000 +v 0.004760 0.024543 0.000000 +v 0.008450 0.023529 0.000000 +v 0.011943 0.021963 0.000000 +v 0.015156 0.019882 0.000000 +v 0.018013 0.017336 0.000000 +v 0.020448 0.014383 0.000000 +v 0.022404 0.011093 0.000000 +v 0.023835 0.007543 0.000000 +v 0.024707 0.003816 0.000000 +vt 1.500000 1.500000 0.000000 +vt 1.000000 0.000000 0.000000 +vt 1.500000 -1.500000 0.000000 +vt 0.970942 -0.239316 0.000000 +vt 0.885456 -0.464723 0.000000 +vt -1.500000 1.500000 0.000000 +vt 0.120537 0.992709 0.000000 +vt 0.354605 0.935016 0.000000 +vt 0.568065 0.822984 0.000000 +vt -1.500000 -1.500000 0.000000 +vt -1.000000 0.000000 0.000000 +vt -0.970942 0.239316 0.000000 +vt -0.885456 0.464723 0.000000 +vt -0.120537 -0.992709 0.000000 +vt -0.354605 -0.935016 0.000000 +vt -0.568065 -0.822984 0.000000 +vt 0.748511 -0.663123 0.000000 +vt 0.568065 -0.822984 0.000000 +vt 0.354605 -0.935016 0.000000 +vt 0.120537 -0.992709 0.000000 +vt -0.748511 -0.663123 0.000000 +vt -0.885456 -0.464723 0.000000 +vt -0.970942 -0.239316 0.000000 +vt -0.748511 0.663123 0.000000 +vt -0.568065 0.822984 0.000000 +vt -0.354605 0.935016 0.000000 +vt -0.120537 0.992709 0.000000 +vt 0.748511 0.663123 0.000000 +vt 0.885456 0.464723 0.000000 +vt 0.970942 0.239316 0.000000 +vt 0.000000 0.000000 0.000000 +vt 3.000000 0.000000 0.000000 +vt 0.000000 3.000000 0.000000 +vt 3.000000 3.000000 0.000000 +vt 0.000000 0.000000 0.000000 +vt 3.000000 0.000000 0.000000 +vt 0.000000 3.000000 0.000000 +vt 3.000000 3.000000 0.000000 +vt 0.000000 0.000000 0.000000 +vt 3.000000 0.000000 0.000000 +vt 0.000000 3.000000 0.000000 +vt 3.000000 3.000000 0.000000 +vt 0.000000 0.000000 0.000000 +vt 3.000000 0.000000 0.000000 +vt 0.000000 3.000000 0.000000 +vt 3.000000 3.000000 0.000000 +vt 1.500000 1.500000 0.000000 +vt -1.500000 1.500000 0.000000 +vt 1.500000 -1.500000 0.000000 +vt -1.500000 -1.500000 0.000000 +vt -10.000000 -0.241661 0.000000 +vt -5.000000 -0.241661 0.000000 +vt -10.000000 0.000000 0.000000 +vt -5.000000 0.000000 0.000000 +vt -10.000000 0.241661 0.000000 +vt -5.000000 0.241661 0.000000 +vt -10.000000 0.483322 0.000000 +vt -5.000000 0.483322 0.000000 +vt -10.000000 0.724983 0.000000 +vt -5.000000 0.724983 0.000000 +vt -10.000000 0.966644 0.000000 +vt -5.000000 0.966644 0.000000 +vt -10.000000 1.208305 0.000000 +vt -5.000000 1.208305 0.000000 +vt -10.000000 1.449966 0.000000 +vt -5.000000 1.449966 0.000000 +vt -10.000000 1.691627 0.000000 +vt -5.000000 1.691627 0.000000 +vt -10.000000 1.933288 0.000000 +vt -5.000000 1.933288 0.000000 +vt -10.000000 2.174949 0.000000 +vt -5.000000 2.174949 0.000000 +vt -10.000000 2.416610 0.000000 +vt -5.000000 2.416610 0.000000 +vt -10.000000 2.658271 0.000000 +vt -5.000000 2.658271 0.000000 +vt -10.000000 2.899932 0.000000 +vt -5.000000 2.899932 0.000000 +vt -10.000000 3.141593 0.000000 +vt -5.000000 3.141593 0.000000 +vt -10.000000 -3.141593 0.000000 +vt -5.000000 -3.141593 0.000000 +vt -10.000000 -2.899932 0.000000 +vt -5.000000 -2.899932 0.000000 +vt -10.000000 -2.658271 0.000000 +vt -5.000000 -2.658271 0.000000 +vt -10.000000 -2.416610 0.000000 +vt -5.000000 -2.416610 0.000000 +vt -10.000000 -2.174949 0.000000 +vt -5.000000 -2.174949 0.000000 +vt -10.000000 -1.933288 0.000000 +vt -5.000000 -1.933288 0.000000 +vt -10.000000 -1.691627 0.000000 +vt -5.000000 -1.691627 0.000000 +vt -10.000000 -1.449966 0.000000 +vt -5.000000 -1.449966 0.000000 +vt -10.000000 -1.208305 0.000000 +vt -5.000000 -1.208305 0.000000 +vt -10.000000 -0.966644 0.000000 +vt -5.000000 -0.966644 0.000000 +vt -10.000000 -0.724983 0.000000 +vt -5.000000 -0.724983 0.000000 +vt -10.000000 -0.483322 0.000000 +vt -5.000000 -0.483322 0.000000 +vt -0.000000 0.241661 0.000000 +vt -0.000000 0.000000 0.000000 +vt -0.000000 -0.241661 0.000000 +vt -0.000000 -0.483322 0.000000 +vt -0.000000 -0.724983 0.000000 +vt -0.000000 -0.966644 0.000000 +vt -0.000000 -1.208305 0.000000 +vt -0.000000 -1.449966 0.000000 +vt -0.000000 -1.691627 0.000000 +vt -0.000000 -1.933288 0.000000 +vt -0.000000 -2.174949 0.000000 +vt -0.000000 -2.416610 0.000000 +vt -0.000000 -2.658271 0.000000 +vt -0.000000 -2.899932 0.000000 +vt -0.000000 -3.141593 0.000000 +vt -0.000000 3.141593 0.000000 +vt -0.000000 2.899932 0.000000 +vt -0.000000 2.658271 0.000000 +vt -0.000000 2.416610 0.000000 +vt -0.000000 2.174949 0.000000 +vt -0.000000 1.933288 0.000000 +vt -0.000000 1.691627 0.000000 +vt -0.000000 1.449966 0.000000 +vt -0.000000 1.208305 0.000000 +vt -0.000000 0.966644 0.000000 +vt -0.000000 0.724983 0.000000 +vt -0.000000 0.483322 0.000000 +vt -2.470701 0.381623 0.000000 +vt -2.500000 0.000000 0.000000 +vt 2.148924 -1.277547 0.000000 +vt -2.470701 -0.381623 0.000000 +vt -2.383491 -0.754301 0.000000 +vt -2.240414 -1.109300 0.000000 +vt -2.044823 -1.438297 0.000000 +vt 1.928723 -1.590606 0.000000 +vt -1.801304 -1.733581 0.000000 +vt 1.663314 -1.866383 0.000000 +vt -1.515564 -1.988232 0.000000 +vt 1.358919 -2.098414 0.000000 +vt -1.194300 -2.196281 0.000000 +vt 1.022672 -2.281259 0.000000 +vt -0.845042 -2.352850 0.000000 +vt 0.662454 -2.410634 0.000000 +vt -0.475978 -2.454271 0.000000 +vt 0.286709 -2.483505 0.000000 +vt -0.095757 -2.498165 0.000000 +vt 2.318756 -0.934543 0.000000 +vt 0.286709 2.483505 0.000000 +vt -0.095757 2.498165 0.000000 +vt -0.475978 2.454271 0.000000 +vt 2.434239 -0.569634 0.000000 +vt 2.318756 0.934543 0.000000 +vt 2.148924 1.277547 0.000000 +vt 1.928723 1.590606 0.000000 +vt 2.434239 0.569634 0.000000 +vt 2.492665 -0.191373 0.000000 +vt 2.492665 0.191373 0.000000 +vt 1.663314 1.866383 0.000000 +vt 1.358919 2.098414 0.000000 +vt 1.022672 2.281259 0.000000 +vt 0.662454 2.410634 0.000000 +vt -0.845042 2.352850 0.000000 +vt -1.194300 2.196281 0.000000 +vt -1.515564 1.988232 0.000000 +vt -1.801304 1.733581 0.000000 +vt -2.044823 1.438297 0.000000 +vt -2.240414 1.109300 0.000000 +vt -2.383491 0.754301 0.000000 +vt -0.000000 -0.268185 0.000000 +vt -0.000000 0.000000 0.000000 +vt -5.220153 0.000000 0.000000 +vt -0.000000 0.268185 0.000000 +vt -5.220153 0.422907 0.000000 +vt 0.000000 0.536369 0.000000 +vt -5.220153 0.845813 0.000000 +vt -0.000000 0.804554 0.000000 +vt -0.000000 1.072739 0.000000 +vt -5.220153 1.268720 0.000000 +vt -0.000000 1.340924 0.000000 +vt -0.000000 1.609108 0.000000 +vt -5.220153 1.691627 0.000000 +vt -0.000000 1.877293 0.000000 +vt -5.220153 2.114534 0.000000 +vt -0.000000 2.145478 0.000000 +vt -0.000000 2.413663 0.000000 +vt -5.220153 2.537440 0.000000 +vt -0.000000 2.681847 0.000000 +vt -5.220153 2.960347 0.000000 +vt -0.000000 2.950032 0.000000 +vt -0.000000 3.218217 0.000000 +vt -5.220153 3.383254 0.000000 +vt -0.000000 3.486402 0.000000 +vt -5.220153 3.806160 0.000000 +vt -0.000000 3.754586 0.000000 +vt -0.000000 4.022771 0.000000 +vt -5.220153 4.229067 0.000000 +vt -0.000000 4.290956 0.000000 +vt -0.000000 4.559141 0.000000 +vt -5.220153 4.651974 0.000000 +vt -0.000000 4.827325 0.000000 +vt -5.220153 5.074880 0.000000 +vt -0.000000 5.095510 0.000000 +vt -0.000000 5.363695 0.000000 +vt -5.220153 5.497787 0.000000 +vt -5.220153 -5.497787 0.000000 +vt -0.000000 -5.631880 0.000000 +vt -0.000000 -5.363695 0.000000 +vt -5.220153 -5.074880 0.000000 +vt -0.000000 -5.095510 0.000000 +vt -0.000000 -4.827325 0.000000 +vt -5.220153 -4.651974 0.000000 +vt -0.000000 -4.559141 0.000000 +vt -5.220153 -4.229067 0.000000 +vt -0.000000 -4.290956 0.000000 +vt -0.000000 -4.022771 0.000000 +vt -5.220153 -3.806160 0.000000 +vt -0.000000 -3.754586 0.000000 +vt 0.000000 -3.486402 0.000000 +vt -5.220153 -3.383254 0.000000 +vt -0.000000 -3.218217 0.000000 +vt -5.220153 -2.960347 0.000000 +vt -0.000000 -2.950032 0.000000 +vt -0.000000 -2.681847 0.000000 +vt -5.220153 -2.537440 0.000000 +vt -0.000000 -2.413663 0.000000 +vt -5.220153 -2.114534 0.000000 +vt -0.000000 -2.145478 0.000000 +vt -0.000000 -1.877293 0.000000 +vt -5.220153 -1.691627 0.000000 +vt -0.000000 -1.609108 0.000000 +vt -5.220153 -1.268720 0.000000 +vt -0.000000 -1.340924 0.000000 +vt -0.000000 -1.072739 0.000000 +vt -5.220153 -0.845813 0.000000 +vt -0.000000 -0.804554 0.000000 +vt 0.000000 -0.536369 0.000000 +vt -5.220153 -0.422907 0.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 -100.000000 0.000000 +vn 0.000000 -100.000000 0.000000 +vn 0.000000 -100.000000 0.000000 +vn 0.000000 -100.000000 0.000000 +vn -100.000000 0.000000 0.000000 +vn -100.000000 0.000000 0.000000 +vn -100.000000 0.000000 0.000000 +vn -100.000000 0.000000 0.000000 +vn 0.000000 100.000000 0.000000 +vn 0.000000 100.000000 0.000000 +vn 0.000000 100.000000 0.000000 +vn 0.000000 100.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 97.094182 -23.931566 0.000000 +vn 97.094182 -23.931566 0.000000 +vn 100.000000 0.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 97.094182 23.931566 0.000000 +vn 97.094182 23.931566 0.000000 +vn 88.545603 46.472317 0.000000 +vn 88.545603 46.472317 0.000000 +vn 74.851075 66.312266 0.000000 +vn 74.851075 66.312266 0.000000 +vn 56.806475 82.298387 0.000000 +vn 56.806475 82.298387 0.000000 +vn 35.460489 93.501624 0.000000 +vn 35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 12.053668 99.270887 0.000000 +vn -12.053668 99.270887 -0.000000 +vn -12.053668 99.270887 -0.000000 +vn -35.460489 93.501624 -0.000000 +vn -35.460489 93.501624 -0.000000 +vn -56.806475 82.298387 -0.000000 +vn -56.806475 82.298387 -0.000000 +vn -74.851075 66.312266 -0.000000 +vn -74.851075 66.312266 -0.000000 +vn -88.545603 46.472317 -0.000000 +vn -88.545603 46.472317 -0.000000 +vn -97.094182 23.931566 -0.000000 +vn -97.094182 23.931566 -0.000000 +vn -100.000000 0.000000 -0.000000 +vn -100.000000 0.000000 -0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -88.545603 -46.472317 0.000000 +vn -88.545603 -46.472317 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -56.806475 -82.298387 0.000000 +vn -56.806475 -82.298387 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -12.053668 -99.270887 0.000000 +vn -12.053668 -99.270887 0.000000 +vn 12.053668 -99.270887 0.000000 +vn 12.053668 -99.270887 0.000000 +vn 35.460489 -93.501624 0.000000 +vn 35.460489 -93.501624 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 74.851075 -66.312266 0.000000 +vn 74.851075 -66.312266 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 97.094182 23.931566 0.000000 +vn 100.000000 0.000000 0.000000 +vn 97.094182 -23.931566 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 74.851075 -66.312266 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 35.460489 -93.501624 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -56.806475 -82.298387 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -88.545603 -46.472317 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -100.000000 -0.000000 0.000000 +vn -97.094182 23.931566 -0.000000 +vn -88.545603 46.472317 -0.000000 +vn -74.851075 66.312266 -0.000000 +vn -56.806475 82.298387 -0.000000 +vn -35.460489 93.501624 -0.000000 +vn -12.053668 99.270887 -0.000000 +vn 12.053668 99.270887 0.000000 +vn 35.460489 93.501624 0.000000 +vn 56.806475 82.298387 0.000000 +vn 74.851075 66.312266 0.000000 +vn 88.545603 46.472317 0.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 94.660097 -14.621150 28.734789 +vn 95.782629 -0.000000 28.734789 +vn 95.782629 -0.000000 28.734789 +vn 94.660097 14.621150 28.734789 +vn 92.999359 22.922283 28.734789 +vn 91.318812 28.899592 28.734789 +vn 84.811306 44.512407 28.734789 +vn 85.837093 42.500652 28.734789 +vn 78.343424 55.105534 28.734789 +vn 71.694327 63.515631 28.734789 +vn 69.013452 66.418788 28.734789 +vn 58.065863 76.175242 28.734789 +vn 54.410735 78.827558 28.734789 +vn 45.757260 84.146213 28.734789 +vn 33.964988 89.558313 28.734789 +vn 32.376145 90.144868 28.734789 +vn 18.236161 94.030603 28.734789 +vn 11.545320 95.084265 28.734789 +vn 3.668737 95.712341 28.734789 +vn -11.545320 95.084265 28.734789 +vn -10.984680 95.150663 28.734789 +vn -25.380625 92.358734 28.734789 +vn -33.964988 89.558313 28.734789 +vn -39.181669 87.401995 28.734789 +vn -54.410735 78.827558 28.734789 +vn -52.064329 80.396627 28.734789 +vn -63.726644 71.506830 28.734789 +vn -71.694327 63.515631 28.734789 +vn -73.895262 60.940974 28.734789 +vn -82.331836 48.946712 28.734789 +vn -84.811306 44.512407 28.734789 +vn -88.838623 35.805182 28.734789 +vn -92.999359 22.922283 28.734789 +vn -93.263107 21.824408 28.734789 +vn -95.501583 7.332089 28.734789 +vn -95.782629 0.000000 28.734789 +vn -95.501583 -7.332089 28.734789 +vn -92.999359 -22.922283 28.734789 +vn -93.263107 -21.824408 28.734789 +vn -88.838623 -35.805182 28.734789 +vn -84.811306 -44.512407 28.734789 +vn -82.331836 -48.946712 28.734789 +vn -71.694327 -63.515631 28.734789 +vn -73.895262 -60.940974 28.734789 +vn -63.726644 -71.506830 28.734789 +vn -54.410735 -78.827558 28.734789 +vn -52.064329 -80.396627 28.734789 +vn -39.181669 -87.401995 28.734789 +vn -33.964988 -89.558313 28.734789 +vn -25.380625 -92.358734 28.734789 +vn -11.545320 -95.084265 28.734789 +vn -10.984680 -95.150663 28.734789 +vn 3.668737 -95.712341 28.734789 +vn 11.545320 -95.084265 28.734789 +vn 18.236161 -94.030603 28.734789 +vn 33.964988 -89.558313 28.734789 +vn 32.376145 -90.144868 28.734789 +vn 45.757260 -84.146213 28.734789 +vn 54.410735 -78.827558 28.734789 +vn 58.065863 -76.175242 28.734789 +vn 71.694327 -63.515631 28.734789 +vn 69.013452 -66.418788 28.734789 +vn 78.343424 -55.105534 28.734789 +vn 84.811306 -44.512407 28.734789 +vn 85.837093 -42.500652 28.734789 +vn 91.318812 -28.899592 28.734789 +vn 92.999359 -22.922283 28.734789 +usemtl Steel_-_Satin +f 2/1/1 5/2/2 1/3/3 +f 1/3/3 5/2/2 6/4/4 +f 1/3/3 6/4/4 7/5/5 +f 3/6/6 25/7/7 2/1/1 +f 2/1/1 25/7/7 26/8/8 +f 2/1/1 26/8/8 27/9/9 +f 4/10/10 18/11/11 3/6/6 +f 3/6/6 18/11/11 19/12/12 +f 3/6/6 19/12/12 20/13/13 +f 1/3/3 12/14/14 4/10/10 +f 4/10/10 12/14/14 13/15/15 +f 4/10/10 13/15/15 14/16/16 +f 7/5/5 8/17/17 1/3/3 +f 1/3/3 8/17/17 9/18/18 +f 1/3/3 9/18/18 10/19/19 +f 10/19/19 11/20/20 1/3/3 +f 1/3/3 11/20/20 12/14/14 +f 14/16/16 15/21/21 4/10/10 +f 4/10/10 15/21/21 16/22/22 +f 4/10/10 16/22/22 17/23/23 +f 17/23/23 18/11/11 4/10/10 +f 20/13/13 21/24/24 3/6/6 +f 3/6/6 21/24/24 22/25/25 +f 3/6/6 22/25/25 23/26/26 +f 23/26/26 24/27/27 3/6/6 +f 3/6/6 24/27/27 25/7/7 +f 27/9/9 28/28/28 2/1/1 +f 2/1/1 28/28/28 29/29/29 +f 2/1/1 29/29/29 30/30/30 +f 30/30/30 5/2/2 2/1/1 +f 3/31/31 2/32/32 32/33/33 +f 32/33/33 2/32/32 31/34/34 +f 4/35/35 3/36/36 33/37/37 +f 33/37/37 3/36/36 32/38/38 +f 1/39/39 4/40/40 34/41/41 +f 34/41/41 4/40/40 33/42/42 +f 2/43/43 1/44/44 31/45/45 +f 31/45/45 1/44/44 34/46/46 +f 34/47/47 33/48/48 31/49/49 +f 31/49/49 33/48/48 32/50/50 +f 30/51/51 62/52/52 5/53/53 +f 5/53/53 62/52/52 63/54/54 +f 5/53/53 63/54/54 6/55/55 +f 6/55/55 63/54/54 86/56/56 +f 6/55/55 86/56/56 7/57/57 +f 7/57/57 86/56/56 85/58/58 +f 7/57/57 85/58/58 8/59/59 +f 8/59/59 85/58/58 84/60/60 +f 8/59/59 84/60/60 9/61/61 +f 9/61/61 84/60/60 83/62/62 +f 9/61/61 83/62/62 10/63/63 +f 10/63/63 83/62/62 82/64/64 +f 10/63/63 82/64/64 11/65/65 +f 11/65/65 82/64/64 81/66/66 +f 11/65/65 81/66/66 12/67/67 +f 12/67/67 81/66/66 80/68/68 +f 12/67/67 80/68/68 13/69/69 +f 13/69/69 80/68/68 79/70/70 +f 13/69/69 79/70/70 14/71/71 +f 14/71/71 79/70/70 78/72/72 +f 14/71/71 78/72/72 15/73/73 +f 15/73/73 78/72/72 77/74/74 +f 15/73/73 77/74/74 16/75/75 +f 16/75/75 77/74/74 76/76/76 +f 16/75/75 76/76/76 17/77/77 +f 17/77/77 76/76/76 75/78/78 +f 17/77/77 75/78/78 18/79/79 +f 18/79/79 75/78/78 61/80/80 +f 18/81/79 61/82/80 19/83/81 +f 19/83/81 61/82/80 74/84/82 +f 19/83/81 74/84/82 20/85/83 +f 20/85/83 74/84/82 73/86/84 +f 20/85/83 73/86/84 21/87/85 +f 21/87/85 73/86/84 72/88/86 +f 21/87/85 72/88/86 22/89/87 +f 22/89/87 72/88/86 71/90/88 +f 22/89/87 71/90/88 23/91/89 +f 23/91/89 71/90/88 70/92/90 +f 23/91/89 70/92/90 24/93/91 +f 24/93/91 70/92/90 69/94/92 +f 24/93/91 69/94/92 25/95/93 +f 25/95/93 69/94/92 68/96/94 +f 25/95/93 68/96/94 26/97/95 +f 26/97/95 68/96/94 67/98/96 +f 26/97/95 67/98/96 27/99/97 +f 27/99/97 67/98/96 66/100/98 +f 27/99/97 66/100/98 28/101/99 +f 28/101/99 66/100/98 65/102/100 +f 28/101/99 65/102/100 29/103/101 +f 29/103/101 65/102/100 64/104/102 +f 29/103/101 64/104/102 30/51/51 +f 30/51/51 64/104/102 62/52/52 +f 36/105/103 86/56/56 35/106/104 +f 35/106/104 86/56/56 63/54/54 +f 35/106/104 63/54/54 60/107/105 +f 60/107/105 63/54/54 62/52/52 +f 60/107/105 62/52/52 59/108/106 +f 59/108/106 62/52/52 64/104/102 +f 59/108/106 64/104/102 58/109/107 +f 58/109/107 64/104/102 65/102/100 +f 58/109/107 65/102/100 57/110/108 +f 57/110/108 65/102/100 66/100/98 +f 57/110/108 66/100/98 56/111/109 +f 56/111/109 66/100/98 67/98/96 +f 56/111/109 67/98/96 55/112/110 +f 55/112/110 67/98/96 68/96/94 +f 55/112/110 68/96/94 54/113/111 +f 54/113/111 68/96/94 69/94/92 +f 54/113/111 69/94/92 53/114/112 +f 53/114/112 69/94/92 70/92/90 +f 53/114/112 70/92/90 52/115/113 +f 52/115/113 70/92/90 71/90/88 +f 52/115/113 71/90/88 51/116/114 +f 51/116/114 71/90/88 72/88/86 +f 51/116/114 72/88/86 50/117/115 +f 50/117/115 72/88/86 73/86/84 +f 50/117/115 73/86/84 49/118/116 +f 49/118/116 73/86/84 74/84/82 +f 49/118/116 74/84/82 48/119/117 +f 48/119/117 74/84/82 61/82/80 +f 48/120/117 61/80/80 47/121/118 +f 47/121/118 61/80/80 75/78/78 +f 47/121/118 75/78/78 46/122/119 +f 46/122/119 75/78/78 76/76/76 +f 46/122/119 76/76/76 45/123/120 +f 45/123/120 76/76/76 77/74/74 +f 45/123/120 77/74/74 44/124/121 +f 44/124/121 77/74/74 78/72/72 +f 44/124/121 78/72/72 43/125/122 +f 43/125/122 78/72/72 79/70/70 +f 43/125/122 79/70/70 42/126/123 +f 42/126/123 79/70/70 80/68/68 +f 42/126/123 80/68/68 41/127/124 +f 41/127/124 80/68/68 81/66/66 +f 41/127/124 81/66/66 40/128/125 +f 40/128/125 81/66/66 82/64/64 +f 40/128/125 82/64/64 39/129/126 +f 39/129/126 82/64/64 83/62/62 +f 39/129/126 83/62/62 38/130/127 +f 38/130/127 83/62/62 84/60/60 +f 38/130/127 84/60/60 37/131/128 +f 37/131/128 84/60/60 85/58/58 +f 37/131/128 85/58/58 36/105/103 +f 36/105/103 85/58/58 86/56/56 +f 127/132/129 87/133/130 104/134/131 +f 104/134/131 87/133/130 88/135/132 +f 104/134/131 88/135/132 89/136/133 +f 89/136/133 90/137/134 104/134/131 +f 104/134/131 90/137/134 91/138/135 +f 104/134/131 91/138/135 103/139/136 +f 103/139/136 91/138/135 92/140/137 +f 103/139/136 92/140/137 102/141/138 +f 102/141/138 92/140/137 93/142/139 +f 102/141/138 93/142/139 101/143/140 +f 101/143/140 93/142/139 94/144/141 +f 101/143/140 94/144/141 100/145/142 +f 100/145/142 94/144/141 95/146/143 +f 100/145/142 95/146/143 99/147/144 +f 99/147/144 95/146/143 96/148/145 +f 99/147/144 96/148/145 98/149/146 +f 98/149/146 96/148/145 97/150/147 +f 105/151/148 117/152/149 104/134/131 +f 104/134/131 117/152/149 118/153/150 +f 104/134/131 118/153/150 119/154/151 +f 106/155/152 110/156/153 105/151/148 +f 105/151/148 110/156/153 111/157/154 +f 105/151/148 111/157/154 112/158/155 +f 110/156/153 106/155/152 109/159/156 +f 109/159/156 106/155/152 107/160/157 +f 109/159/156 107/160/157 108/161/158 +f 112/158/155 113/162/159 105/151/148 +f 105/151/148 113/162/159 114/163/160 +f 105/151/148 114/163/160 115/164/161 +f 115/164/161 116/165/162 105/151/148 +f 105/151/148 116/165/162 117/152/149 +f 119/154/151 120/166/163 104/134/131 +f 104/134/131 120/166/163 121/167/164 +f 104/134/131 121/167/164 122/168/165 +f 122/168/165 123/169/166 104/134/131 +f 104/134/131 123/169/166 124/170/167 +f 104/134/131 124/170/167 125/171/168 +f 125/171/168 126/172/169 104/134/131 +f 104/134/131 126/172/169 127/132/129 +f 88/173/170 87/174/171 35/175/172 +f 35/175/172 87/174/171 127/176/173 +f 35/175/172 127/176/173 36/177/174 +f 36/177/174 127/176/173 126/178/175 +f 36/177/174 126/178/175 37/179/176 +f 37/179/176 126/178/175 125/180/177 +f 37/179/176 125/180/177 124/181/178 +f 37/179/176 124/181/178 38/182/179 +f 38/182/179 124/181/178 123/183/180 +f 38/182/179 123/183/180 122/184/181 +f 38/182/179 122/184/181 39/185/182 +f 39/185/182 122/184/181 121/186/183 +f 39/185/182 121/186/183 40/187/184 +f 40/187/184 121/186/183 120/188/185 +f 40/187/184 120/188/185 119/189/186 +f 40/187/184 119/189/186 41/190/187 +f 41/190/187 119/189/186 118/191/188 +f 41/190/187 118/191/188 42/192/189 +f 42/192/189 118/191/188 117/193/190 +f 42/192/189 117/193/190 116/194/191 +f 42/192/189 116/194/191 43/195/192 +f 43/195/192 116/194/191 115/196/193 +f 43/195/192 115/196/193 44/197/194 +f 44/197/194 115/196/193 114/198/195 +f 44/197/194 114/198/195 113/199/196 +f 44/197/194 113/199/196 45/200/197 +f 45/200/197 113/199/196 112/201/198 +f 45/200/197 112/201/198 111/202/199 +f 45/200/197 111/202/199 46/203/200 +f 46/203/200 111/202/199 110/204/201 +f 46/203/200 110/204/201 47/205/202 +f 47/205/202 110/204/201 109/206/203 +f 47/205/202 109/206/203 108/207/204 +f 47/205/202 108/207/204 48/208/205 +f 48/209/205 108/210/204 107/211/206 +f 48/209/205 107/211/206 49/212/207 +f 49/212/207 107/211/206 106/213/208 +f 49/212/207 106/213/208 105/214/209 +f 49/212/207 105/214/209 50/215/210 +f 50/215/210 105/214/209 104/216/211 +f 50/215/210 104/216/211 51/217/212 +f 51/217/212 104/216/211 103/218/213 +f 51/217/212 103/218/213 102/219/214 +f 51/217/212 102/219/214 52/220/215 +f 52/220/215 102/219/214 101/221/216 +f 52/220/215 101/221/216 100/222/217 +f 52/220/215 100/222/217 53/223/218 +f 53/223/218 100/222/217 99/224/219 +f 53/223/218 99/224/219 54/225/220 +f 54/225/220 99/224/219 98/226/221 +f 54/225/220 98/226/221 97/227/222 +f 54/225/220 97/227/222 55/228/223 +f 55/228/223 97/227/222 96/229/224 +f 55/228/223 96/229/224 56/230/225 +f 56/230/225 96/229/224 95/231/226 +f 56/230/225 95/231/226 94/232/227 +f 56/230/225 94/232/227 57/233/228 +f 57/233/228 94/232/227 93/234/229 +f 57/233/228 93/234/229 58/235/230 +f 58/235/230 93/234/229 92/236/231 +f 58/235/230 92/236/231 91/237/232 +f 58/235/230 91/237/232 59/238/233 +f 59/238/233 91/237/232 90/239/234 +f 59/238/233 90/239/234 89/240/235 +f 59/238/233 89/240/235 60/241/236 +f 60/241/236 89/240/235 88/173/170 +f 60/241/236 88/173/170 35/175/172 +# 127 vertices +# 241 texture params +# 236 normals +# 250 facets + +# 1 groups diff --git a/examples/models/custom/body1.xml b/examples/models/custom/body1.xml new file mode 100644 index 00000000..3ed8f7d2 --- /dev/null +++ b/examples/models/custom/body1.xml @@ -0,0 +1,7 @@ + + + body1.obj + 1000 + 0.25 + + \ No newline at end of file diff --git a/rostok.egg-info/PKG-INFO b/rostok.egg-info/PKG-INFO new file mode 100644 index 00000000..8a2212bb --- /dev/null +++ b/rostok.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 2.1 +Name: rostok +Version: 0.0.0 +Summary: UNKNOWN +Home-page: UNKNOWN +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + diff --git a/rostok.egg-info/SOURCES.txt b/rostok.egg-info/SOURCES.txt new file mode 100644 index 00000000..f7ed39bf --- /dev/null +++ b/rostok.egg-info/SOURCES.txt @@ -0,0 +1,6 @@ +README.md +setup.py +rostok.egg-info/PKG-INFO +rostok.egg-info/SOURCES.txt +rostok.egg-info/dependency_links.txt +rostok.egg-info/top_level.txt \ No newline at end of file diff --git a/rostok.egg-info/dependency_links.txt b/rostok.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/rostok.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/rostok.egg-info/top_level.txt b/rostok.egg-info/top_level.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/rostok.egg-info/top_level.txt @@ -0,0 +1 @@ + diff --git a/rostok/intexp/__init__.py b/rostok/intexp/__init__.py new file mode 100644 index 00000000..f55bad0a --- /dev/null +++ b/rostok/intexp/__init__.py @@ -0,0 +1,4 @@ +from . import chrono_api + +from . import poses_generator +from . import testee \ No newline at end of file diff --git a/rostok/intexp/chrono_api.py b/rostok/intexp/chrono_api.py new file mode 100644 index 00000000..c44ff070 --- /dev/null +++ b/rostok/intexp/chrono_api.py @@ -0,0 +1,172 @@ +import pychrono as chrono +from numpy import asarray + +from .testee import TesteeObject, ErrorReport +from dataclasses import dataclass + +class ChTesteeObject(TesteeObject): + """Class for creation of rigid bodies from a mesh shape for PyChrono Engine. + Supported files for loading mesh - .obj, for loading physicals parameters - .xml + + Init State: + - empty mesh + - empty physical parameters + - empty poses + """ + chrono_body_mesh = None + chrono_material = None + + def __init__(self) -> None: + super().__init__() + self.chrono_body_mesh = None + self.chrono_material = None + pass + + def createChronoBodyMeshFromFile(self, obj_fname: str, xml_fname: str): + """ Method created ChBodyEasyMesh object from .obj file with physicals + parameters from .xml file + + Args: + obj_fname (str): mesh file like name 'body1.obj' or path '../folder1/body1.obj' + xml_fname (str): physical parameters like name 'body1_param.xml' or path '../folder1/body1_param.xml' + + TODO: Error cheking when file imported + """ + error1 = super().loadObject3DMesh(obj_fname) + error2 = super().loadObjectDescription(xml_fname) + if (error1 is ErrorReport.NONE) and (error2 is ErrorReport.NONE): + self.chrono_material = chrono.ChMaterialSurfaceNSC() + self.chrono_material.SetFriction(super().getMuFriction()) + self.chrono_material.SetDampingF(0.001) # GAG, need import from xml config + + self.chrono_body_mesh = chrono.ChBodyEasyMesh(self.__convert03DMeshToChTriangleMeshConnected(), + self.getDensity(), + True, + True, + True, + self.chrono_material) + self.chrono_body_mesh.SetName(super().getName()) + self.chrono_body_mesh.GetVisualShape(0).SetColor(chrono.ChColor(240/255, 100/255, 55/255)) + print('OK') + + return (error1, error1) + + def __convert03DMeshToChTriangleMeshConnected(self) -> chrono.ChTriangleMeshConnected: + """Converting the spatial mesh format from O3D to ChTriangleMeshConnected to create + ChBodyEasyMesh simulation object + + Returns: + chrono.ChTriangleMeshConnected: The spatial mesh for describing ChBodyEasyMesh + """ + triangles = asarray(self._mesh.triangles) + vertices = asarray(self._mesh.vertices) + ch_mesh = chrono.ChTriangleMeshConnected() + + for item in triangles: + x, y, z = 0, 1, 2 + vert1_index, vert2_index, vert3_index = item[0], item[1], item[2] + ch_mesh.addTriangle(chrono.ChVectorD(vertices[vert1_index][x], vertices[vert1_index][y], vertices[vert1_index][z]), + chrono.ChVectorD(vertices[vert2_index][x], vertices[vert2_index][y], vertices[vert2_index][z]), + chrono.ChVectorD(vertices[vert3_index][x], vertices[vert3_index][y], vertices[vert3_index][z])) + + return ch_mesh + + def getChronoGraspingPosesList(self) -> list[chrono.ChFrameD]: + """Returned list of all poses for interaction with testee object. + All poses described like position + rotation in local coordinates of object. + + Returns: + list[chrono.ChFrameD]: Vecotor and Quaternion of poses + """ + poses = self.getGraspingPosesList() + print(len(poses)) + chrono_poses = [] + for i in poses: + chrono_poses.append(chrono.ChFrameD(chrono.ChVectorD(i[0][0], i[0][1], i[0][2]), + chrono.ChQuaternionD(i[1][0], i[1][1], i[1][2], i[1][3]))) + return chrono_poses + + def setChronoBodyMeshOnPose(self, pose_to_ref: chrono.ChFrameD, pose_to_abs: chrono.ChFrameD): + """Positions the object in the capture pose relative to the global coordinate system + + Args: + pose_to_ref (chrono.ChFrameD): gripping pose in local coordinate system + pose_to_abs (chrono.ChFrameD): place in global coordinate system + """ + cog_to_ref = self.chrono_body_mesh.GetFrame_COG_to_REF() + myX = pose_to_ref.GetInverse() * cog_to_ref + desired_cog_to_abs = pose_to_abs * myX + self.chrono_body_mesh.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) + self.chrono_body_mesh.SetNoSpeedNoAcceleration() + + def setChronoBodyMeshRefFrameInPoint(self, desired_ref_to_abs_point: chrono.ChFrameD): + """Positions the object relative local frame to the global coordinate system + + Args: + desired_ref_to_abs_point (chrono.ChFrameD): place in global coordinate system + """ + cog_to_ref = self.chrono_body_mesh.GetFrame_COG_to_REF() + desired_cog_to_abs = desired_ref_to_abs_point * cog_to_ref + self.chrono_body_mesh.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) + self.chrono_body_mesh.SetNoSpeedNoAcceleration() + + def addStaticImpactToChronoBodyMesh(self): + pass + + def addSineImpactToChronoBodyMesh(self): + pass + + def applyKickImpactToChronoBodyMesh(self): + pass + + def clearAllExternalForces(self): + self.chrono_body_mesh.RemoveAllForces() + pass + + def updateExternalForcesTimer(self): + pass + +@dataclass +class ImpactTimerParameters: + test_num: int = 0 # number of actual impact + test_bound: int = 0 # number of all impacts + clock: float = 0 # actual time + clock_bound: float = 0 # time to change of impact + step: float = 0 + +def updateImpact(chtestee: ChTesteeObject, + force: chrono.ChForce, + solver: ImpactTimerParameters, + impacts_point:list, + impacts_dir: list, + impacts_magnitude: list): + """The function of passing through all the impacts at regular intervals + + Args: + chtestee (ChTesteeObject): Chrono entity of Testee Object + force (ChForce): Constant force + solver (ImpactTimerParameters): timer entity for tracking + impacts_point (list): sets the application point, in rigid body coordinates + impacts_dir (list): gets the force direction, in rigid body coordinates + impacts_magnitude (list): force modulus + """ + if solver.clock > solver.clock_bound: + if solver.test_num < solver.test_bound: + chtestee.chrono_body_mesh.RemoveAllForces() + chtestee.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), chrono.ChQuaternionD(1,0,0,0))) + chtestee.chrono_body_mesh.SetNoSpeedNoAcceleration() + + chtestee.chrono_body_mesh.AddForce(force) + force.SetVrelpoint(impacts_point[solver.test_num]) + force.SetRelDir(impacts_dir[solver.test_num]) + force.SetMforce(impacts_magnitude[solver.test_num]) + solver.test_num += 1 + else: + solver.test_num = 0 + solver.clock = 0 + + else: + solver.clock += solver.step + +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/rostok/intexp/poses_generator.py b/rostok/intexp/poses_generator.py new file mode 100644 index 00000000..2abf63b9 --- /dev/null +++ b/rostok/intexp/poses_generator.py @@ -0,0 +1,99 @@ +from random import uniform +from math import pi, sin, cos +from scipy.spatial.transform import Rotation + +import xml.etree.ElementTree +# import open3d as o3d + +def genRandomPosesAroundLine(num_pose_on_layer: int, + min_dist: float, + max_dist: float, + step: float, + length: float) -> list[float]: + """Generates poses with random angular displacement and random distances on predetermined range. + Generation is performed along a straight line with a fixed step. + + Args: + num_pose_on_layer (int): number of poses per layer + min_dist (float): minimum distance from line + max_dist (float): maximum distance from line + step (float): step between layers + length (float): line length + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as positions and rotations + """ + poses = [] + gape = max_dist - min_dist + + for i in range(round(length/step)): + for j in range(num_pose_on_layer): + phi = uniform(0, 2*pi) + ro = gape * uniform(0, 1) + min_dist + + coord = [ro*sin(phi), ro*cos(phi), i*step] + rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) + orient = rot.as_quat() + + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + return poses + +def genCylindricalSurfaceFromPoses(num_pose_on_layer: int, + dist: float, + step: float, + length: float) -> list[float]: + """Generates poses with discrete angular displacement on predetermined distance. + Generation is performed along a straight line with a fixed step. + + Args: + num_pose_on_layer (int): number of poses per layer + dist (float): predetermined distance from line + step (float): step between layers + length (float): line length + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as positions and rotations + """ + poses = [] + dphi = 2*pi / num_pose_on_layer + + for i in range(round(length/step)): + for j in range(num_pose_on_layer): + phi = dphi*j + coord = [dist*sin(phi), dist*cos(phi), i*step] + rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + return poses + +# def genPosesFromConvexHull(mesh: o3d.geometry.TriangleMesh, offset: float, scale_factor: float = 3) -> list: +# poses = [] +# mesh_convex_hull, _ = mesh.compute_convex_hull() +# triangles_scaled = round(len(mesh_convex_hull.triangles)/scale_factor) +# mesh_convex_hull_simplify = mesh_convex_hull.simplify_quadric_decimation(triangles_scaled) +# return poses + +def rewritePosesOnXMLFile(poses: list, xml_file_name: str) -> None: + ''' + Deletes all poses in the file and overwrites the passed sheet + ''' + doc = xml.etree.ElementTree.parse(xml_file_name) + doc.find('grasping_poses').clear() + new_poses = [] + + index = 0 + for item in poses: + new_pos = xml.etree.ElementTree.SubElement(doc.find('grasping_poses'), + 'pose', + index = str(index), + coordinates = str(item[0]), + orientation = str(item[1])) + new_poses.append(new_pos) + index += 1 + + doc.write(xml_file_name, encoding="utf-8", xml_declaration=True) + +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/rostok/intexp/readme.md b/rostok/intexp/readme.md new file mode 100644 index 00000000..3ee62efe --- /dev/null +++ b/rostok/intexp/readme.md @@ -0,0 +1,30 @@ +# Interaction Explorer + +:no_entry: Works are frozen :no_entry: + +The package implements the search for a reliable interaction between the gripper and the object. The grip and object are user defined. The result of the package is a list of possible poses from which the grip can take and hold a rigid body, as well as quantitative estimates based on analytical analysis. + +## Task List +- [X] First step: + - [X] Description of the testee object + - [X] Loading a volume mesh from an .obj file + - [X] Loading object parameters from .xml file + - [X] Loading user poses from .xml file + - [ ] Easy generation of grasping poses: + - [X] Random + - [ ] From point cloud + - [ ] From convex hull +- [ ] Determination of the topology of the body: + - [ ] Reeb graph + - [ ] Body skeletonization +- [ ] Gripper structure integration: + - [ ] Gripper mechanism integration in graph form + - [ ] Extraction of geometric features from gripper + - [ ] Determining the quality of the gripper position + +## Used extensions + +* [PyChrono](https://projectchrono.org/pychrono/) +* [Open3D](http://www.open3d.org/) +* [lxml](https://lxml.de/) +* [SciPy](https://scipy.org/) diff --git a/rostok/intexp/testee.py b/rostok/intexp/testee.py new file mode 100644 index 00000000..a354de47 --- /dev/null +++ b/rostok/intexp/testee.py @@ -0,0 +1,370 @@ +from lxml import etree +import open3d as o3d + +from enum import Enum +from dataclasses import dataclass + +@dataclass +class PhysicsParameters: + mass: float = None + density: float = None + mu_friction: float = None + +class ErrorReport(Enum): + NONE = 0 + WRONG_FILE_NAME = 1 + WRONG_LINKED_FILE_DESCRIPTION = 2 + WRONG_WEIGHT_PARAMETER_DESCRIPTION = 3 + WRONG_FRICTION_DESCRIPTION = 4 + GRASPING_POSES_MISSING = 5 + WRONG_XML_STRUCTURE = 6 + WRONG_WEIGHT_VALUE = 7 + WRONG_FRICTION_VALUE = 8 + WRONG_GRASPING_POSES_DESCRIPTION = 9 + WRONG_POSE_COORDINATE_DESCRIPTION = 10 + WRONG_POSE_ORIENTATION_DESCRIPTION = 11 + REQUIRED_OBJ_MESH_FORMAT = 12 + BROKEN_3D_MESH = 13 + MESH_NOT_FOUND = 14 + WEIGHT_NOT_DEFINED = 15 + +class TesteeObject(object): + """Geometry and physical parameters of the testee object + + Init State: + - empty mesh + - empty physical parameters + - empty poses + + TODO: Added @property and @setter + """ + + def __init__(self) -> None: + self.__parameters = PhysicsParameters() + self.__grasping_poses_list = [] + self.__linked_obj_file = None + + def loadObject3DMesh(self, file_mesh: str) -> ErrorReport: + """Loading a volumetric mesh from a Wavefront OBJ file + + Args: + file_mesh (str): mesh file like name 'body1.obj' or path '../folder1/body1.obj' + + Returns: + error (ErrorReport): zero or error code + + TODO: Checking the integrity of the grid and the possibility of its use + """ + + mesh = o3d.io.read_triangle_mesh(filename=file_mesh, + enable_post_processing=False, + print_progress=True) + + self._mesh = mesh + return ErrorReport.NONE + + def getMeshVertices(self): + """Vertex coordinates + + Returns: + float64 array of shape (num_vertices, 3), use numpy.asarray() to access data + """ + return self._mesh.vertices + + def showObject3DModel(self): + """Visualization of the object model through OpenGL + """ + mesh_list = [self._mesh] + o3d.visualization.draw_geometries(geometry_list = mesh_list, + window_name = 'Testee Object', + width = 1280, + height = 720, + mesh_show_wireframe = True) + + def showObjectWithGraspingPoses(self): + """Visualization of the object shape and gripping poses through OpenGL. + The location and direction of capture is indicated by an arrow. + + TODO: Change arrow on frame + """ + demo_list = [] + demo_list.append(self._mesh) + + for pose in self.__grasping_poses_list: + demo_list.append(self.__createArrowSignGraspingPose(pose)) + + demo_list.append(o3d.geometry.TriangleMesh.create_coordinate_frame(size = 0.1)) + o3d.visualization.draw_geometries(geometry_list = demo_list, + window_name = 'Testee Object and Poses', + width = 1280, + height = 720, + mesh_show_wireframe = True) + + demo_list.clear() + + def __createArrowSignGraspingPose(self, pose) -> o3d.geometry.TriangleMesh: + """Creates an o3d.geometry.TriangleMesh() object in the form of an arrow at the grip position. + Arrow aligned with grip direction. + + Args: + pose (list[(x,y,z), (x, y, z, w)]): position and orientation + + Returns: + o3d.geometry.TriangleMesh: Arrow icon + """ + coordinate, orientation = pose + arrow_size = (self._mesh.get_max_bound()[2] - self._mesh.get_min_bound()[2]) / 10 + arrow_mesh = o3d.geometry.TriangleMesh().create_arrow(cylinder_radius=arrow_size*0.1, + cone_radius=arrow_size*0.15, + cylinder_height=arrow_size * 2/3, + cone_height=arrow_size * 1/3, + resolution=20, + cylinder_split=4, + cone_split=1) + arrow_mesh.paint_uniform_color([220/255, 20/255, 60/255]) + arrow_mesh.translate(coordinate) + arrow_mesh.rotate(o3d.geometry.get_rotation_matrix_from_quaternion(orientation)) + return arrow_mesh + + def loadObjectDescription(self, fname: str) -> ErrorReport: + """Loading physical parameters and required grip positions in xml format + + Args: + fname (str): physical parameters like name 'body1_param.xml' or path '../folder1/body1_param.xml' + + Returns: + error (ErrorReport): zero or error code + """ + try: + description_file = etree.parse(fname) + + xmlread_error = self.__checkXMLLinkedObjFile( description_file.find('obj_file') ) + if xmlread_error.value: return xmlread_error + + xmlread_error = self.__checkXMLWeightParam( description_file.find('weight') ) + if xmlread_error.value: return xmlread_error + + xmlread_error = self.__checkXMLMuFriction( description_file.find('mu_friction') ) + if xmlread_error.value: return xmlread_error + + xmlread_error = self.__checkXMLGraspingPoses( description_file.find('grasping_poses') ) + if xmlread_error.value: return xmlread_error + + return ErrorReport.NONE + + except OSError: + return ErrorReport.WRONG_FILE_NAME + except: + return ErrorReport.WRONG_XML_STRUCTURE + + def __checkXMLLinkedObjFile(self, xmlsubelem_linked_obj_file) -> ErrorReport: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_linked_obj_file (lxmlsubelem): linked .obj file name + + Returns: + error (ErrorReport): zero or error code + """ + if (xmlsubelem_linked_obj_file is not None) and (xmlsubelem_linked_obj_file.text.endswith('.obj')): + self.__linked_obj_file = xmlsubelem_linked_obj_file.text + return ErrorReport.NONE + else: + return ErrorReport.WRONG_LINKED_FILE_DESCRIPTION + + def __checkXMLWeightParam(self, xmlsubelem_weight) -> ErrorReport: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_weight (lxmlsubelem): weight paramaeter (density or mass) + + Returns: + error (ErrorReport): zero or error code + """ + if(xmlsubelem_weight.get('parameter') == 'mass'): + weight_value = float(xmlsubelem_weight.text) + if weight_value > 0: + self.__parameters.mass = weight_value + return ErrorReport.NONE + else: + return ErrorReport.WRONG_WEIGHT_VALUE + + elif(xmlsubelem_weight.get('parameter') == 'density'): + weight_value = float(xmlsubelem_weight.text) + if weight_value > 0: + self.__parameters.density = weight_value + return ErrorReport.NONE + else: + return ErrorReport.WRONG_WEIGHT_VALUE + + else: + return ErrorReport.WRONG_WEIGHT_PARAMETER_DESCRIPTION + + def __checkXMLMuFriction(self, xmlsubelem_friction) -> ErrorReport: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_friction (lxmlsubelem): friction coefficient for surface + + Returns: + error (ErrorReport): zero or error code + """ + if (xmlsubelem_friction is not None): + value = float(xmlsubelem_friction.text) + if (value > 0) or (value <= 1): + self.__parameters.mu_friction = value + return ErrorReport.NONE + else: + return ErrorReport.WRONG_FRICTION_VALUE + else: + return ErrorReport.WRONG_FRICTION_DESCRIPTION + + def __checkXMLGraspingPoses(self, xmlsubelem_grasping_poses) -> ErrorReport: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_grasping_poses (lxmlsubelem): list of user's grasping poses (desired) + + Returns: + error (ErrorReport): zero or error code + """ + if xmlsubelem_grasping_poses is not None: + for xmlsubelem_pose in xmlsubelem_grasping_poses.getchildren(): + coordinates = xmlsubelem_pose.get('coordinates') + coordinates = coordinates.replace('[', '') + coordinates = coordinates.replace(']', '') + coordinates = [float(x) for x in coordinates.split(',')] + if len(coordinates) != 3: return ErrorReport.WRONG_POSE_COORDINATE_DESCRIPTION + + orientation = xmlsubelem_pose.get('orientation') + orientation = orientation.replace('[', '') + orientation = orientation.replace(']', '') + orientation = [float(x) for x in orientation.split(',')] + if len(orientation) != 4: return ErrorReport.WRONG_POSE_ORIENTATION_DESCRIPTION + + self.__grasping_poses_list.append([coordinates, orientation]) + else: + return ErrorReport.GRASPING_POSES_MISSING + + return ErrorReport.NONE + + def __checkIntersectionGraspingPosesAndMesh(self) -> ErrorReport: + """ !!!_summary_!!! + + Returns: + error (ErrorReport): zero or error code + + TODO: Create check method for detection instersection btw grasping points + adnd mesh. If grasping pose was inside -> remove pos from poses_list + """ + + pass + + def getGraspingPosesNumber(self) -> int: + """Property for getting number of poses + + Returns: + int: number of poses + """ + return len(self.__grasping_poses_list) + + def getGraspingPose(self, index: int) -> list: + """Property for getting pose with index + + Args: + index (int): pose with index + + Returns: + [(x,y,z), (x, y, z, w)]: pose as position and rotation + """ + return self.__grasping_poses_list[index] + + def getGraspingPosesList(self) -> list: + """Property for getting all poses + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as position and rotation + """ + return self.__grasping_poses_list + + def addGraspingPose(self, coord, orient): + """Add new pose in poses list + + Args: + coord (list[(x,y,z)]): place of pose in local coordinate system + orient (list[(x,y,z,w)]): orientation of gripper in local coordinate system + """ + self.__grasping_poses_list.append([coord, orient]) + + def addGraspingPosesList(self, new_poses): + """Add new poses in poses list + + Args: + new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper + """ + self.__grasping_poses_list.extend(new_poses) + + def clearGraspingPosesList(self): + """Clear list of poses. + """ + self.__grasping_poses_list.clear() + + def rewriteGraspingPosesList(self, new_poses): + """Replaces an existing list of capture poses with a new one. + + Args: + new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper + """ + self.__grasping_poses_list.clear() + self.__grasping_poses_list.extend(new_poses) + + def setDensity(self, density_val: float): + """Setter of density + + Args: + density_val (float): new value in kg/m3 + """ + self.__parameters.density = density_val + + def getDensity(self) -> float: + """Property of density + + Returns: + float: kg/m3 + """ + if self.__parameters.density: + return self.__parameters.density + elif self.__parameters.mass and self._mesh: + return self.__parameters.mass / self._mesh.get_volume() + else: + return None + + def setMuFriction(self, mu_val: float): + """Setter of surface friction coefficient + + Args: + mu_val (float): from 0 to 1 + """ + if mu_val > 0 and mu_val <= 1: self.__parameters.mu_friction = mu_val + + def getMuFriction(self) -> float: + """Property of surface friction coefficient + + Returns: + float: from 0 to 1 or None if not defined + """ + if self.__parameters.mu_friction: + return self.__parameters.mu_friction + else: + return None + + def getName(self): + """Property of linked file name + + Returns: + str: name of testee object + """ + return self.__linked_obj_file + +if __name__ == '__main__': + pass \ No newline at end of file From 25923b79f17168b55ae9883e162bdc7e60eca769 Mon Sep 17 00:00:00 2001 From: Ivolga Dmitriy Date: Wed, 7 Dec 2022 16:05:54 +0300 Subject: [PATCH 2/6] =?UTF-8?q?Comments=20are=20taken=20and=20=D0=BF=D1=83?= =?UTF-8?q?=D1=82=20from=20the=20body=20is=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- README.md | 2 +- ...intexp_external_forces_on_testee_object.py | 18 ++- .../intexp_load_and_show_testee_object.py | 15 +- rostok/intexp/poses_generator.py | 150 ++++++++++++++++-- rostok/intexp/testee.py | 3 +- 6 files changed, 165 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index bde2c4b3..44b30c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /__pycache__ /examples/__pycache__ *.pyc - +/rostok.egg-info diff --git a/README.md b/README.md index 33ec2b77..e2d21b83 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ * Установить Pychono из архива `conda install pychrono-7.0.0-py39_2112.tar.bz2` * Установка движка способ 2 * `conda install -c projectchrono pychrono` -* Установить пакет в режими разработки `pip3 install -e .` +* Установить пакет `rostok` и остальные локальные пакеты в режими разработки `pip3 install -e .` # Прокидываем дисплей через Docker * Установить Х сервер для Windows https://sourceforge.net/projects/vcxsrv/ diff --git a/examples/intexp_external_forces_on_testee_object.py b/examples/intexp_external_forces_on_testee_object.py index 03e6508e..a400c2e7 100644 --- a/examples/intexp_external_forces_on_testee_object.py +++ b/examples/intexp_external_forces_on_testee_object.py @@ -4,11 +4,13 @@ """ Testee object is dumbbell """ obj_db = intexp.chrono_api.ChTesteeObject() # Create Chrono Testee Object +# create 3D mesh and parameters (grasping poses read from file) obj_db.createChronoBodyMeshFromFile('./examples/models/custom/body1.obj', - './examples/models/custom/body1.xml') # create 3D mesh and parameters (grasping poses read from file) -obj_db.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), chrono.ChQuaternionD(1,0,0,0))) + './examples/models/custom/body1.xml') +obj_db.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), + chrono.ChQuaternionD(1,0,0,0))) -''' Impact on Testee Object''' +""" Impact on Testee Object """ impact = chrono.ChForce() # Create Impact obj_db.chrono_body_mesh.AddForce(impact) # Attach to ChBody @@ -26,8 +28,9 @@ chrono.ChVectorD(0, 0, 0), chrono.ChVectorD(0, 0, 0)] +# Switch timer of impact every 2 sec tracking_timer = intexp.chrono_api.ImpactTimerParameters(test_bound=len(IMPACTS_DIR), - clock_bound=2.0, step = 1e-3) # Switch timer of impact every 2 sec + clock_bound=2.0, step = 1e-3) ''' Floor added for clarity ''' @@ -58,10 +61,11 @@ while vis.Run(): system.Update() + system.DoStepDynamics(1e-3) vis.BeginScene() vis.Render() vis.EndScene() - system.DoStepDynamics(1e-3) - + # Update timer and go through the list of impacts intexp.chrono_api.updateImpact(obj_db, impact, tracking_timer, - IMPACTS_APPLICATION_POINT, IMPACTS_DIR, IMPACTS_M) # Update timer and go through the list of impacts \ No newline at end of file + IMPACTS_APPLICATION_POINT, IMPACTS_DIR, IMPACTS_M) + \ No newline at end of file diff --git a/examples/intexp_load_and_show_testee_object.py b/examples/intexp_load_and_show_testee_object.py index 3111fb8a..c3a2ac58 100644 --- a/examples/intexp_load_and_show_testee_object.py +++ b/examples/intexp_load_and_show_testee_object.py @@ -9,8 +9,15 @@ './examples/models/custom/body1.xml') # create 3D mesh and parameters (grasping poses read from file) print('Uploded mesh with code error:', error) # Uncomment the line below for generate new poses -# obj_db.rewriteGraspingPosesList(intexp.poses_generator.genRandomPosesAroundLine(20, 0.04, 0.06, 0.015, 0.18)) -obj_db.rewriteGraspingPosesList(intexp.poses_generator.genCylindricalSurfaceFromPoses(20, 0.07, 0.015, 0.18)) +# new_gen_poses = intexp.poses_generator.genRandomPosesAroundLine(20, 0.04, 0.06, 0.015, 0.18) +# new_gen_poses = intexp.poses_generator.genCylindricalSurfaceFromPoses(20, 0.07, 0.015, 0.18) +# new_gen_poses = intexp.poses_generator.genRandomPosesAroundTesteeObjectAxis(obj_db, 20, 0.07, 0.02, 'y') +new_gen_poses = intexp.poses_generator.genCylindricalSurfaceAroundTesteeObjectAxis(obj_db, 20, 0.07, 0.02, 'y') +if new_gen_poses is not None: + obj_db.setGraspingPosesList(new_gen_poses) +else: + print('ERROR') + obj_db.showObjectWithGraspingPoses() desired_poses = obj_db.getChronoGraspingPosesList() # List of all grasping poses hand_power_grasp_frame = chrono.ChFrameD(chrono.ChVectorD(0, 0, 0), chrono.Q_ROTATE_Z_TO_Y) # Power grasp point in the ABS @@ -46,12 +53,12 @@ while vis.Run(): system.Update() + system.DoStepDynamics(1e-3) vis.BeginScene() vis.Render() vis.EndScene() - system.DoStepDynamics(1e-3) - if counter > 0.5: #Demonstration all poses every 0.5 seconds + if counter > 0.1: #Demonstration all poses every 0.5 seconds obj_db.setChronoBodyMeshOnPose(desired_poses[i], hand_power_grasp_frame) counter = 0 i += 1 diff --git a/rostok/intexp/poses_generator.py b/rostok/intexp/poses_generator.py index 2abf63b9..33f7a534 100644 --- a/rostok/intexp/poses_generator.py +++ b/rostok/intexp/poses_generator.py @@ -1,15 +1,15 @@ from random import uniform from math import pi, sin, cos from scipy.spatial.transform import Rotation - +import numpy as np import xml.etree.ElementTree -# import open3d as o3d +from .testee import TesteeObject def genRandomPosesAroundLine(num_pose_on_layer: int, min_dist: float, max_dist: float, step: float, - length: float) -> list[float]: + length: float) -> list[list[float]]: """Generates poses with random angular displacement and random distances on predetermined range. Generation is performed along a straight line with a fixed step. @@ -39,10 +39,77 @@ def genRandomPosesAroundLine(num_pose_on_layer: int, return poses -def genCylindricalSurfaceFromPoses(num_pose_on_layer: int, +def genRandomPosesAroundTesteeObjectAxis(testee_obj: TesteeObject, + num_pose_on_layer: int, + max_dist: float, + step: float, + axis = 'z'): + """Generates poses with random angular displacement and random distances on predetermined range. + Generation occurs around the selected axis of the object, taking into account its geometric dimensions. + + Args: + testee_obj (TesteeObject): _description_ + num_pose_on_layer (int): number of poses per layer + max_dist (float): maximum distance from object + step (float): step between layers + axis (str, optional): orientation around one of the axes 'x','y','z'. Defaults to 'z'. + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as positions and rotations + """ + + poses = [] + size = {'x': testee_obj._mesh.get_max_bound()[0] - testee_obj._mesh.get_min_bound()[0], + 'y': testee_obj._mesh.get_max_bound()[1] - testee_obj._mesh.get_min_bound()[1], + 'z': testee_obj._mesh.get_max_bound()[2] - testee_obj._mesh.get_min_bound()[2]} + + if axis == 'x': + min_dist = (size['y']+size['z'])/4 * 1.2 + for item in np.arange(testee_obj._mesh.get_min_bound()[0], testee_obj._mesh.get_max_bound()[0], step): + for j in range(num_pose_on_layer): + phi = uniform(0, 2*pi) + ro = max_dist * uniform(0, 1) + min_dist + coord = [item, + ro*sin(phi) + testee_obj._mesh.get_center()[1], + ro*cos(phi) + testee_obj._mesh.get_center()[2]] + rot = Rotation.from_euler('xyz', [pi/2, -phi, pi]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + elif axis == 'y': + min_dist = (size['x']+size['z'])/4 * 1.2 + for item in np.arange(testee_obj._mesh.get_min_bound()[1], testee_obj._mesh.get_max_bound()[1], step): + for j in range(num_pose_on_layer): + phi = uniform(0, 2*pi) + ro = max_dist * uniform(0, 1) + min_dist + coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + item, + ro*cos(phi) + testee_obj._mesh.get_center()[2]] + rot = Rotation.from_euler('xyz', [0, phi, pi]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + elif axis == 'z': + min_dist = (size['x']+size['y'])/4 * 1.2 + for item in np.arange(testee_obj._mesh.get_min_bound()[2], testee_obj._mesh.get_max_bound()[2], step): + for j in range(num_pose_on_layer): + phi = uniform(0, 2*pi) + ro = max_dist * uniform(0, 1) + min_dist + coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + ro*cos(phi) + testee_obj._mesh.get_center()[1], + item] + rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + else: + return None + + return poses + +def genCylindricalSurfacePoses(num_pose_on_layer: int, dist: float, step: float, - length: float) -> list[float]: + length: float): """Generates poses with discrete angular displacement on predetermined distance. Generation is performed along a straight line with a fixed step. @@ -68,12 +135,73 @@ def genCylindricalSurfaceFromPoses(num_pose_on_layer: int, return poses -# def genPosesFromConvexHull(mesh: o3d.geometry.TriangleMesh, offset: float, scale_factor: float = 3) -> list: -# poses = [] -# mesh_convex_hull, _ = mesh.compute_convex_hull() -# triangles_scaled = round(len(mesh_convex_hull.triangles)/scale_factor) -# mesh_convex_hull_simplify = mesh_convex_hull.simplify_quadric_decimation(triangles_scaled) -# return poses +def genCylindricalSurfaceAroundTesteeObjectAxis(testee_obj: TesteeObject, + num_pose_on_layer: int, + dist: float, + step: float, + axis = 'z'): + """Generates poses with discrete angular displacement on predetermined distance. + Generation occurs around the selected axis of the object, taking into account its geometric dimensions. + + Args: + testee_obj (TesteeObject): _description_ + num_pose_on_layer (int): number of poses per layer + dist (float): distance from object + step (float): step between layers + axis (str, optional): orientation around one of the axes 'x','y','z'. Defaults to 'z'. + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as positions and rotations + """ + + poses = [] + dphi = 2*pi / num_pose_on_layer + size = {'x': testee_obj._mesh.get_max_bound()[0] - testee_obj._mesh.get_min_bound()[0], + 'y': testee_obj._mesh.get_max_bound()[1] - testee_obj._mesh.get_min_bound()[1], + 'z': testee_obj._mesh.get_max_bound()[2] - testee_obj._mesh.get_min_bound()[2]} + + if axis == 'x': + min_dist = (size['y']+size['z'])/4 * 1.1 + for item in np.arange(testee_obj._mesh.get_min_bound()[0], testee_obj._mesh.get_max_bound()[0], step): + for j in range(num_pose_on_layer): + phi = dphi*j + ro = dist + min_dist + coord = [item, + ro*sin(phi) + testee_obj._mesh.get_center()[1], + ro*cos(phi) + testee_obj._mesh.get_center()[2]] + rot = Rotation.from_euler('xyz', [pi/2, -phi, pi]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + elif axis == 'y': + min_dist = (size['x']+size['z'])/4 * 1.1 + for item in np.arange(testee_obj._mesh.get_min_bound()[1], testee_obj._mesh.get_max_bound()[1], step): + for j in range(num_pose_on_layer): + phi = dphi*j + ro = dist + min_dist + coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + item, + ro*cos(phi) + testee_obj._mesh.get_center()[2]] + rot = Rotation.from_euler('xyz', [0, phi, pi]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + + elif axis == 'z': + min_dist = (size['x']+size['y'])/4 * 1.1 + for item in np.arange(testee_obj._mesh.get_min_bound()[2], testee_obj._mesh.get_max_bound()[2], step): + for j in range(num_pose_on_layer): + phi = dphi*j + ro = dist + coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + ro*cos(phi) + testee_obj._mesh.get_center()[1], + item] + rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) + orient = rot.as_quat() + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + else: + return None + + return poses def rewritePosesOnXMLFile(poses: list, xml_file_name: str) -> None: ''' diff --git a/rostok/intexp/testee.py b/rostok/intexp/testee.py index a354de47..f7ec66e9 100644 --- a/rostok/intexp/testee.py +++ b/rostok/intexp/testee.py @@ -43,6 +43,7 @@ def __init__(self) -> None: self.__parameters = PhysicsParameters() self.__grasping_poses_list = [] self.__linked_obj_file = None + self._mesh = o3d.geometry.TriangleMesh() def loadObject3DMesh(self, file_mesh: str) -> ErrorReport: """Loading a volumetric mesh from a Wavefront OBJ file @@ -309,7 +310,7 @@ def clearGraspingPosesList(self): """ self.__grasping_poses_list.clear() - def rewriteGraspingPosesList(self, new_poses): + def setGraspingPosesList(self, new_poses): """Replaces an existing list of capture poses with a new one. Args: From e33b4a49cc1e54129b604f393300930765727a80 Mon Sep 17 00:00:00 2001 From: Ivolga Dmitriy Date: Wed, 7 Dec 2022 16:07:55 +0300 Subject: [PATCH 3/6] Fixed and added generation from the body --- rostok/intexp/poses_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rostok/intexp/poses_generator.py b/rostok/intexp/poses_generator.py index 33f7a534..1fadf135 100644 --- a/rostok/intexp/poses_generator.py +++ b/rostok/intexp/poses_generator.py @@ -9,7 +9,7 @@ def genRandomPosesAroundLine(num_pose_on_layer: int, min_dist: float, max_dist: float, step: float, - length: float) -> list[list[float]]: + length: float): """Generates poses with random angular displacement and random distances on predetermined range. Generation is performed along a straight line with a fixed step. From 19fe331899fa4a2151040315fccecec932ed3531 Mon Sep 17 00:00:00 2001 From: Ivolga Dmitriy Date: Wed, 28 Dec 2022 18:55:20 +0300 Subject: [PATCH 4/6] Rewrite under PEP and added Crutch --- examples/intexp_crutch_render.py | 50 + ...intexp_external_forces_on_testee_object.py | 14 +- .../intexp_load_and_show_testee_object.py | 50 +- examples/models/custom/body1.xml | 3 +- examples/models/custom/pipe.mtl | 5 + examples/models/custom/pipe.obj | 1327 +++++++++++++++++ examples/models/custom/pipe.xml | 9 + rostok/intexp/__init__.py | 3 +- rostok/intexp/chrono_api.py | 224 +-- rostok/intexp/entity.py | 443 ++++++ rostok/intexp/poses_generator.py | 228 +-- rostok/intexp/testee.py | 371 ----- rostok/intexp/utils.py | 27 + 13 files changed, 2140 insertions(+), 614 deletions(-) create mode 100644 examples/intexp_crutch_render.py create mode 100644 examples/models/custom/pipe.mtl create mode 100644 examples/models/custom/pipe.obj create mode 100644 examples/models/custom/pipe.xml create mode 100644 rostok/intexp/entity.py delete mode 100644 rostok/intexp/testee.py create mode 100644 rostok/intexp/utils.py diff --git a/examples/intexp_crutch_render.py b/examples/intexp_crutch_render.py new file mode 100644 index 00000000..850ffcaf --- /dev/null +++ b/examples/intexp_crutch_render.py @@ -0,0 +1,50 @@ +import open3d as o3d +import trimesh as tm +import pychrono as chrono +import pychrono.irrlicht as irr +from numpy import asarray +from rostok.intexp.chrono_api import ChTesteeObject, ChCrutch + +''' Floor added for clarity ''' +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceNSC()) +floor.SetPos(chrono.ChVectorD(0,-0.1,0)) +floor.SetBodyFixed(True) +floor.SetName('Floor') +floor.GetVisualShape(0).SetColor(chrono.ChColor(80/255, 80/255, 80/255)) + +''' Testee object placed in Crutch ''' +obj_db = ChTesteeObject() # Create Chrono Testee Object +# Create 3D mesh and setup parameters from files +obj_db.create_chrono_body_from_file('./examples/models/custom/pipe.obj', + './examples/models/custom/pipe.xml') +obj_db.set_chrono_body_ref_frame_in_point(chrono.ChFrameD(chrono.ChVectorD(0, 0.2, 0), + chrono.ChQuaternionD(1, 0, 0, 0))) + +holder = ChCrutch(depth_k = 0.05) +holder.build_chrono_body(obj_db) + +obj_db.set_chrono_body_ref_frame_in_point(holder.place_for_object) + +system = chrono.ChSystemNSC() +system.Set_G_acc(chrono.ChVectorD(0,-9.8,0)) +system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation +system.Add(floor) +system.Add(holder.chrono_body) +vis = irr.ChVisualSystemIrrlicht() +vis.AttachSystem(system) +vis.SetWindowSize(1028,768) +vis.SetWindowTitle('ZXC') +vis.SetSymbolScale(3) +vis.Initialize() +vis.AddLight(chrono.ChVectorD(0,10,0), 15) +vis.AddSkyBox() +vis.AddCamera(chrono.ChVectorD(1,0.5,1)) +vis.EnableCollisionShapeDrawing(True) +vis.EnableBodyFrameDrawing(True) + +while vis.Run(): + system.Update() + system.DoStepDynamics(1e-3) + vis.BeginScene() + vis.Render() + vis.EndScene() diff --git a/examples/intexp_external_forces_on_testee_object.py b/examples/intexp_external_forces_on_testee_object.py index a400c2e7..68345eb2 100644 --- a/examples/intexp_external_forces_on_testee_object.py +++ b/examples/intexp_external_forces_on_testee_object.py @@ -5,14 +5,14 @@ """ Testee object is dumbbell """ obj_db = intexp.chrono_api.ChTesteeObject() # Create Chrono Testee Object # create 3D mesh and parameters (grasping poses read from file) -obj_db.createChronoBodyMeshFromFile('./examples/models/custom/body1.obj', +obj_db.create_chrono_body_from_file('./examples/models/custom/body1.obj', './examples/models/custom/body1.xml') -obj_db.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), - chrono.ChQuaternionD(1,0,0,0))) +obj_db.set_chrono_body_ref_frame_in_point(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), + chrono.ChQuaternionD(1,0,0,0))) """ Impact on Testee Object """ impact = chrono.ChForce() # Create Impact -obj_db.chrono_body_mesh.AddForce(impact) # Attach to ChBody +obj_db.chrono_body.AddForce(impact) # Attach to ChBody IMPACTS_M = [1, 1, 1, 2, 2, 2] # Exam with 6 constant forces and different directions IMPACTS_DIR = [chrono.ChVectorD(1,0,0), @@ -43,7 +43,7 @@ ''' Simulation Solver ''' system = chrono.ChSystemNSC() system.Set_G_acc(chrono.ChVectorD(0,0,0)) -system.Add(obj_db.chrono_body_mesh) # Chrono Testee Object added to simulation +system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation system.Add(floor) ''' PyChrono Visualisation ''' @@ -66,6 +66,6 @@ vis.Render() vis.EndScene() # Update timer and go through the list of impacts - intexp.chrono_api.updateImpact(obj_db, impact, tracking_timer, - IMPACTS_APPLICATION_POINT, IMPACTS_DIR, IMPACTS_M) + intexp.chrono_api.update_impact(obj_db, impact, tracking_timer, + IMPACTS_APPLICATION_POINT, IMPACTS_DIR, IMPACTS_M) \ No newline at end of file diff --git a/examples/intexp_load_and_show_testee_object.py b/examples/intexp_load_and_show_testee_object.py index c3a2ac58..f86557f1 100644 --- a/examples/intexp_load_and_show_testee_object.py +++ b/examples/intexp_load_and_show_testee_object.py @@ -3,25 +3,28 @@ from rostok import intexp +GENERATE_NEW_POSES = True + ''' Testee object is dumbbell ''' obj_db = intexp.chrono_api.ChTesteeObject() # Create Chrono Testee Object -error = obj_db.createChronoBodyMeshFromFile('./examples/models/custom/body1.obj', - './examples/models/custom/body1.xml') # create 3D mesh and parameters (grasping poses read from file) -print('Uploded mesh with code error:', error) + +# Create 3D mesh and setup parameters from files +obj_db.create_chrono_body_from_file('./examples/models/custom/pipe.obj', + './examples/models/custom/pipe.xml') + # Uncomment the line below for generate new poses -# new_gen_poses = intexp.poses_generator.genRandomPosesAroundLine(20, 0.04, 0.06, 0.015, 0.18) -# new_gen_poses = intexp.poses_generator.genCylindricalSurfaceFromPoses(20, 0.07, 0.015, 0.18) -# new_gen_poses = intexp.poses_generator.genRandomPosesAroundTesteeObjectAxis(obj_db, 20, 0.07, 0.02, 'y') -new_gen_poses = intexp.poses_generator.genCylindricalSurfaceAroundTesteeObjectAxis(obj_db, 20, 0.07, 0.02, 'y') -if new_gen_poses is not None: - obj_db.setGraspingPosesList(new_gen_poses) -else: - print('ERROR') - -obj_db.showObjectWithGraspingPoses() -desired_poses = obj_db.getChronoGraspingPosesList() # List of all grasping poses +if GENERATE_NEW_POSES: + # new_gen_poses = intexp.poses_generator.gen_random_poses_around_line(20, 0.04, 0.06, 0.015, 0.18) + # new_gen_poses = intexp.poses_generator.gen_cylindrical_surface_from_poses(20, 0.07, 0.015, 0.18) + new_gen_poses = intexp.poses_generator.gen_cylindrical_surface_around_object_axis(obj_db, 20, 0.07, 0.02, 'z') + # new_gen_poses = intexp.poses_generator.gen_random_poses_around_object_axis(obj_db, 20, 0.07, 0.02, 'z') + obj_db.rewrite_grasping_poses_list(new_gen_poses) + +obj_db.demonstrate_object_and_grasping_poses() + +desired_poses = obj_db.get_chrono_grasping_poses_list() # List of all grasping poses hand_power_grasp_frame = chrono.ChFrameD(chrono.ChVectorD(0, 0, 0), chrono.Q_ROTATE_Z_TO_Y) # Power grasp point in the ABS -obj_db.setChronoBodyMeshOnPose(desired_poses[0], hand_power_grasp_frame) # Position object for grasp pose into point +obj_db.set_chrono_body_on_pose(desired_poses[0], hand_power_grasp_frame) # Position object for grasp pose into point ''' Floor added for clarity ''' floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, False, chrono.ChMaterialSurfaceNSC()) @@ -33,8 +36,9 @@ ''' Simulation Solver ''' system = chrono.ChSystemNSC() system.Set_G_acc(chrono.ChVectorD(0,0,0)) -system.Add(obj_db.chrono_body_mesh) # Chrono Testee Object added to simulation +system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation system.Add(floor) + ''' PyChrono Visualisation ''' vis = irr.ChVisualSystemIrrlicht() vis.AttachSystem(system) @@ -48,7 +52,7 @@ vis.EnableCollisionShapeDrawing(True) vis.EnableBodyFrameDrawing(True) -counter = 0 +counter = 0.0 i = 1 while vis.Run(): @@ -57,12 +61,12 @@ vis.BeginScene() vis.Render() vis.EndScene() - - if counter > 0.1: #Demonstration all poses every 0.5 seconds - obj_db.setChronoBodyMeshOnPose(desired_poses[i], hand_power_grasp_frame) - counter = 0 + + if counter > 0.1: #Demonstration all poses every 0.1 seconds + obj_db.set_chrono_body_on_pose(desired_poses[i], hand_power_grasp_frame) + counter = 0.0 i += 1 if i >= len(desired_poses): i = 0 - else: - counter += 1e-3 \ No newline at end of file + else: + counter += 1e-3 diff --git a/examples/models/custom/body1.xml b/examples/models/custom/body1.xml index 3ed8f7d2..aee5c77e 100644 --- a/examples/models/custom/body1.xml +++ b/examples/models/custom/body1.xml @@ -2,6 +2,7 @@ body1.obj 1000 - 0.25 + 0.25 + 0.001 \ No newline at end of file diff --git a/examples/models/custom/pipe.mtl b/examples/models/custom/pipe.mtl new file mode 100644 index 00000000..68afc6fe --- /dev/null +++ b/examples/models/custom/pipe.mtl @@ -0,0 +1,5 @@ +# WaveFront *.mtl file (generated by Autodesk ATF) + +newmtl Steel_-_Satin +Kd 0.627451 0.627451 0.627451 + diff --git a/examples/models/custom/pipe.obj b/examples/models/custom/pipe.obj new file mode 100644 index 00000000..ee9e97a5 --- /dev/null +++ b/examples/models/custom/pipe.obj @@ -0,0 +1,1327 @@ +# WaveFront *.obj file (generated by Autodesk ATF) + +mtllib tube.mtl + +g Body1 + +v -0.012500 -0.000000 1.000000 +v -0.011068 -0.005809 1.000000 +v -0.007101 -0.010287 1.000000 +v -0.001507 -0.012409 1.000000 +v 0.004433 -0.011688 1.000000 +v 0.009356 -0.008289 1.000000 +v 0.012137 -0.002991 1.000000 +v 0.012137 0.002991 1.000000 +v 0.009356 0.008289 1.000000 +v 0.004433 0.011688 1.000000 +v -0.001507 0.012409 1.000000 +v -0.007101 0.010287 1.000000 +v -0.011068 0.005809 1.000000 +v -0.012500 -0.000000 0.000000 +v -0.011068 0.005809 0.000000 +v -0.007101 0.010287 0.000000 +v -0.001507 0.012409 0.000000 +v 0.004433 0.011688 0.000000 +v 0.009356 0.008289 0.000000 +v 0.012137 0.002991 0.000000 +v 0.012137 -0.002991 0.000000 +v 0.009356 -0.008289 0.000000 +v 0.004433 -0.011688 0.000000 +v -0.001507 -0.012409 0.000000 +v -0.007101 -0.010287 0.000000 +v -0.011068 -0.005809 0.000000 +v -0.012500 0.000000 0.875000 +v -0.012500 0.000000 0.750000 +v -0.012500 0.000000 0.625000 +v -0.012500 0.000000 0.500000 +v -0.012500 0.000000 0.375000 +v -0.012500 0.000000 0.250000 +v -0.012500 0.000000 0.125000 +v -0.011068 -0.005809 0.875000 +v -0.011068 -0.005809 0.750000 +v -0.011068 -0.005809 0.625000 +v -0.011068 -0.005809 0.500000 +v -0.011068 -0.005809 0.375000 +v -0.011068 -0.005809 0.250000 +v -0.011068 -0.005809 0.125000 +v -0.007101 -0.010287 0.875000 +v -0.007101 -0.010287 0.750000 +v -0.007101 -0.010287 0.625000 +v -0.007101 -0.010287 0.500000 +v -0.007101 -0.010287 0.375000 +v -0.007101 -0.010287 0.250000 +v -0.007101 -0.010287 0.125000 +v -0.001507 -0.012409 0.875000 +v -0.001507 -0.012409 0.750000 +v -0.001507 -0.012409 0.625000 +v -0.001507 -0.012409 0.500000 +v -0.001507 -0.012409 0.375000 +v -0.001507 -0.012409 0.250000 +v -0.001507 -0.012409 0.125000 +v 0.004433 -0.011688 0.875000 +v 0.004433 -0.011688 0.750000 +v 0.004433 -0.011688 0.625000 +v 0.004433 -0.011688 0.500000 +v 0.004433 -0.011688 0.375000 +v 0.004433 -0.011688 0.250000 +v 0.004433 -0.011688 0.125000 +v 0.009356 -0.008289 0.875000 +v 0.009356 -0.008289 0.750000 +v 0.009356 -0.008289 0.625000 +v 0.009356 -0.008289 0.500000 +v 0.009356 -0.008289 0.375000 +v 0.009356 -0.008289 0.250000 +v 0.009356 -0.008289 0.125000 +v 0.012137 -0.002991 0.875000 +v 0.012137 -0.002991 0.750000 +v 0.012137 -0.002991 0.625000 +v 0.012137 -0.002991 0.500000 +v 0.012137 -0.002991 0.375000 +v 0.012137 -0.002991 0.250000 +v 0.012137 -0.002991 0.125000 +v 0.012137 0.002991 0.875000 +v 0.012137 0.002991 0.750000 +v 0.012137 0.002991 0.625000 +v 0.012137 0.002991 0.500000 +v 0.012137 0.002991 0.375000 +v 0.012137 0.002991 0.250000 +v 0.012137 0.002991 0.125000 +v 0.009356 0.008289 0.875000 +v 0.009356 0.008289 0.750000 +v 0.009356 0.008289 0.625000 +v 0.009356 0.008289 0.500000 +v 0.009356 0.008289 0.375000 +v 0.009356 0.008289 0.250000 +v 0.009356 0.008289 0.125000 +v 0.004433 0.011688 0.875000 +v 0.004433 0.011688 0.750000 +v 0.004433 0.011688 0.625000 +v 0.004433 0.011688 0.500000 +v 0.004433 0.011688 0.375000 +v 0.004433 0.011688 0.250000 +v 0.004433 0.011688 0.125000 +v -0.001507 0.012409 0.875000 +v -0.001507 0.012409 0.750000 +v -0.001507 0.012409 0.625000 +v -0.001507 0.012409 0.500000 +v -0.001507 0.012409 0.375000 +v -0.001507 0.012409 0.250000 +v -0.001507 0.012409 0.125000 +v -0.007101 0.010287 0.875000 +v -0.007101 0.010287 0.750000 +v -0.007101 0.010287 0.625000 +v -0.007101 0.010287 0.500000 +v -0.007101 0.010287 0.375000 +v -0.007101 0.010287 0.250000 +v -0.007101 0.010287 0.125000 +v -0.011068 0.005809 0.875000 +v -0.011068 0.005809 0.750000 +v -0.011068 0.005809 0.625000 +v -0.011068 0.005809 0.500000 +v -0.011068 0.005809 0.375000 +v -0.011068 0.005809 0.250000 +v -0.011068 0.005809 0.125000 +v -0.016750 -0.000000 1.000000 +v -0.015302 0.006813 1.000000 +v -0.011208 0.012448 1.000000 +v -0.005176 0.015930 1.000000 +v 0.001751 0.016658 1.000000 +v 0.008375 0.014506 1.000000 +v 0.013551 0.009845 1.000000 +v 0.016384 0.003483 1.000000 +v 0.016384 -0.003483 1.000000 +v 0.013551 -0.009845 1.000000 +v 0.008375 -0.014506 1.000000 +v 0.001751 -0.016658 1.000000 +v -0.005176 -0.015930 1.000000 +v -0.011208 -0.012448 1.000000 +v -0.015302 -0.006813 1.000000 +v -0.016750 -0.000000 0.000000 +v -0.015302 -0.006813 0.000000 +v -0.011208 -0.012448 0.000000 +v -0.005176 -0.015930 0.000000 +v 0.001751 -0.016658 0.000000 +v 0.008375 -0.014506 0.000000 +v 0.013551 -0.009845 0.000000 +v 0.016384 -0.003483 0.000000 +v 0.016384 0.003483 0.000000 +v 0.013551 0.009845 0.000000 +v 0.008375 0.014506 0.000000 +v 0.001751 0.016658 0.000000 +v -0.005176 0.015930 0.000000 +v -0.011208 0.012448 0.000000 +v -0.015302 0.006813 0.000000 +v -0.016750 -0.000000 0.857143 +v -0.016750 -0.000000 0.714286 +v -0.016750 -0.000000 0.571429 +v -0.016750 -0.000000 0.428571 +v -0.016750 -0.000000 0.285714 +v -0.016750 -0.000000 0.142857 +v -0.015302 0.006813 0.857143 +v -0.015302 0.006813 0.714286 +v -0.015302 0.006813 0.571429 +v -0.015302 0.006813 0.428571 +v -0.015302 0.006813 0.285714 +v -0.015302 0.006813 0.142857 +v -0.011208 0.012448 0.857143 +v -0.011208 0.012448 0.714286 +v -0.011208 0.012448 0.571429 +v -0.011208 0.012448 0.428571 +v -0.011208 0.012448 0.285714 +v -0.011208 0.012448 0.142857 +v -0.005176 0.015930 0.857143 +v -0.005176 0.015930 0.714286 +v -0.005176 0.015930 0.571429 +v -0.005176 0.015930 0.428571 +v -0.005176 0.015930 0.285714 +v -0.005176 0.015930 0.142857 +v 0.001751 0.016658 0.857143 +v 0.001751 0.016658 0.714286 +v 0.001751 0.016658 0.571429 +v 0.001751 0.016658 0.428571 +v 0.001751 0.016658 0.285714 +v 0.001751 0.016658 0.142857 +v 0.008375 0.014506 0.857143 +v 0.008375 0.014506 0.714286 +v 0.008375 0.014506 0.571429 +v 0.008375 0.014506 0.428571 +v 0.008375 0.014506 0.285714 +v 0.008375 0.014506 0.142857 +v 0.013551 0.009845 0.857143 +v 0.013551 0.009845 0.714286 +v 0.013551 0.009845 0.571429 +v 0.013551 0.009845 0.428571 +v 0.013551 0.009845 0.285714 +v 0.013551 0.009845 0.142857 +v 0.016384 0.003483 0.857143 +v 0.016384 0.003483 0.714286 +v 0.016384 0.003483 0.571429 +v 0.016384 0.003483 0.428571 +v 0.016384 0.003483 0.285714 +v 0.016384 0.003483 0.142857 +v 0.016384 -0.003483 0.857143 +v 0.016384 -0.003483 0.714286 +v 0.016384 -0.003483 0.571429 +v 0.016384 -0.003483 0.428571 +v 0.016384 -0.003483 0.285714 +v 0.016384 -0.003483 0.142857 +v 0.013551 -0.009845 0.857143 +v 0.013551 -0.009845 0.714286 +v 0.013551 -0.009845 0.571429 +v 0.013551 -0.009845 0.428571 +v 0.013551 -0.009845 0.285714 +v 0.013551 -0.009845 0.142857 +v 0.008375 -0.014506 0.857143 +v 0.008375 -0.014506 0.714286 +v 0.008375 -0.014506 0.571429 +v 0.008375 -0.014506 0.428571 +v 0.008375 -0.014506 0.285714 +v 0.008375 -0.014506 0.142857 +v 0.001751 -0.016658 0.857143 +v 0.001751 -0.016658 0.714286 +v 0.001751 -0.016658 0.571429 +v 0.001751 -0.016658 0.428571 +v 0.001751 -0.016658 0.285714 +v 0.001751 -0.016658 0.142857 +v -0.005176 -0.015930 0.857143 +v -0.005176 -0.015930 0.714286 +v -0.005176 -0.015930 0.571429 +v -0.005176 -0.015930 0.428571 +v -0.005176 -0.015930 0.285714 +v -0.005176 -0.015930 0.142857 +v -0.011208 -0.012448 0.857143 +v -0.011208 -0.012448 0.714286 +v -0.011208 -0.012448 0.571429 +v -0.011208 -0.012448 0.428571 +v -0.011208 -0.012448 0.285714 +v -0.011208 -0.012448 0.142857 +v -0.015302 -0.006813 0.857143 +v -0.015302 -0.006813 0.714286 +v -0.015302 -0.006813 0.571429 +v -0.015302 -0.006813 0.428571 +v -0.015302 -0.006813 0.285714 +v -0.015302 -0.006813 0.142857 +vt 100.000000 -3.322838 0.000000 +vt 87.500000 -3.322838 0.000000 +vt 100.000000 -3.926991 0.000000 +vt 87.500000 -3.926991 0.000000 +vt 100.000000 3.926991 0.000000 +vt 87.500000 3.926991 0.000000 +vt 100.000000 3.322838 0.000000 +vt 87.500000 3.322838 0.000000 +vt 100.000000 2.718686 0.000000 +vt 87.500000 2.718686 0.000000 +vt 100.000000 2.114534 0.000000 +vt 87.500000 2.114534 0.000000 +vt 100.000000 1.510381 0.000000 +vt 87.500000 1.510381 0.000000 +vt 100.000000 0.906229 0.000000 +vt 87.500000 0.906229 0.000000 +vt 100.000000 0.302076 0.000000 +vt 87.500000 0.302076 0.000000 +vt 100.000000 -0.302076 0.000000 +vt 87.500000 -0.302076 0.000000 +vt 100.000000 -0.906229 0.000000 +vt 87.500000 -0.906229 0.000000 +vt 100.000000 -1.510381 0.000000 +vt 87.500000 -1.510381 0.000000 +vt 100.000000 -2.114534 0.000000 +vt 87.500000 -2.114534 0.000000 +vt 100.000000 -2.718686 0.000000 +vt 87.500000 -2.718686 0.000000 +vt 0.000000 3.322838 0.000000 +vt 12.500000 3.322838 0.000000 +vt 0.000000 3.926991 0.000000 +vt 12.500000 3.926991 0.000000 +vt 0.000000 -3.926991 0.000000 +vt 12.500000 -3.926991 0.000000 +vt 0.000000 -3.322838 0.000000 +vt 12.500000 -3.322838 0.000000 +vt 0.000000 -2.718686 0.000000 +vt 12.500000 -2.718686 0.000000 +vt 0.000000 -2.114534 0.000000 +vt 12.500000 -2.114534 0.000000 +vt 0.000000 -1.510381 0.000000 +vt 12.500000 -1.510381 0.000000 +vt 0.000000 -0.906229 0.000000 +vt 12.500000 -0.906229 0.000000 +vt 0.000000 -0.302076 0.000000 +vt 12.500000 -0.302076 0.000000 +vt 0.000000 0.302076 0.000000 +vt 12.500000 0.302076 0.000000 +vt -0.000000 0.906229 0.000000 +vt 12.500000 0.906229 0.000000 +vt 0.000000 1.510381 0.000000 +vt 12.500000 1.510381 0.000000 +vt 0.000000 2.114534 0.000000 +vt 12.500000 2.114534 0.000000 +vt 0.000000 2.718686 0.000000 +vt 12.500000 2.718686 0.000000 +vt 75.000000 3.926991 0.000000 +vt 75.000000 3.322838 0.000000 +vt 75.000000 2.718686 0.000000 +vt 75.000000 2.114534 0.000000 +vt 75.000000 1.510381 0.000000 +vt 75.000000 0.906229 0.000000 +vt 75.000000 0.302076 0.000000 +vt 75.000000 -0.302076 0.000000 +vt 75.000000 -0.906229 0.000000 +vt 75.000000 -1.510381 0.000000 +vt 75.000000 -2.114534 0.000000 +vt 75.000000 -2.718686 0.000000 +vt 75.000000 -3.322838 0.000000 +vt 75.000000 -3.926991 0.000000 +vt 62.500000 3.926991 0.000000 +vt 62.500000 3.322838 0.000000 +vt 62.500000 2.718686 0.000000 +vt 62.500000 2.114534 0.000000 +vt 62.500000 1.510381 0.000000 +vt 62.500000 0.906229 0.000000 +vt 62.500000 0.302076 0.000000 +vt 62.500000 -0.302076 0.000000 +vt 62.500000 -0.906229 0.000000 +vt 62.500000 -1.510381 0.000000 +vt 62.500000 -2.114534 0.000000 +vt 62.500000 -2.718686 0.000000 +vt 62.500000 -3.322838 0.000000 +vt 62.500000 -3.926991 0.000000 +vt 50.000000 3.926991 0.000000 +vt 50.000000 3.322838 0.000000 +vt 50.000000 2.718686 0.000000 +vt 50.000000 2.114534 0.000000 +vt 50.000000 1.510381 0.000000 +vt 50.000000 0.906229 0.000000 +vt 50.000000 0.302076 0.000000 +vt 50.000000 -0.302076 0.000000 +vt 50.000000 -0.906229 0.000000 +vt 50.000000 -1.510381 0.000000 +vt 50.000000 -2.114534 0.000000 +vt 50.000000 -2.718686 0.000000 +vt 50.000000 -3.322838 0.000000 +vt 50.000000 -3.926991 0.000000 +vt 37.500000 3.926991 0.000000 +vt 37.500000 3.322838 0.000000 +vt 37.500000 2.718686 0.000000 +vt 37.500000 2.114534 0.000000 +vt 37.500000 1.510381 0.000000 +vt 37.500000 0.906229 0.000000 +vt 37.500000 0.302076 0.000000 +vt 37.500000 -0.302076 0.000000 +vt 37.500000 -0.906229 0.000000 +vt 37.500000 -1.510381 0.000000 +vt 37.500000 -2.114534 0.000000 +vt 37.500000 -2.718686 0.000000 +vt 37.500000 -3.322838 0.000000 +vt 37.500000 -3.926991 0.000000 +vt 25.000000 3.926991 0.000000 +vt 25.000000 3.322838 0.000000 +vt 25.000000 2.718686 0.000000 +vt 25.000000 2.114534 0.000000 +vt 25.000000 1.510381 0.000000 +vt 25.000000 0.906229 0.000000 +vt 25.000000 0.302076 0.000000 +vt 25.000000 -0.302076 0.000000 +vt 25.000000 -0.906229 0.000000 +vt 25.000000 -1.510381 0.000000 +vt 25.000000 -2.114534 0.000000 +vt 25.000000 -2.718686 0.000000 +vt 25.000000 -3.322838 0.000000 +vt 25.000000 -3.926991 0.000000 +vt -100.000000 4.560545 0.000000 +vt -85.714286 4.560545 0.000000 +vt -100.000000 5.262168 0.000000 +vt -85.714286 5.262168 0.000000 +vt -100.000000 -5.262168 0.000000 +vt -85.714286 -5.262168 0.000000 +vt -100.000000 -4.560545 0.000000 +vt -85.714286 -4.560545 0.000000 +vt -100.000000 -3.858923 0.000000 +vt -85.714286 -3.858923 0.000000 +vt -100.000000 -3.157301 0.000000 +vt -85.714286 -3.157301 0.000000 +vt -100.000000 -2.455678 0.000000 +vt -85.714286 -2.455678 0.000000 +vt -100.000000 -1.754056 0.000000 +vt -85.714286 -1.754056 0.000000 +vt -100.000000 -1.052434 0.000000 +vt -85.714286 -1.052434 0.000000 +vt -100.000000 -0.350811 0.000000 +vt -85.714286 -0.350811 0.000000 +vt -100.000000 0.350811 0.000000 +vt -85.714286 0.350811 0.000000 +vt -100.000000 1.052434 0.000000 +vt -85.714286 1.052434 0.000000 +vt -100.000000 1.754056 0.000000 +vt -85.714286 1.754056 0.000000 +vt -100.000000 2.455678 0.000000 +vt -85.714286 2.455678 0.000000 +vt -100.000000 3.157301 0.000000 +vt -85.714286 3.157301 0.000000 +vt -100.000000 3.858923 0.000000 +vt -85.714286 3.858923 0.000000 +vt -0.000000 -4.560545 0.000000 +vt -14.285714 -4.560545 0.000000 +vt -0.000000 -5.262168 0.000000 +vt -14.285714 -5.262168 0.000000 +vt -0.000000 5.262168 0.000000 +vt -14.285714 5.262168 0.000000 +vt -0.000000 4.560545 0.000000 +vt -14.285714 4.560545 0.000000 +vt -0.000000 3.858923 0.000000 +vt -14.285714 3.858923 0.000000 +vt -0.000000 3.157301 0.000000 +vt -14.285714 3.157301 0.000000 +vt -0.000000 2.455678 0.000000 +vt -14.285714 2.455678 0.000000 +vt -0.000000 1.754056 0.000000 +vt -14.285714 1.754056 0.000000 +vt -0.000000 1.052434 0.000000 +vt -14.285714 1.052434 0.000000 +vt -0.000000 0.350811 0.000000 +vt -14.285714 0.350811 0.000000 +vt -0.000000 -0.350811 0.000000 +vt -14.285714 -0.350811 0.000000 +vt -0.000000 -1.052434 0.000000 +vt -14.285714 -1.052434 0.000000 +vt -0.000000 -1.754056 0.000000 +vt -14.285714 -1.754056 0.000000 +vt -0.000000 -2.455678 0.000000 +vt -14.285714 -2.455678 0.000000 +vt -0.000000 -3.157301 0.000000 +vt -14.285714 -3.157301 0.000000 +vt -0.000000 -3.858923 0.000000 +vt -14.285714 -3.858923 0.000000 +vt -71.428571 -5.262168 0.000000 +vt -71.428571 -4.560545 0.000000 +vt -71.428571 -3.858923 0.000000 +vt -71.428571 -3.157301 0.000000 +vt -71.428571 -2.455678 0.000000 +vt -71.428571 -1.754056 0.000000 +vt -71.428571 -1.052434 0.000000 +vt -71.428571 -0.350811 0.000000 +vt -71.428571 0.350811 0.000000 +vt -71.428571 1.052434 0.000000 +vt -71.428571 1.754056 0.000000 +vt -71.428571 2.455678 0.000000 +vt -71.428571 3.157301 0.000000 +vt -71.428571 3.858923 0.000000 +vt -71.428571 4.560545 0.000000 +vt -71.428571 5.262168 0.000000 +vt -57.142857 -5.262168 0.000000 +vt -57.142857 -4.560545 0.000000 +vt -57.142857 -3.858923 0.000000 +vt -57.142857 -3.157301 0.000000 +vt -57.142857 -2.455678 0.000000 +vt -57.142857 -1.754056 0.000000 +vt -57.142857 -1.052434 0.000000 +vt -57.142857 -0.350811 0.000000 +vt -57.142857 0.350811 0.000000 +vt -57.142857 1.052434 0.000000 +vt -57.142857 1.754056 0.000000 +vt -57.142857 2.455678 0.000000 +vt -57.142857 3.157301 0.000000 +vt -57.142857 3.858923 0.000000 +vt -57.142857 4.560545 0.000000 +vt -57.142857 5.262168 0.000000 +vt -42.857143 -5.262168 0.000000 +vt -42.857143 -4.560545 0.000000 +vt -42.857143 -3.858923 0.000000 +vt -42.857143 -3.157301 0.000000 +vt -42.857143 -2.455678 0.000000 +vt -42.857143 -1.754056 0.000000 +vt -42.857143 -1.052434 0.000000 +vt -42.857143 -0.350811 0.000000 +vt -42.857143 0.350811 0.000000 +vt -42.857143 1.052434 0.000000 +vt -42.857143 1.754056 0.000000 +vt -42.857143 2.455678 0.000000 +vt -42.857143 3.157301 0.000000 +vt -42.857143 3.858923 0.000000 +vt -42.857143 4.560545 0.000000 +vt -42.857143 5.262168 0.000000 +vt -28.571429 -5.262168 0.000000 +vt -28.571429 -4.560545 0.000000 +vt -28.571429 -3.858923 0.000000 +vt -28.571429 -3.157301 0.000000 +vt -28.571429 -2.455678 0.000000 +vt -28.571429 -1.754056 0.000000 +vt -28.571429 -1.052434 0.000000 +vt -28.571429 -0.350811 0.000000 +vt -28.571429 0.350811 0.000000 +vt -28.571429 1.052434 0.000000 +vt -28.571429 1.754056 0.000000 +vt -28.571429 2.455678 0.000000 +vt -28.571429 3.157301 0.000000 +vt -28.571429 3.858923 0.000000 +vt -28.571429 4.560545 0.000000 +vt -28.571429 5.262168 0.000000 +vt -1.106820 0.580904 0.000000 +vt -1.530189 0.681284 0.000000 +vt -1.250000 -0.000000 0.000000 +vt -1.675000 -0.000000 0.000000 +vt -1.530189 -0.681284 0.000000 +vt -1.120794 1.244768 0.000000 +vt -0.710081 1.028730 0.000000 +vt -0.517603 1.593020 0.000000 +vt -0.150671 1.240886 0.000000 +vt 0.175085 1.665824 0.000000 +vt 0.443256 1.168770 0.000000 +vt 0.837500 1.450593 0.000000 +vt 0.935638 0.828903 0.000000 +vt 1.355103 0.984540 0.000000 +vt 1.213677 0.299145 0.000000 +vt 1.638397 0.348252 0.000000 +vt 1.638397 -0.348252 0.000000 +vt 1.213677 -0.299145 0.000000 +vt 1.355103 -0.984540 0.000000 +vt 0.935638 -0.828903 0.000000 +vt 0.837500 -1.450593 0.000000 +vt 0.443256 -1.168770 0.000000 +vt 0.175085 -1.665824 0.000000 +vt -0.150671 -1.240886 0.000000 +vt -0.517603 -1.593020 0.000000 +vt -0.710081 -1.028730 0.000000 +vt -1.120794 -1.244768 0.000000 +vt -1.106820 -0.580904 0.000000 +vt 1.106820 -0.580904 0.000000 +vt 1.530189 -0.681284 0.000000 +vt 1.250000 -0.000000 0.000000 +vt 1.675000 -0.000000 0.000000 +vt 1.530189 0.681284 0.000000 +vt 1.120794 -1.244768 0.000000 +vt 0.710081 -1.028730 0.000000 +vt 0.517603 -1.593020 0.000000 +vt 0.150671 -1.240886 0.000000 +vt -0.175085 -1.665824 0.000000 +vt -0.443256 -1.168770 0.000000 +vt -0.837500 -1.450593 0.000000 +vt -0.935638 -0.828903 0.000000 +vt -1.355103 -0.984540 0.000000 +vt -1.213677 -0.299145 0.000000 +vt -1.638397 -0.348252 0.000000 +vt -1.213677 0.299145 0.000000 +vt -1.638397 0.348252 0.000000 +vt -1.355103 0.984540 0.000000 +vt -0.935638 0.828903 0.000000 +vt -0.837500 1.450593 0.000000 +vt -0.443256 1.168770 0.000000 +vt -0.175085 1.665824 0.000000 +vt 0.150671 1.240886 0.000000 +vt 0.517603 1.593020 0.000000 +vt 0.710081 1.028730 0.000000 +vt 1.120794 1.244768 0.000000 +vt 1.106820 0.580904 0.000000 +vn 88.545603 46.472317 0.000000 +vn 88.545603 46.472317 0.000000 +vn 100.000000 0.000000 0.000000 +vn 100.000000 0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 46.472317 0.000000 +vn 88.545603 46.472317 0.000000 +vn 56.806475 82.298387 0.000000 +vn 56.806475 82.298387 0.000000 +vn 12.053668 99.270887 0.000000 +vn 12.053668 99.270887 0.000000 +vn -35.460489 93.501624 0.000000 +vn -35.460489 93.501624 0.000000 +vn -74.851075 66.312266 0.000000 +vn -74.851075 66.312266 0.000000 +vn -97.094182 23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -35.460489 -93.501624 0.000000 +vn 12.053668 -99.270887 0.000000 +vn 12.053668 -99.270887 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 46.472317 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 46.472317 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 46.472317 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 46.472317 0.000000 +vn 100.000000 -0.000000 0.000000 +vn 88.545603 -46.472317 0.000000 +vn 56.806475 -82.298387 0.000000 +vn 12.053668 -99.270887 0.000000 +vn -35.460489 -93.501624 0.000000 +vn -74.851075 -66.312266 0.000000 +vn -97.094182 -23.931566 0.000000 +vn -97.094182 23.931566 0.000000 +vn -74.851075 66.312266 0.000000 +vn -35.460489 93.501624 0.000000 +vn 12.053668 99.270887 0.000000 +vn 56.806475 82.298387 0.000000 +vn 88.545603 46.472317 0.000000 +vn -91.354546 40.673664 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn -100.000000 0.000000 -0.000000 +vn -100.000000 0.000000 -0.000000 +vn -91.354546 -40.673664 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -30.901699 -95.105652 0.000000 +vn -30.901699 -95.105652 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 80.901699 58.778525 0.000000 +vn 80.901699 58.778525 0.000000 +vn 50.000000 86.602540 0.000000 +vn 50.000000 86.602540 0.000000 +vn 10.452846 99.452190 0.000000 +vn 10.452846 99.452190 0.000000 +vn -30.901699 95.105652 -0.000000 +vn -30.901699 95.105652 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -91.354546 -40.673664 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -100.000000 -0.000000 0.000000 +vn -100.000000 -0.000000 0.000000 +vn -91.354546 40.673664 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -30.901699 95.105652 -0.000000 +vn -30.901699 95.105652 -0.000000 +vn 10.452846 99.452190 0.000000 +vn 10.452846 99.452190 0.000000 +vn 50.000000 86.602540 0.000000 +vn 50.000000 86.602540 0.000000 +vn 80.901699 58.778525 0.000000 +vn 80.901699 58.778525 0.000000 +vn 97.814760 20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 10.452846 -99.452190 0.000000 +vn -30.901699 -95.105652 0.000000 +vn -30.901699 -95.105652 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -100.000000 -0.000000 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -30.901699 -95.105652 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 80.901699 58.778525 0.000000 +vn 50.000000 86.602540 0.000000 +vn 10.452846 99.452190 0.000000 +vn -30.901699 95.105652 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn -100.000000 -0.000000 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -30.901699 -95.105652 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 80.901699 58.778525 0.000000 +vn 50.000000 86.602540 0.000000 +vn 10.452846 99.452190 0.000000 +vn -30.901699 95.105652 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn -100.000000 -0.000000 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -30.901699 -95.105652 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 80.901699 58.778525 0.000000 +vn 50.000000 86.602540 0.000000 +vn 10.452846 99.452190 0.000000 +vn -30.901699 95.105652 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn -100.000000 -0.000000 0.000000 +vn -91.354546 -40.673664 0.000000 +vn -66.913061 -74.314483 0.000000 +vn -30.901699 -95.105652 0.000000 +vn 10.452846 -99.452190 0.000000 +vn 50.000000 -86.602540 0.000000 +vn 80.901699 -58.778525 0.000000 +vn 97.814760 -20.791169 0.000000 +vn 97.814760 20.791169 0.000000 +vn 80.901699 58.778525 0.000000 +vn 50.000000 86.602540 0.000000 +vn 10.452846 99.452190 0.000000 +vn -30.901699 95.105652 -0.000000 +vn -66.913061 74.314483 -0.000000 +vn -91.354546 40.673664 -0.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +vn 0.000000 0.000000 -100.000000 +usemtl Steel_-_Satin +f 2/1/1 34/2/2 1/3/3 +f 1/3/3 34/2/2 27/4/4 +f 1/5/3 27/6/4 13/7/5 +f 13/7/5 27/6/4 111/8/6 +f 13/7/5 111/8/6 12/9/7 +f 12/9/7 111/8/6 104/10/8 +f 12/9/7 104/10/8 11/11/9 +f 11/11/9 104/10/8 97/12/10 +f 11/11/9 97/12/10 10/13/11 +f 10/13/11 97/12/10 90/14/12 +f 10/13/11 90/14/12 9/15/13 +f 9/15/13 90/14/12 83/16/14 +f 9/15/13 83/16/14 8/17/15 +f 8/17/15 83/16/14 76/18/16 +f 8/17/15 76/18/16 7/19/17 +f 7/19/17 76/18/16 69/20/18 +f 7/19/17 69/20/18 6/21/19 +f 6/21/19 69/20/18 62/22/20 +f 6/21/19 62/22/20 5/23/21 +f 5/23/21 62/22/20 55/24/22 +f 5/23/21 55/24/22 4/25/23 +f 4/25/23 55/24/22 48/26/24 +f 4/25/23 48/26/24 3/27/25 +f 3/27/25 48/26/24 41/28/26 +f 3/27/25 41/28/26 2/1/1 +f 2/1/1 41/28/26 34/2/2 +f 15/29/27 117/30/28 14/31/29 +f 14/31/29 117/30/28 33/32/30 +f 14/33/29 33/34/30 26/35/31 +f 26/35/31 33/34/30 40/36/32 +f 26/35/31 40/36/32 25/37/33 +f 25/37/33 40/36/32 47/38/34 +f 25/37/33 47/38/34 24/39/35 +f 24/39/35 47/38/34 54/40/36 +f 24/39/35 54/40/36 23/41/37 +f 23/41/37 54/40/36 61/42/38 +f 23/41/37 61/42/38 22/43/39 +f 22/43/39 61/42/38 68/44/40 +f 22/43/39 68/44/40 21/45/41 +f 21/45/41 68/44/40 75/46/42 +f 21/45/41 75/46/42 20/47/43 +f 20/47/43 75/46/42 82/48/44 +f 20/47/43 82/48/44 19/49/45 +f 19/49/45 82/48/44 89/50/46 +f 19/49/45 89/50/46 18/51/47 +f 18/51/47 89/50/46 96/52/48 +f 18/51/47 96/52/48 17/53/49 +f 17/53/49 96/52/48 103/54/50 +f 17/53/49 103/54/50 16/55/51 +f 16/55/51 103/54/50 110/56/52 +f 16/55/51 110/56/52 15/29/27 +f 15/29/27 110/56/52 117/30/28 +f 27/6/4 28/57/53 111/8/6 +f 111/8/6 28/57/53 112/58/54 +f 111/8/6 112/58/54 104/10/8 +f 104/10/8 112/58/54 105/59/55 +f 104/10/8 105/59/55 97/12/10 +f 97/12/10 105/59/55 98/60/56 +f 97/12/10 98/60/56 90/14/12 +f 90/14/12 98/60/56 91/61/57 +f 90/14/12 91/61/57 83/16/14 +f 83/16/14 91/61/57 84/62/58 +f 83/16/14 84/62/58 76/18/16 +f 76/18/16 84/62/58 77/63/59 +f 76/18/16 77/63/59 69/20/18 +f 69/20/18 77/63/59 70/64/60 +f 69/20/18 70/64/60 62/22/20 +f 62/22/20 70/64/60 63/65/61 +f 62/22/20 63/65/61 55/24/22 +f 55/24/22 63/65/61 56/66/62 +f 55/24/22 56/66/62 48/26/24 +f 48/26/24 56/66/62 49/67/63 +f 48/26/24 49/67/63 41/28/26 +f 41/28/26 49/67/63 42/68/64 +f 41/28/26 42/68/64 34/2/2 +f 34/2/2 42/68/64 35/69/65 +f 34/2/2 35/69/65 27/4/4 +f 27/4/4 35/69/65 28/70/53 +f 28/57/53 29/71/66 112/58/54 +f 112/58/54 29/71/66 113/72/67 +f 112/58/54 113/72/67 105/59/55 +f 105/59/55 113/72/67 106/73/68 +f 105/59/55 106/73/68 98/60/56 +f 98/60/56 106/73/68 99/74/69 +f 98/60/56 99/74/69 91/61/57 +f 91/61/57 99/74/69 92/75/70 +f 91/61/57 92/75/70 84/62/58 +f 84/62/58 92/75/70 85/76/71 +f 84/62/58 85/76/71 77/63/59 +f 77/63/59 85/76/71 78/77/72 +f 77/63/59 78/77/72 70/64/60 +f 70/64/60 78/77/72 71/78/73 +f 70/64/60 71/78/73 63/65/61 +f 63/65/61 71/78/73 64/79/74 +f 63/65/61 64/79/74 56/66/62 +f 56/66/62 64/79/74 57/80/75 +f 56/66/62 57/80/75 49/67/63 +f 49/67/63 57/80/75 50/81/76 +f 49/67/63 50/81/76 42/68/64 +f 42/68/64 50/81/76 43/82/77 +f 42/68/64 43/82/77 35/69/65 +f 35/69/65 43/82/77 36/83/78 +f 35/69/65 36/83/78 28/70/53 +f 28/70/53 36/83/78 29/84/66 +f 29/71/66 30/85/79 113/72/67 +f 113/72/67 30/85/79 114/86/80 +f 113/72/67 114/86/80 106/73/68 +f 106/73/68 114/86/80 107/87/81 +f 106/73/68 107/87/81 99/74/69 +f 99/74/69 107/87/81 100/88/82 +f 99/74/69 100/88/82 92/75/70 +f 92/75/70 100/88/82 93/89/83 +f 92/75/70 93/89/83 85/76/71 +f 85/76/71 93/89/83 86/90/84 +f 85/76/71 86/90/84 78/77/72 +f 78/77/72 86/90/84 79/91/85 +f 78/77/72 79/91/85 71/78/73 +f 71/78/73 79/91/85 72/92/86 +f 71/78/73 72/92/86 64/79/74 +f 64/79/74 72/92/86 65/93/87 +f 64/79/74 65/93/87 57/80/75 +f 57/80/75 65/93/87 58/94/88 +f 57/80/75 58/94/88 50/81/76 +f 50/81/76 58/94/88 51/95/89 +f 50/81/76 51/95/89 43/82/77 +f 43/82/77 51/95/89 44/96/90 +f 43/82/77 44/96/90 36/83/78 +f 36/83/78 44/96/90 37/97/91 +f 36/83/78 37/97/91 29/84/66 +f 29/84/66 37/97/91 30/98/79 +f 30/85/79 31/99/92 114/86/80 +f 114/86/80 31/99/92 115/100/93 +f 114/86/80 115/100/93 107/87/81 +f 107/87/81 115/100/93 108/101/94 +f 107/87/81 108/101/94 100/88/82 +f 100/88/82 108/101/94 101/102/95 +f 100/88/82 101/102/95 93/89/83 +f 93/89/83 101/102/95 94/103/96 +f 93/89/83 94/103/96 86/90/84 +f 86/90/84 94/103/96 87/104/97 +f 86/90/84 87/104/97 79/91/85 +f 79/91/85 87/104/97 80/105/98 +f 79/91/85 80/105/98 72/92/86 +f 72/92/86 80/105/98 73/106/99 +f 72/92/86 73/106/99 65/93/87 +f 65/93/87 73/106/99 66/107/100 +f 65/93/87 66/107/100 58/94/88 +f 58/94/88 66/107/100 59/108/101 +f 58/94/88 59/108/101 51/95/89 +f 51/95/89 59/108/101 52/109/102 +f 51/95/89 52/109/102 44/96/90 +f 44/96/90 52/109/102 45/110/103 +f 44/96/90 45/110/103 37/97/91 +f 37/97/91 45/110/103 38/111/104 +f 37/97/91 38/111/104 30/98/79 +f 30/98/79 38/111/104 31/112/92 +f 31/99/92 32/113/105 115/100/93 +f 115/100/93 32/113/105 116/114/106 +f 115/100/93 116/114/106 108/101/94 +f 108/101/94 116/114/106 109/115/107 +f 108/101/94 109/115/107 101/102/95 +f 101/102/95 109/115/107 102/116/108 +f 101/102/95 102/116/108 94/103/96 +f 94/103/96 102/116/108 95/117/109 +f 94/103/96 95/117/109 87/104/97 +f 87/104/97 95/117/109 88/118/110 +f 87/104/97 88/118/110 80/105/98 +f 80/105/98 88/118/110 81/119/111 +f 80/105/98 81/119/111 73/106/99 +f 73/106/99 81/119/111 74/120/112 +f 73/106/99 74/120/112 66/107/100 +f 66/107/100 74/120/112 67/121/113 +f 66/107/100 67/121/113 59/108/101 +f 59/108/101 67/121/113 60/122/114 +f 59/108/101 60/122/114 52/109/102 +f 52/109/102 60/122/114 53/123/115 +f 52/109/102 53/123/115 45/110/103 +f 45/110/103 53/123/115 46/124/116 +f 45/110/103 46/124/116 38/111/104 +f 38/111/104 46/124/116 39/125/117 +f 38/111/104 39/125/117 31/112/92 +f 31/112/92 39/125/117 32/126/105 +f 32/113/105 33/32/30 116/114/106 +f 116/114/106 33/32/30 117/30/28 +f 116/114/106 117/30/28 109/115/107 +f 109/115/107 117/30/28 110/56/52 +f 109/115/107 110/56/52 102/116/108 +f 102/116/108 110/56/52 103/54/50 +f 102/116/108 103/54/50 95/117/109 +f 95/117/109 103/54/50 96/52/48 +f 95/117/109 96/52/48 88/118/110 +f 88/118/110 96/52/48 89/50/46 +f 88/118/110 89/50/46 81/119/111 +f 81/119/111 89/50/46 82/48/44 +f 81/119/111 82/48/44 74/120/112 +f 74/120/112 82/48/44 75/46/42 +f 74/120/112 75/46/42 67/121/113 +f 67/121/113 75/46/42 68/44/40 +f 67/121/113 68/44/40 60/122/114 +f 60/122/114 68/44/40 61/42/38 +f 60/122/114 61/42/38 53/123/115 +f 53/123/115 61/42/38 54/40/36 +f 53/123/115 54/40/36 46/124/116 +f 46/124/116 54/40/36 47/38/34 +f 46/124/116 47/38/34 39/125/117 +f 39/125/117 47/38/34 40/36/32 +f 39/125/117 40/36/32 32/126/105 +f 32/126/105 40/36/32 33/34/30 +f 119/127/118 154/128/119 118/129/120 +f 118/129/120 154/128/119 148/130/121 +f 118/131/120 148/132/121 132/133/122 +f 132/133/122 148/132/121 232/134/123 +f 132/133/122 232/134/123 131/135/124 +f 131/135/124 232/134/123 226/136/125 +f 131/135/124 226/136/125 130/137/126 +f 130/137/126 226/136/125 220/138/127 +f 130/137/126 220/138/127 129/139/128 +f 129/139/128 220/138/127 214/140/129 +f 129/139/128 214/140/129 128/141/130 +f 128/141/130 214/140/129 208/142/131 +f 128/141/130 208/142/131 127/143/132 +f 127/143/132 208/142/131 202/144/133 +f 127/143/132 202/144/133 126/145/134 +f 126/145/134 202/144/133 196/146/135 +f 126/145/134 196/146/135 125/147/136 +f 125/147/136 196/146/135 190/148/137 +f 125/147/136 190/148/137 124/149/138 +f 124/149/138 190/148/137 184/150/139 +f 124/149/138 184/150/139 123/151/140 +f 123/151/140 184/150/139 178/152/141 +f 123/151/140 178/152/141 122/153/142 +f 122/153/142 178/152/141 172/154/143 +f 122/153/142 172/154/143 121/155/144 +f 121/155/144 172/154/143 166/156/145 +f 121/155/144 166/156/145 120/157/146 +f 120/157/146 166/156/145 160/158/147 +f 120/157/146 160/158/147 119/127/118 +f 119/127/118 160/158/147 154/128/119 +f 134/159/148 237/160/149 133/161/150 +f 133/161/150 237/160/149 153/162/151 +f 133/163/150 153/164/151 147/165/152 +f 147/165/152 153/164/151 159/166/153 +f 147/165/152 159/166/153 146/167/154 +f 146/167/154 159/166/153 165/168/155 +f 146/167/154 165/168/155 145/169/156 +f 145/169/156 165/168/155 171/170/157 +f 145/169/156 171/170/157 144/171/158 +f 144/171/158 171/170/157 177/172/159 +f 144/171/158 177/172/159 143/173/160 +f 143/173/160 177/172/159 183/174/161 +f 143/173/160 183/174/161 142/175/162 +f 142/175/162 183/174/161 189/176/163 +f 142/175/162 189/176/163 141/177/164 +f 141/177/164 189/176/163 195/178/165 +f 141/177/164 195/178/165 140/179/166 +f 140/179/166 195/178/165 201/180/167 +f 140/179/166 201/180/167 139/181/168 +f 139/181/168 201/180/167 207/182/169 +f 139/181/168 207/182/169 138/183/170 +f 138/183/170 207/182/169 213/184/171 +f 138/183/170 213/184/171 137/185/172 +f 137/185/172 213/184/171 219/186/173 +f 137/185/172 219/186/173 136/187/174 +f 136/187/174 219/186/173 225/188/175 +f 136/187/174 225/188/175 135/189/176 +f 135/189/176 225/188/175 231/190/177 +f 135/189/176 231/190/177 134/159/148 +f 134/159/148 231/190/177 237/160/149 +f 148/132/121 149/191/178 232/134/123 +f 232/134/123 149/191/178 233/192/179 +f 232/134/123 233/192/179 226/136/125 +f 226/136/125 233/192/179 227/193/180 +f 226/136/125 227/193/180 220/138/127 +f 220/138/127 227/193/180 221/194/181 +f 220/138/127 221/194/181 214/140/129 +f 214/140/129 221/194/181 215/195/182 +f 214/140/129 215/195/182 208/142/131 +f 208/142/131 215/195/182 209/196/183 +f 208/142/131 209/196/183 202/144/133 +f 202/144/133 209/196/183 203/197/184 +f 202/144/133 203/197/184 196/146/135 +f 196/146/135 203/197/184 197/198/185 +f 196/146/135 197/198/185 190/148/137 +f 190/148/137 197/198/185 191/199/186 +f 190/148/137 191/199/186 184/150/139 +f 184/150/139 191/199/186 185/200/187 +f 184/150/139 185/200/187 178/152/141 +f 178/152/141 185/200/187 179/201/188 +f 178/152/141 179/201/188 172/154/143 +f 172/154/143 179/201/188 173/202/189 +f 172/154/143 173/202/189 166/156/145 +f 166/156/145 173/202/189 167/203/190 +f 166/156/145 167/203/190 160/158/147 +f 160/158/147 167/203/190 161/204/191 +f 160/158/147 161/204/191 154/128/119 +f 154/128/119 161/204/191 155/205/192 +f 154/128/119 155/205/192 148/130/121 +f 148/130/121 155/205/192 149/206/178 +f 149/191/178 150/207/193 233/192/179 +f 233/192/179 150/207/193 234/208/194 +f 233/192/179 234/208/194 227/193/180 +f 227/193/180 234/208/194 228/209/195 +f 227/193/180 228/209/195 221/194/181 +f 221/194/181 228/209/195 222/210/196 +f 221/194/181 222/210/196 215/195/182 +f 215/195/182 222/210/196 216/211/197 +f 215/195/182 216/211/197 209/196/183 +f 209/196/183 216/211/197 210/212/198 +f 209/196/183 210/212/198 203/197/184 +f 203/197/184 210/212/198 204/213/199 +f 203/197/184 204/213/199 197/198/185 +f 197/198/185 204/213/199 198/214/200 +f 197/198/185 198/214/200 191/199/186 +f 191/199/186 198/214/200 192/215/201 +f 191/199/186 192/215/201 185/200/187 +f 185/200/187 192/215/201 186/216/202 +f 185/200/187 186/216/202 179/201/188 +f 179/201/188 186/216/202 180/217/203 +f 179/201/188 180/217/203 173/202/189 +f 173/202/189 180/217/203 174/218/204 +f 173/202/189 174/218/204 167/203/190 +f 167/203/190 174/218/204 168/219/205 +f 167/203/190 168/219/205 161/204/191 +f 161/204/191 168/219/205 162/220/206 +f 161/204/191 162/220/206 155/205/192 +f 155/205/192 162/220/206 156/221/207 +f 155/205/192 156/221/207 149/206/178 +f 149/206/178 156/221/207 150/222/193 +f 150/207/193 151/223/208 234/208/194 +f 234/208/194 151/223/208 235/224/209 +f 234/208/194 235/224/209 228/209/195 +f 228/209/195 235/224/209 229/225/210 +f 228/209/195 229/225/210 222/210/196 +f 222/210/196 229/225/210 223/226/211 +f 222/210/196 223/226/211 216/211/197 +f 216/211/197 223/226/211 217/227/212 +f 216/211/197 217/227/212 210/212/198 +f 210/212/198 217/227/212 211/228/213 +f 210/212/198 211/228/213 204/213/199 +f 204/213/199 211/228/213 205/229/214 +f 204/213/199 205/229/214 198/214/200 +f 198/214/200 205/229/214 199/230/215 +f 198/214/200 199/230/215 192/215/201 +f 192/215/201 199/230/215 193/231/216 +f 192/215/201 193/231/216 186/216/202 +f 186/216/202 193/231/216 187/232/217 +f 186/216/202 187/232/217 180/217/203 +f 180/217/203 187/232/217 181/233/218 +f 180/217/203 181/233/218 174/218/204 +f 174/218/204 181/233/218 175/234/219 +f 174/218/204 175/234/219 168/219/205 +f 168/219/205 175/234/219 169/235/220 +f 168/219/205 169/235/220 162/220/206 +f 162/220/206 169/235/220 163/236/221 +f 162/220/206 163/236/221 156/221/207 +f 156/221/207 163/236/221 157/237/222 +f 156/221/207 157/237/222 150/222/193 +f 150/222/193 157/237/222 151/238/208 +f 151/223/208 152/239/223 235/224/209 +f 235/224/209 152/239/223 236/240/224 +f 235/224/209 236/240/224 229/225/210 +f 229/225/210 236/240/224 230/241/225 +f 229/225/210 230/241/225 223/226/211 +f 223/226/211 230/241/225 224/242/226 +f 223/226/211 224/242/226 217/227/212 +f 217/227/212 224/242/226 218/243/227 +f 217/227/212 218/243/227 211/228/213 +f 211/228/213 218/243/227 212/244/228 +f 211/228/213 212/244/228 205/229/214 +f 205/229/214 212/244/228 206/245/229 +f 205/229/214 206/245/229 199/230/215 +f 199/230/215 206/245/229 200/246/230 +f 199/230/215 200/246/230 193/231/216 +f 193/231/216 200/246/230 194/247/231 +f 193/231/216 194/247/231 187/232/217 +f 187/232/217 194/247/231 188/248/232 +f 187/232/217 188/248/232 181/233/218 +f 181/233/218 188/248/232 182/249/233 +f 181/233/218 182/249/233 175/234/219 +f 175/234/219 182/249/233 176/250/234 +f 175/234/219 176/250/234 169/235/220 +f 169/235/220 176/250/234 170/251/235 +f 169/235/220 170/251/235 163/236/221 +f 163/236/221 170/251/235 164/252/236 +f 163/236/221 164/252/236 157/237/222 +f 157/237/222 164/252/236 158/253/237 +f 157/237/222 158/253/237 151/238/208 +f 151/238/208 158/253/237 152/254/223 +f 152/239/223 153/162/151 236/240/224 +f 236/240/224 153/162/151 237/160/149 +f 236/240/224 237/160/149 230/241/225 +f 230/241/225 237/160/149 231/190/177 +f 230/241/225 231/190/177 224/242/226 +f 224/242/226 231/190/177 225/188/175 +f 224/242/226 225/188/175 218/243/227 +f 218/243/227 225/188/175 219/186/173 +f 218/243/227 219/186/173 212/244/228 +f 212/244/228 219/186/173 213/184/171 +f 212/244/228 213/184/171 206/245/229 +f 206/245/229 213/184/171 207/182/169 +f 206/245/229 207/182/169 200/246/230 +f 200/246/230 207/182/169 201/180/167 +f 200/246/230 201/180/167 194/247/231 +f 194/247/231 201/180/167 195/178/165 +f 194/247/231 195/178/165 188/248/232 +f 188/248/232 195/178/165 189/176/163 +f 188/248/232 189/176/163 182/249/233 +f 182/249/233 189/176/163 183/174/161 +f 182/249/233 183/174/161 176/250/234 +f 176/250/234 183/174/161 177/172/159 +f 176/250/234 177/172/159 170/251/235 +f 170/251/235 177/172/159 171/170/157 +f 170/251/235 171/170/157 164/252/236 +f 164/252/236 171/170/157 165/168/155 +f 164/252/236 165/168/155 158/253/237 +f 158/253/237 165/168/155 159/166/153 +f 158/253/237 159/166/153 152/254/223 +f 152/254/223 159/166/153 153/164/151 +f 13/255/238 119/256/239 1/257/240 +f 1/257/240 119/256/239 118/258/241 +f 1/257/240 118/258/241 132/259/242 +f 119/256/239 13/255/238 120/260/243 +f 120/260/243 13/255/238 12/261/244 +f 120/260/243 12/261/244 121/262/245 +f 121/262/245 12/261/244 11/263/246 +f 121/262/245 11/263/246 122/264/247 +f 122/264/247 11/263/246 10/265/248 +f 122/264/247 10/265/248 123/266/249 +f 123/266/249 10/265/248 9/267/250 +f 123/266/249 9/267/250 124/268/251 +f 124/268/251 9/267/250 8/269/252 +f 124/268/251 8/269/252 125/270/253 +f 125/270/253 8/269/252 126/271/254 +f 126/271/254 8/269/252 7/272/255 +f 126/271/254 7/272/255 127/273/256 +f 127/273/256 7/272/255 6/274/257 +f 127/273/256 6/274/257 128/275/258 +f 128/275/258 6/274/257 5/276/259 +f 128/275/258 5/276/259 129/277/260 +f 129/277/260 5/276/259 4/278/261 +f 129/277/260 4/278/261 130/279/262 +f 130/279/262 4/278/261 3/280/263 +f 130/279/262 3/280/263 131/281/264 +f 131/281/264 3/280/263 2/282/265 +f 131/281/264 2/282/265 132/259/242 +f 132/259/242 2/282/265 1/257/240 +f 26/283/266 134/284/267 14/285/268 +f 14/285/268 134/284/267 133/286/269 +f 14/285/268 133/286/269 147/287/270 +f 134/284/267 26/283/266 135/288/271 +f 135/288/271 26/283/266 25/289/272 +f 135/288/271 25/289/272 136/290/273 +f 136/290/273 25/289/272 24/291/274 +f 136/290/273 24/291/274 137/292/275 +f 137/292/275 24/291/274 23/293/276 +f 137/292/275 23/293/276 138/294/277 +f 138/294/277 23/293/276 22/295/278 +f 138/294/277 22/295/278 139/296/279 +f 139/296/279 22/295/278 21/297/280 +f 139/296/279 21/297/280 140/298/281 +f 140/298/281 21/297/280 20/299/282 +f 140/298/281 20/299/282 141/300/283 +f 141/300/283 20/299/282 142/301/284 +f 142/301/284 20/299/282 19/302/285 +f 142/301/284 19/302/285 143/303/286 +f 143/303/286 19/302/285 18/304/287 +f 143/303/286 18/304/287 144/305/288 +f 144/305/288 18/304/287 17/306/289 +f 144/305/288 17/306/289 145/307/290 +f 145/307/290 17/306/289 16/308/291 +f 145/307/290 16/308/291 146/309/292 +f 146/309/292 16/308/291 15/310/293 +f 146/309/292 15/310/293 147/287/270 +f 147/287/270 15/310/293 14/285/268 +# 237 vertices +# 310 texture params +# 293 normals +# 474 facets + +# 1 groups diff --git a/examples/models/custom/pipe.xml b/examples/models/custom/pipe.xml new file mode 100644 index 00000000..ae5bd882 --- /dev/null +++ b/examples/models/custom/pipe.xml @@ -0,0 +1,9 @@ + + + pipe.obj + 8000 + 0.4 + 0.01 + + + \ No newline at end of file diff --git a/rostok/intexp/__init__.py b/rostok/intexp/__init__.py index f55bad0a..401dd98c 100644 --- a/rostok/intexp/__init__.py +++ b/rostok/intexp/__init__.py @@ -1,4 +1,5 @@ from . import chrono_api from . import poses_generator -from . import testee \ No newline at end of file +from . import entity +from . import utils diff --git a/rostok/intexp/chrono_api.py b/rostok/intexp/chrono_api.py index c44ff070..a65e25fe 100644 --- a/rostok/intexp/chrono_api.py +++ b/rostok/intexp/chrono_api.py @@ -1,145 +1,106 @@ -import pychrono as chrono -from numpy import asarray - -from .testee import TesteeObject, ErrorReport from dataclasses import dataclass +import pychrono as chrono +from .entity import TesteeObject, Crutch +from .utils import o3d_to_chrono_trianglemesh -class ChTesteeObject(TesteeObject): +class ChTesteeObject(TesteeObject): """Class for creation of rigid bodies from a mesh shape for PyChrono Engine. Supported files for loading mesh - .obj, for loading physicals parameters - .xml - Init State: - - empty mesh + Init State: + - empty mesh: o3d.geometry.TriangleMesh() - empty physical parameters - empty poses """ - chrono_body_mesh = None - chrono_material = None - - def __init__(self) -> None: + + def __init__(self, obj_fname:str = "", xml_fname:str = "") -> None: super().__init__() - self.chrono_body_mesh = None - self.chrono_material = None - pass - - def createChronoBodyMeshFromFile(self, obj_fname: str, xml_fname: str): + + if (not obj_fname) and (not xml_fname): + self.chrono_body: chrono.ChBodyEasyMesh = None + self.chrono_material: chrono.ChMaterialSurfaceNSC = None + return + + self.create_chrono_body_from_file(obj_fname, xml_fname) + + def create_chrono_body_from_file(self, obj_fname: str, xml_fname: str): """ Method created ChBodyEasyMesh object from .obj file with physicals parameters from .xml file Args: obj_fname (str): mesh file like name 'body1.obj' or path '../folder1/body1.obj' - xml_fname (str): physical parameters like name 'body1_param.xml' or path '../folder1/body1_param.xml' - - TODO: Error cheking when file imported + xml_fname (str): physical parameters like name 'body1_param.xml' + or path '../folder1/body1_param.xml' """ - error1 = super().loadObject3DMesh(obj_fname) - error2 = super().loadObjectDescription(xml_fname) - if (error1 is ErrorReport.NONE) and (error2 is ErrorReport.NONE): - self.chrono_material = chrono.ChMaterialSurfaceNSC() - self.chrono_material.SetFriction(super().getMuFriction()) - self.chrono_material.SetDampingF(0.001) # GAG, need import from xml config - - self.chrono_body_mesh = chrono.ChBodyEasyMesh(self.__convert03DMeshToChTriangleMeshConnected(), - self.getDensity(), - True, - True, - True, - self.chrono_material) - self.chrono_body_mesh.SetName(super().getName()) - self.chrono_body_mesh.GetVisualShape(0).SetColor(chrono.ChColor(240/255, 100/255, 55/255)) - print('OK') - - return (error1, error1) - - def __convert03DMeshToChTriangleMeshConnected(self) -> chrono.ChTriangleMeshConnected: - """Converting the spatial mesh format from O3D to ChTriangleMeshConnected to create - ChBodyEasyMesh simulation object + super().load_object_mesh(obj_fname) + super().load_object_description(xml_fname) - Returns: - chrono.ChTriangleMeshConnected: The spatial mesh for describing ChBodyEasyMesh - """ - triangles = asarray(self._mesh.triangles) - vertices = asarray(self._mesh.vertices) - ch_mesh = chrono.ChTriangleMeshConnected() - - for item in triangles: - x, y, z = 0, 1, 2 - vert1_index, vert2_index, vert3_index = item[0], item[1], item[2] - ch_mesh.addTriangle(chrono.ChVectorD(vertices[vert1_index][x], vertices[vert1_index][y], vertices[vert1_index][z]), - chrono.ChVectorD(vertices[vert2_index][x], vertices[vert2_index][y], vertices[vert2_index][z]), - chrono.ChVectorD(vertices[vert3_index][x], vertices[vert3_index][y], vertices[vert3_index][z])) - - return ch_mesh - - def getChronoGraspingPosesList(self) -> list[chrono.ChFrameD]: + self.chrono_material = chrono.ChMaterialSurfaceNSC() + self.chrono_material.SetFriction(super().mu_contact) + self.chrono_material.SetDampingF(super().d_contact) + + self.chrono_body = chrono.ChBodyEasyMesh(o3d_to_chrono_trianglemesh(self.mesh), + self.density, + True, + True, + True, + self.chrono_material) + self.chrono_body.SetName(super().obj_file_name) + self.chrono_body.GetVisualShape(0).SetColor(chrono.ChColor(240/255, 100/255, 55/255)) + + def get_chrono_grasping_poses_list(self) -> list[chrono.ChFrameD]: """Returned list of all poses for interaction with testee object. All poses described like position + rotation in local coordinates of object. Returns: list[chrono.ChFrameD]: Vecotor and Quaternion of poses """ - poses = self.getGraspingPosesList() + poses = self.get_grasping_poses_list() print(len(poses)) chrono_poses = [] for i in poses: chrono_poses.append(chrono.ChFrameD(chrono.ChVectorD(i[0][0], i[0][1], i[0][2]), - chrono.ChQuaternionD(i[1][0], i[1][1], i[1][2], i[1][3]))) + chrono.ChQuaternionD(i[1][0], i[1][1], i[1][2], i[1][3]))) return chrono_poses - - def setChronoBodyMeshOnPose(self, pose_to_ref: chrono.ChFrameD, pose_to_abs: chrono.ChFrameD): + + def set_chrono_body_on_pose(self, pose_to_ref: chrono.ChFrameD, pose_to_abs: chrono.ChFrameD): """Positions the object in the capture pose relative to the global coordinate system Args: pose_to_ref (chrono.ChFrameD): gripping pose in local coordinate system pose_to_abs (chrono.ChFrameD): place in global coordinate system """ - cog_to_ref = self.chrono_body_mesh.GetFrame_COG_to_REF() - myX = pose_to_ref.GetInverse() * cog_to_ref - desired_cog_to_abs = pose_to_abs * myX - self.chrono_body_mesh.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) - self.chrono_body_mesh.SetNoSpeedNoAcceleration() - - def setChronoBodyMeshRefFrameInPoint(self, desired_ref_to_abs_point: chrono.ChFrameD): + cog_to_ref = self.chrono_body.GetFrame_COG_to_REF() + temp = pose_to_ref.GetInverse() * cog_to_ref + desired_cog_to_abs = pose_to_abs * temp + self.chrono_body.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) + self.chrono_body.SetNoSpeedNoAcceleration() + + def set_chrono_body_ref_frame_in_point(self, desired_ref_to_abs_point: chrono.ChFrameD): """Positions the object relative local frame to the global coordinate system Args: desired_ref_to_abs_point (chrono.ChFrameD): place in global coordinate system """ - cog_to_ref = self.chrono_body_mesh.GetFrame_COG_to_REF() + cog_to_ref = self.chrono_body.GetFrame_COG_to_REF() desired_cog_to_abs = desired_ref_to_abs_point * cog_to_ref - self.chrono_body_mesh.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) - self.chrono_body_mesh.SetNoSpeedNoAcceleration() - - def addStaticImpactToChronoBodyMesh(self): - pass - - def addSineImpactToChronoBodyMesh(self): - pass - - def applyKickImpactToChronoBodyMesh(self): - pass - - def clearAllExternalForces(self): - self.chrono_body_mesh.RemoveAllForces() - pass - - def updateExternalForcesTimer(self): - pass - + self.chrono_body.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) + self.chrono_body.SetNoSpeedNoAcceleration() + @dataclass class ImpactTimerParameters: test_num: int = 0 # number of actual impact test_bound: int = 0 # number of all impacts clock: float = 0 # actual time clock_bound: float = 0 # time to change of impact - step: float = 0 - -def updateImpact(chtestee: ChTesteeObject, - force: chrono.ChForce, - solver: ImpactTimerParameters, - impacts_point:list, - impacts_dir: list, - impacts_magnitude: list): + step: float = 0 + +def update_impact(chtestee: ChTesteeObject, + force: chrono.ChForce, + solver: ImpactTimerParameters, + impacts_point:list, + impacts_dir: list, + impacts_magnitude: list): """The function of passing through all the impacts at regular intervals Args: @@ -152,21 +113,72 @@ def updateImpact(chtestee: ChTesteeObject, """ if solver.clock > solver.clock_bound: if solver.test_num < solver.test_bound: - chtestee.chrono_body_mesh.RemoveAllForces() - chtestee.setChronoBodyMeshRefFrameInPoint(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), chrono.ChQuaternionD(1,0,0,0))) - chtestee.chrono_body_mesh.SetNoSpeedNoAcceleration() - - chtestee.chrono_body_mesh.AddForce(force) + chtestee.chrono_body.RemoveAllForces() + chtestee.set_chrono_body_ref_frame_in_point(chrono.ChFrameD(chrono.ChVectorD(0,0.2,0), + chrono.ChQuaternionD(1,0,0,0))) + chtestee.chrono_body.SetNoSpeedNoAcceleration() + + chtestee.chrono_body.AddForce(force) force.SetVrelpoint(impacts_point[solver.test_num]) force.SetRelDir(impacts_dir[solver.test_num]) force.SetMforce(impacts_magnitude[solver.test_num]) solver.test_num += 1 else: - solver.test_num = 0 + solver.test_num = 0 solver.clock = 0 + + solver.clock += solver.step + +class ChCrutch(Crutch): + def __init__(self, horn_width=0.05, base_height=0.05, gap=0.01, depth_k=0.05) -> None: + super().__init__(horn_width, base_height, gap, depth_k) + self.chrono_body: chrono.ChBodyEasyMesh = None + self.chrono_material = chrono.ChMaterialSurfaceNSC() + self.chrono_frame_for_object_placing: chrono.ChFrameD = chrono.ChFrameD() + self.__holded_object_place: chrono.ChFrameD = None + + def build_chrono_body(self, chrono_obj: ChTesteeObject, + mu_contact: float = 0.25, d_contact: float = 0.001, + density = 2700, + start_pos: list[float] = [0.0, 0.0, 0.0]): + + super().build_for_testee_object(chrono_obj.mesh) + + self.chrono_material = chrono.ChMaterialSurfaceNSC() + self.chrono_material.SetFriction(mu_contact) + self.chrono_material.SetDampingF(d_contact) + + self.chrono_body = chrono.ChBodyEasyMesh(o3d_to_chrono_trianglemesh(self.mesh), + density, + True, + True, + True, + self.chrono_material) + self.chrono_body.SetPos(chrono.ChVectorD(start_pos[0], + start_pos[1], + start_pos[2])) + self.chrono_body.SetBodyFixed(True) + self.chrono_body.SetName("Crutch") + self.chrono_body.GetVisualShape(0).SetColor(chrono.ChColor(165/255, 165/255, 165/255)) + + self.__holded_object_place = chrono.ChFrameD(chrono.ChVectorD(0, + self.mesh.get_max_bound()[1] * 0.8, + -self.mesh.get_max_bound()[2]), + chrono.ChQuaternionD(1, 0, 0, 0)) + + def set_position(self, desired_ref_to_abs_point: chrono.ChFrameD): + cog_to_ref = self.chrono_body.GetFrame_COG_to_REF() + desired_cog_to_abs = desired_ref_to_abs_point * cog_to_ref + self.chrono_body.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) + self.chrono_body.SetNoSpeedNoAcceleration() - else: - solver.clock += solver.step + @property + def place_for_object(self) -> chrono.ChFrameD: + return self.__holded_object_place + + @place_for_object.setter + def place_for_object(self, new_place: chrono.ChFrameD): + self.__holded_object_place = new_place if __name__ == '__main__': - pass \ No newline at end of file + pass diff --git a/rostok/intexp/entity.py b/rostok/intexp/entity.py new file mode 100644 index 00000000..06dfd09a --- /dev/null +++ b/rostok/intexp/entity.py @@ -0,0 +1,443 @@ +from dataclasses import dataclass +from lxml import etree +import open3d as o3d +import numpy as np + +@dataclass +class PhysicsParameters: + mass: float = 0 + density: float = 0 + mu_contact: float = 0 + d_contact: float = 0.001 + +class TesteeObject(): + """Geometry and physical parameters of the testee object + + Init State: + - empty mesh + - empty physical parameters + - empty poses + """ + + def __init__(self) -> None: + self.__parameters = PhysicsParameters() + self.__grasping_poses_list: list[list[float]] = [] + self.__linked_obj_file: str = "" + self.mesh = o3d.geometry.TriangleMesh() + + def load_object_mesh(self, file_mesh: str): + """Loading a volumetric mesh from a Wavefront OBJ file + + Args: + file_mesh (str): mesh file like name 'body1.obj' or path '../folder1/body1.obj' + + TODO: Checking the integrity of the grid and the possibility of its use + """ + + self.mesh = o3d.io.read_triangle_mesh(filename = file_mesh, + enable_post_processing = False, + print_progress = True) + + def demonstrate_object(self): + """Visualization of the object model through OpenGL + """ + o3d.visualization.draw_geometries(geometry_list = [self.mesh], + window_name = 'Testee Object', + width = 1280, + height = 720, + mesh_show_wireframe = True) + + def demonstrate_object_and_grasping_poses(self): + """Visualization of the object shape and gripping poses through OpenGL. + The location and direction of capture is indicated by an arrow. + + TODO: Change arrow on frame + """ + demo_list = [] + demo_list.append(self.mesh) + for pose in self.__grasping_poses_list: + demo_list.append(self.__create_arrow_sign(pose)) + + demo_list.append(o3d.geometry.TriangleMesh.create_coordinate_frame(size = 0.1)) + o3d.visualization.draw_geometries(geometry_list = demo_list, + window_name = "Testee Object and "+ + f"{self.grasping_poses_number} poses", + width = 1280, + height = 720, + mesh_show_wireframe = True) + + demo_list.clear() + + def __create_arrow_sign(self, pose) -> o3d.geometry.TriangleMesh: + """Creates an o3d.geometry.TriangleMesh() object in the form of an + arrow at the grip position. Arrow aligned with grip direction. + + Args: + pose (list[(x,y,z), (x, y, z, w)]): position and orientation + + Returns: + o3d.geometry.TriangleMesh: Arrow icon + """ + coordinate, orientation = pose + arrow_size = (self.mesh.get_max_bound()[2] - self.mesh.get_min_bound()[2]) / 10 + arrow_mesh = o3d.geometry.TriangleMesh().create_arrow(cylinder_radius=arrow_size*0.1, + cone_radius=arrow_size*0.15, + cylinder_height=arrow_size * 2/3, + cone_height=arrow_size * 1/3, + resolution=20, + cylinder_split=4, + cone_split=1) + arrow_mesh.paint_uniform_color([220/255, 20/255, 60/255]) + arrow_mesh.translate(coordinate) + arrow_mesh.rotate(o3d.geometry.get_rotation_matrix_from_quaternion(orientation)) + return arrow_mesh + + def load_object_description(self, fname: str): + """Loading physical parameters and required grip positions in xml format + + Args: + fname (str): physical parameters like name 'body1_param.xml' or + path '../folder1/body1_param.xml' + + Returns: + error (ErrorReport): zero or error code + """ + try: + description_file = etree.parse(fname) + except OSError: + print("Wrong file name") + raise + + xmlread_error = self.__check_xml_linked_obj_file( description_file.find('obj_file') ) + if xmlread_error: + raise Exception("Not found paramater ") + + xmlread_error = self.__check_xml_weight_param( description_file.find('weight') ) + if xmlread_error: + raise Exception("Not found paramater ") + + xmlread_error = self.__check_xml_mu_contact( description_file.find('mu_contact') ) + if xmlread_error: + raise Exception("Not found paramater ") + + xmlread_error = self.__check_xml_d_contact( description_file.find('d_contact') ) + if xmlread_error: + raise Exception("Not found paramater ") + + xmlread_error = self.__check_xml_grasping_poses( description_file.find('grasping_poses') ) + if xmlread_error: + raise Exception("Not found paramater ") + + def __check_xml_linked_obj_file(self, xmlsubelem_linked_obj_file) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_linked_obj_file (lxmlsubelem): linked .obj file name + + Returns: + error (bool): the presence of an error + """ + if (xmlsubelem_linked_obj_file is not None) and \ + (xmlsubelem_linked_obj_file.text.endswith('.obj')): + self.__linked_obj_file = xmlsubelem_linked_obj_file.text + return False + + return True + + def __check_xml_weight_param(self, xmlsubelem_weight) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_weight (lxmlsubelem): weight paramaeter (density or mass) + + Returns: + error (bool): the presence of an error + """ + if xmlsubelem_weight.get('parameter') == 'mass': + weight_value = float(xmlsubelem_weight.text) + if weight_value > 0: + self.__parameters.mass = weight_value + return False + + return True + + elif xmlsubelem_weight.get('parameter') == 'density': + weight_value = float(xmlsubelem_weight.text) + if weight_value > 0: + self.__parameters.density = weight_value + return False + + return True + + else: + return True + + def __check_xml_mu_contact(self, xmlsubelem_friction) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_friction (lxmlsubelem): friction coefficient for surface + + Returns: + error (bool): the presence of an error + """ + if xmlsubelem_friction is not None: + value = float(xmlsubelem_friction.text) + if (value > 0) or (value <= 1): + self.__parameters.mu_contact = value + return False + + return True + + return True + + def __check_xml_d_contact(self, xmlsubelem_friction) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_friction (lxmlsubelem): damping coefficient for surface + + Returns: + error (bool): the presence of an error + """ + if xmlsubelem_friction is not None: + value = float(xmlsubelem_friction.text) + if (value > 0) or (value <= 1): + self.__parameters.d_contact = value + return False + + return True + + return True + + def __check_xml_grasping_poses(self, xmlsubelem_grasping_poses) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_grasping_poses (lxmlsubelem): list of user's grasping poses (desired) + + Returns: + error (bool): the presence of an error + """ + if xmlsubelem_grasping_poses is not None: + for xmlsubelem_pose in xmlsubelem_grasping_poses.getchildren(): + coordinates = xmlsubelem_pose.get('coordinates') + coordinates = coordinates.replace('[', '') + coordinates = coordinates.replace(']', '') + coordinates = [float(x) for x in coordinates.split(',')] + if len(coordinates) != 3: + return True + + orientation = xmlsubelem_pose.get('orientation') + orientation = orientation.replace('[', '') + orientation = orientation.replace(']', '') + orientation = [float(x) for x in orientation.split(',')] + if len(orientation) != 4: + return True + + self.__grasping_poses_list.append([coordinates, orientation]) + else: + return True + + return False + + @property + def grasping_poses_number(self) -> int: + """Property for getting number of poses + + Returns: + int: number of poses + """ + return len(self.__grasping_poses_list) + + def get_grasping_pose(self, index: int) -> list: + """Property for getting pose with index + + Args: + index (int): pose with index + + Returns: + [(x,y,z), (x, y, z, w)]: pose as position and rotation + """ + return self.__grasping_poses_list[index] + + def get_grasping_poses_list(self) -> list: + """Property for getting all poses + + Returns: + list[(x,y,z), (x, y, z, w)]: poses as position and rotation + """ + return self.__grasping_poses_list + + def add_grasping_pose(self, coord, orient): + """Add new pose in poses list + + Args: + coord (list[(x,y,z)]): place of pose in local coordinate system + orient (list[(x,y,z,w)]): orientation of gripper in local coordinate system + """ + self.__grasping_poses_list.append([coord, orient]) + + def add_grasping_poses_list(self, new_poses): + """Add new poses in poses list + + Args: + new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper + """ + self.__grasping_poses_list.extend(new_poses) + + def clear_grasping_poses_list(self): + """Clear list of poses. + """ + self.__grasping_poses_list.clear() + + def rewrite_grasping_poses_list(self, new_poses): + """Replaces an existing list of capture poses with a new one. + + Args: + new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper + """ + self.__grasping_poses_list.clear() + self.__grasping_poses_list.extend(new_poses) + + @property + def density(self) -> float: + """Property of density + + Returns: + float: kg/m3 + """ + if self.__parameters.density: + return self.__parameters.density + + return self.__parameters.mass / self.mesh.get_volume() + + @density.setter + def density(self, density_val: float): + """Setter of density + + Args: + density_val (float): new value in kg/m3 + """ + if density_val <= 0: + raise Exception("Wrong density value") + + self.__parameters.density = density_val + + @property + def mu_contact(self) -> float: + """Property of surface friction coefficient + + Returns: + float: from 0 to 1 + """ + if self.__parameters.mu_contact: + return self.__parameters.mu_contact + + raise Exception("Mu friction not setup") + + @mu_contact.setter + def mu_contact(self, mu_val: float): + """Setter of surface friction coefficient + + Args: + mu_val (float): from 0 to 1 + """ + if mu_val > 0 and mu_val <= 1.0: + self.__parameters.mu_contact = mu_val + return + + raise Exception("Wrong mu friction value") + + @property + def d_contact(self) -> float: + """Property of surface damping coefficient + + Returns: + float: from 0 to 1 + """ + if self.__parameters.d_contact: + return self.__parameters.d_contact + + raise Exception("Damping contact not setup") + + @d_contact.setter + def d_contact(self, d_val: float): + """Setter of surface friction coefficient + + Args: + d_val (float): from 0 to 1 + """ + if d_val > 0 and d_val <= 1.0: + self.__parameters.d_contact = d_val + return + + raise Exception("Wrong damping contact value") + + @property + def obj_file_name(self) -> str: + """Property of linked file name + + Returns: + str: name of testee object + """ + return self.__linked_obj_file + + @property + def bound_box(self) -> list[float]: + total_bound_box = np.absolute(self.mesh.get_max_bound()) + \ + np.absolute(self.mesh.get_min_bound()) + return total_bound_box + +class Crutch(): + def __init__(self, horn_width = 0.05, base_height = 0.05, gap = 0.01, depth_k: float = 0.05): + if (horn_width > 0) and (base_height > 0) and (gap > 0) and (depth_k > 0): + self.mesh = o3d.geometry.TriangleMesh() + self.__horn_width = horn_width + self.__base_height = base_height + self.__gap = gap + self.__depth_k = depth_k + else: + raise Exception("Invalid argument value. Arguments must be positive.") + + def build_for_testee_object(self, obj: o3d.geometry.TriangleMesh): + if obj.is_empty(): + raise Exception("Unable not build Crutch for TesteeObject without mesh") + + bound_box = self.__calc_summary_bound_box(obj) + crutch_depth = bound_box[2] * self.__depth_k + left_horn = o3d.geometry.TriangleMesh().create_box(width = self.__horn_width, + height = bound_box[1]*0.5, + depth = crutch_depth) + right_horn = o3d.geometry.TriangleMesh().create_box(width = self.__horn_width, + height = bound_box[1]*0.5, + depth = crutch_depth) + base = o3d.geometry.TriangleMesh().create_box(width = bound_box[1] + + self.__gap*2 + self.__horn_width*2, + height = self.__base_height, + depth = crutch_depth) + bottom = o3d.geometry.TriangleMesh().create_box(width = base.get_max_bound()[0], + height = 0.01, + depth = bound_box[2]) + + left_horn.translate([0, self.__base_height, 0]) + right_horn.translate([base.get_max_bound()[0]-self.__horn_width, + self.__base_height, + 0]) + self.mesh = base + left_horn + right_horn + bottom + + left_horn.translate([0, 0, bound_box[2]-crutch_depth]) + right_horn.translate([0, 0, bound_box[2]-crutch_depth]) + base.translate([0, 0, bound_box[2]-crutch_depth]) + self.mesh += base + left_horn + right_horn + + self.mesh.translate([-self.mesh.get_max_bound()[0]/2, + 0, + -self.mesh.get_max_bound()[2]/2]) + + def __calc_summary_bound_box(self, mesh) -> list[float]: + return np.absolute(mesh.get_max_bound()) + \ + np.absolute(mesh.get_min_bound()) + +if __name__ == '__main__': + pass diff --git a/rostok/intexp/poses_generator.py b/rostok/intexp/poses_generator.py index 1fadf135..ecd02c99 100644 --- a/rostok/intexp/poses_generator.py +++ b/rostok/intexp/poses_generator.py @@ -1,17 +1,18 @@ from random import uniform from math import pi, sin, cos +import xml.etree.ElementTree from scipy.spatial.transform import Rotation import numpy as np -import xml.etree.ElementTree -from .testee import TesteeObject - -def genRandomPosesAroundLine(num_pose_on_layer: int, - min_dist: float, - max_dist: float, - step: float, - length: float): - """Generates poses with random angular displacement and random distances on predetermined range. - Generation is performed along a straight line with a fixed step. +from .entity import TesteeObject + +def gen_random_poses_around_line(num_pose_on_layer: int, + min_dist: float, + max_dist: float, + step: float, + length: float): + """Generates poses with random angular displacement and random distances + on predetermined range. Generation is performed along a straight line with + a fixed step. Args: num_pose_on_layer (int): number of poses per layer @@ -22,31 +23,32 @@ def genRandomPosesAroundLine(num_pose_on_layer: int, Returns: list[(x,y,z), (x, y, z, w)]: poses as positions and rotations - """ + """ poses = [] - gape = max_dist - min_dist - + gap = max_dist - min_dist + for i in range(round(length/step)): - for j in range(num_pose_on_layer): + for _ in range(num_pose_on_layer): phi = uniform(0, 2*pi) - ro = gape * uniform(0, 1) + min_dist - - coord = [ro*sin(phi), ro*cos(phi), i*step] + des_dist = gap * uniform(0, 1) + min_dist + + coord = [des_dist*sin(phi), des_dist*cos(phi), i*step] rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) orient = rot.as_quat() - + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - + return poses -def genRandomPosesAroundTesteeObjectAxis(testee_obj: TesteeObject, - num_pose_on_layer: int, - max_dist: float, - step: float, - axis = 'z'): - """Generates poses with random angular displacement and random distances on predetermined range. - Generation occurs around the selected axis of the object, taking into account its geometric dimensions. - +def gen_random_poses_around_object_axis(testee_obj: TesteeObject, + num_pose_on_layer: int, + max_dist: float, + step: float, + axis = 'z'): + """Generates poses with random angular displacement and random distances + on predetermined range. Generation occurs around the selected axis of the object, + taking into account its geometric dimensions. + Args: testee_obj (TesteeObject): _description_ num_pose_on_layer (int): number of poses per layer @@ -57,59 +59,67 @@ def genRandomPosesAroundTesteeObjectAxis(testee_obj: TesteeObject, Returns: list[(x,y,z), (x, y, z, w)]: poses as positions and rotations """ - + poses = [] - size = {'x': testee_obj._mesh.get_max_bound()[0] - testee_obj._mesh.get_min_bound()[0], - 'y': testee_obj._mesh.get_max_bound()[1] - testee_obj._mesh.get_min_bound()[1], - 'z': testee_obj._mesh.get_max_bound()[2] - testee_obj._mesh.get_min_bound()[2]} - + size = {'x': testee_obj.mesh.get_max_bound()[0] - testee_obj.mesh.get_min_bound()[0], + 'y': testee_obj.mesh.get_max_bound()[1] - testee_obj.mesh.get_min_bound()[1], + 'z': testee_obj.mesh.get_max_bound()[2] - testee_obj.mesh.get_min_bound()[2]} + if axis == 'x': min_dist = (size['y']+size['z'])/4 * 1.2 - for item in np.arange(testee_obj._mesh.get_min_bound()[0], testee_obj._mesh.get_max_bound()[0], step): - for j in range(num_pose_on_layer): + for item in np.arange(testee_obj.mesh.get_min_bound()[0], + testee_obj.mesh.get_max_bound()[0], + step): + + for _ in range(num_pose_on_layer): phi = uniform(0, 2*pi) - ro = max_dist * uniform(0, 1) + min_dist - coord = [item, - ro*sin(phi) + testee_obj._mesh.get_center()[1], - ro*cos(phi) + testee_obj._mesh.get_center()[2]] + des_dist = max_dist * uniform(0, 1) + min_dist + coord = [item, + des_dist*sin(phi) + testee_obj.mesh.get_center()[1], + des_dist*cos(phi) + testee_obj.mesh.get_center()[2]] rot = Rotation.from_euler('xyz', [pi/2, -phi, pi]) orient = rot.as_quat() - poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + elif axis == 'y': min_dist = (size['x']+size['z'])/4 * 1.2 - for item in np.arange(testee_obj._mesh.get_min_bound()[1], testee_obj._mesh.get_max_bound()[1], step): - for j in range(num_pose_on_layer): + for item in np.arange(testee_obj.mesh.get_min_bound()[1], + testee_obj.mesh.get_max_bound()[1], + step): + for _ in range(num_pose_on_layer): phi = uniform(0, 2*pi) - ro = max_dist * uniform(0, 1) + min_dist - coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + des_dist = max_dist * uniform(0, 1) + min_dist + coord = [des_dist*sin(phi) + testee_obj.mesh.get_center()[0], item, - ro*cos(phi) + testee_obj._mesh.get_center()[2]] + des_dist*cos(phi) + testee_obj.mesh.get_center()[2]] rot = Rotation.from_euler('xyz', [0, phi, pi]) orient = rot.as_quat() poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - - elif axis == 'z': + + elif axis == 'z': min_dist = (size['x']+size['y'])/4 * 1.2 - for item in np.arange(testee_obj._mesh.get_min_bound()[2], testee_obj._mesh.get_max_bound()[2], step): - for j in range(num_pose_on_layer): + for item in np.arange(testee_obj.mesh.get_min_bound()[2], + testee_obj.mesh.get_max_bound()[2], + step): + for _ in range(num_pose_on_layer): phi = uniform(0, 2*pi) - ro = max_dist * uniform(0, 1) + min_dist - coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], - ro*cos(phi) + testee_obj._mesh.get_center()[1], + des_dist = max_dist * uniform(0, 1) + min_dist + coord = [des_dist*sin(phi) + testee_obj.mesh.get_center()[0], + des_dist*cos(phi) + testee_obj.mesh.get_center()[1], item] rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) orient = rot.as_quat() - poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) else: - return None - + raise Exception("The axis of the body is incorrect. \ + You only need to use \'x\', \'y\', \'z\'") + return poses -def genCylindricalSurfacePoses(num_pose_on_layer: int, - dist: float, - step: float, - length: float): +def gen_cylindrical_surface_from_poses(num_pose_on_layer: int, + dist: float, + step: float, + length: float): """Generates poses with discrete angular displacement on predetermined distance. Generation is performed along a straight line with a fixed step. @@ -124,7 +134,7 @@ def genCylindricalSurfacePoses(num_pose_on_layer: int, """ poses = [] dphi = 2*pi / num_pose_on_layer - + for i in range(round(length/step)): for j in range(num_pose_on_layer): phi = dphi*j @@ -132,17 +142,18 @@ def genCylindricalSurfacePoses(num_pose_on_layer: int, rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) orient = rot.as_quat() poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - + return poses -def genCylindricalSurfaceAroundTesteeObjectAxis(testee_obj: TesteeObject, - num_pose_on_layer: int, - dist: float, - step: float, - axis = 'z'): +def gen_cylindrical_surface_around_object_axis(testee_obj: TesteeObject, + num_pose_on_layer: int, + dist: float, + step: float, + axis = 'z'): """Generates poses with discrete angular displacement on predetermined distance. - Generation occurs around the selected axis of the object, taking into account its geometric dimensions. - + Generation occurs around the selected axis of the object, + taking into account its geometric dimensions. + Args: testee_obj (TesteeObject): _description_ num_pose_on_layer (int): number of poses per layer @@ -153,75 +164,82 @@ def genCylindricalSurfaceAroundTesteeObjectAxis(testee_obj: TesteeObject, Returns: list[(x,y,z), (x, y, z, w)]: poses as positions and rotations """ - + poses = [] dphi = 2*pi / num_pose_on_layer - size = {'x': testee_obj._mesh.get_max_bound()[0] - testee_obj._mesh.get_min_bound()[0], - 'y': testee_obj._mesh.get_max_bound()[1] - testee_obj._mesh.get_min_bound()[1], - 'z': testee_obj._mesh.get_max_bound()[2] - testee_obj._mesh.get_min_bound()[2]} - + size = {'x': testee_obj.mesh.get_max_bound()[0] - testee_obj.mesh.get_min_bound()[0], + 'y': testee_obj.mesh.get_max_bound()[1] - testee_obj.mesh.get_min_bound()[1], + 'z': testee_obj.mesh.get_max_bound()[2] - testee_obj.mesh.get_min_bound()[2]} + if axis == 'x': min_dist = (size['y']+size['z'])/4 * 1.1 - for item in np.arange(testee_obj._mesh.get_min_bound()[0], testee_obj._mesh.get_max_bound()[0], step): + for item in np.arange(testee_obj.mesh.get_min_bound()[0], + testee_obj.mesh.get_max_bound()[0], + step): for j in range(num_pose_on_layer): phi = dphi*j - ro = dist + min_dist - coord = [item, - ro*sin(phi) + testee_obj._mesh.get_center()[1], - ro*cos(phi) + testee_obj._mesh.get_center()[2]] + des_dist = dist + min_dist + coord = [item, + des_dist*sin(phi) + testee_obj.mesh.get_center()[1], + des_dist*cos(phi) + testee_obj.mesh.get_center()[2]] rot = Rotation.from_euler('xyz', [pi/2, -phi, pi]) orient = rot.as_quat() - poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + elif axis == 'y': min_dist = (size['x']+size['z'])/4 * 1.1 - for item in np.arange(testee_obj._mesh.get_min_bound()[1], testee_obj._mesh.get_max_bound()[1], step): + for item in np.arange(testee_obj.mesh.get_min_bound()[1], + testee_obj.mesh.get_max_bound()[1], + step): for j in range(num_pose_on_layer): phi = dphi*j - ro = dist + min_dist - coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], + des_dist = dist + min_dist + coord = [des_dist*sin(phi) + testee_obj.mesh.get_center()[0], item, - ro*cos(phi) + testee_obj._mesh.get_center()[2]] + des_dist*cos(phi) + testee_obj.mesh.get_center()[2]] rot = Rotation.from_euler('xyz', [0, phi, pi]) orient = rot.as_quat() - poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) - + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + elif axis == 'z': min_dist = (size['x']+size['y'])/4 * 1.1 - for item in np.arange(testee_obj._mesh.get_min_bound()[2], testee_obj._mesh.get_max_bound()[2], step): + for item in np.arange(testee_obj.mesh.get_min_bound()[2], + testee_obj.mesh.get_max_bound()[2], + step): for j in range(num_pose_on_layer): phi = dphi*j - ro = dist - coord = [ro*sin(phi) + testee_obj._mesh.get_center()[0], - ro*cos(phi) + testee_obj._mesh.get_center()[1], + des_dist = dist + coord = [des_dist*sin(phi) + testee_obj.mesh.get_center()[0], + des_dist*cos(phi) + testee_obj.mesh.get_center()[1], item] rot = Rotation.from_euler('xyz', [phi, 0, -pi/2]) orient = rot.as_quat() - poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) + poses.append([coord, [orient[0], orient[1], orient[2], orient[3]]]) else: - return None - + raise Exception("The axis of the body is incorrect. \ + You only need to use \'x\', \'y\', \'z\'") + return poses -def rewritePosesOnXMLFile(poses: list, xml_file_name: str) -> None: +def rewrite_poses_in_xmlconfig(poses: list, xml_file_name: str): ''' Deletes all poses in the file and overwrites the passed sheet ''' - doc = xml.etree.ElementTree.parse(xml_file_name) - doc.find('grasping_poses').clear() + config_file = xml.etree.ElementTree.parse(xml_file_name) + config_file.find('grasping_poses').clear() new_poses = [] - + index = 0 for item in poses: - new_pos = xml.etree.ElementTree.SubElement(doc.find('grasping_poses'), - 'pose', - index = str(index), - coordinates = str(item[0]), - orientation = str(item[1])) + new_pos = xml.etree.ElementTree.SubElement(config_file.find('grasping_poses'), + 'pose', + index = str(index), + coordinates = str(item[0]), + orientation = str(item[1])) new_poses.append(new_pos) index += 1 - - doc.write(xml_file_name, encoding="utf-8", xml_declaration=True) + + config_file.write(xml_file_name, encoding="utf-8", xml_declaration=True) if __name__ == '__main__': - pass \ No newline at end of file + pass diff --git a/rostok/intexp/testee.py b/rostok/intexp/testee.py deleted file mode 100644 index f7ec66e9..00000000 --- a/rostok/intexp/testee.py +++ /dev/null @@ -1,371 +0,0 @@ -from lxml import etree -import open3d as o3d - -from enum import Enum -from dataclasses import dataclass - -@dataclass -class PhysicsParameters: - mass: float = None - density: float = None - mu_friction: float = None - -class ErrorReport(Enum): - NONE = 0 - WRONG_FILE_NAME = 1 - WRONG_LINKED_FILE_DESCRIPTION = 2 - WRONG_WEIGHT_PARAMETER_DESCRIPTION = 3 - WRONG_FRICTION_DESCRIPTION = 4 - GRASPING_POSES_MISSING = 5 - WRONG_XML_STRUCTURE = 6 - WRONG_WEIGHT_VALUE = 7 - WRONG_FRICTION_VALUE = 8 - WRONG_GRASPING_POSES_DESCRIPTION = 9 - WRONG_POSE_COORDINATE_DESCRIPTION = 10 - WRONG_POSE_ORIENTATION_DESCRIPTION = 11 - REQUIRED_OBJ_MESH_FORMAT = 12 - BROKEN_3D_MESH = 13 - MESH_NOT_FOUND = 14 - WEIGHT_NOT_DEFINED = 15 - -class TesteeObject(object): - """Geometry and physical parameters of the testee object - - Init State: - - empty mesh - - empty physical parameters - - empty poses - - TODO: Added @property and @setter - """ - - def __init__(self) -> None: - self.__parameters = PhysicsParameters() - self.__grasping_poses_list = [] - self.__linked_obj_file = None - self._mesh = o3d.geometry.TriangleMesh() - - def loadObject3DMesh(self, file_mesh: str) -> ErrorReport: - """Loading a volumetric mesh from a Wavefront OBJ file - - Args: - file_mesh (str): mesh file like name 'body1.obj' or path '../folder1/body1.obj' - - Returns: - error (ErrorReport): zero or error code - - TODO: Checking the integrity of the grid and the possibility of its use - """ - - mesh = o3d.io.read_triangle_mesh(filename=file_mesh, - enable_post_processing=False, - print_progress=True) - - self._mesh = mesh - return ErrorReport.NONE - - def getMeshVertices(self): - """Vertex coordinates - - Returns: - float64 array of shape (num_vertices, 3), use numpy.asarray() to access data - """ - return self._mesh.vertices - - def showObject3DModel(self): - """Visualization of the object model through OpenGL - """ - mesh_list = [self._mesh] - o3d.visualization.draw_geometries(geometry_list = mesh_list, - window_name = 'Testee Object', - width = 1280, - height = 720, - mesh_show_wireframe = True) - - def showObjectWithGraspingPoses(self): - """Visualization of the object shape and gripping poses through OpenGL. - The location and direction of capture is indicated by an arrow. - - TODO: Change arrow on frame - """ - demo_list = [] - demo_list.append(self._mesh) - - for pose in self.__grasping_poses_list: - demo_list.append(self.__createArrowSignGraspingPose(pose)) - - demo_list.append(o3d.geometry.TriangleMesh.create_coordinate_frame(size = 0.1)) - o3d.visualization.draw_geometries(geometry_list = demo_list, - window_name = 'Testee Object and Poses', - width = 1280, - height = 720, - mesh_show_wireframe = True) - - demo_list.clear() - - def __createArrowSignGraspingPose(self, pose) -> o3d.geometry.TriangleMesh: - """Creates an o3d.geometry.TriangleMesh() object in the form of an arrow at the grip position. - Arrow aligned with grip direction. - - Args: - pose (list[(x,y,z), (x, y, z, w)]): position and orientation - - Returns: - o3d.geometry.TriangleMesh: Arrow icon - """ - coordinate, orientation = pose - arrow_size = (self._mesh.get_max_bound()[2] - self._mesh.get_min_bound()[2]) / 10 - arrow_mesh = o3d.geometry.TriangleMesh().create_arrow(cylinder_radius=arrow_size*0.1, - cone_radius=arrow_size*0.15, - cylinder_height=arrow_size * 2/3, - cone_height=arrow_size * 1/3, - resolution=20, - cylinder_split=4, - cone_split=1) - arrow_mesh.paint_uniform_color([220/255, 20/255, 60/255]) - arrow_mesh.translate(coordinate) - arrow_mesh.rotate(o3d.geometry.get_rotation_matrix_from_quaternion(orientation)) - return arrow_mesh - - def loadObjectDescription(self, fname: str) -> ErrorReport: - """Loading physical parameters and required grip positions in xml format - - Args: - fname (str): physical parameters like name 'body1_param.xml' or path '../folder1/body1_param.xml' - - Returns: - error (ErrorReport): zero or error code - """ - try: - description_file = etree.parse(fname) - - xmlread_error = self.__checkXMLLinkedObjFile( description_file.find('obj_file') ) - if xmlread_error.value: return xmlread_error - - xmlread_error = self.__checkXMLWeightParam( description_file.find('weight') ) - if xmlread_error.value: return xmlread_error - - xmlread_error = self.__checkXMLMuFriction( description_file.find('mu_friction') ) - if xmlread_error.value: return xmlread_error - - xmlread_error = self.__checkXMLGraspingPoses( description_file.find('grasping_poses') ) - if xmlread_error.value: return xmlread_error - - return ErrorReport.NONE - - except OSError: - return ErrorReport.WRONG_FILE_NAME - except: - return ErrorReport.WRONG_XML_STRUCTURE - - def __checkXMLLinkedObjFile(self, xmlsubelem_linked_obj_file) -> ErrorReport: - """Checking the correctness of loaded parameters - - Args: - xmlsubelem_linked_obj_file (lxmlsubelem): linked .obj file name - - Returns: - error (ErrorReport): zero or error code - """ - if (xmlsubelem_linked_obj_file is not None) and (xmlsubelem_linked_obj_file.text.endswith('.obj')): - self.__linked_obj_file = xmlsubelem_linked_obj_file.text - return ErrorReport.NONE - else: - return ErrorReport.WRONG_LINKED_FILE_DESCRIPTION - - def __checkXMLWeightParam(self, xmlsubelem_weight) -> ErrorReport: - """Checking the correctness of loaded parameters - - Args: - xmlsubelem_weight (lxmlsubelem): weight paramaeter (density or mass) - - Returns: - error (ErrorReport): zero or error code - """ - if(xmlsubelem_weight.get('parameter') == 'mass'): - weight_value = float(xmlsubelem_weight.text) - if weight_value > 0: - self.__parameters.mass = weight_value - return ErrorReport.NONE - else: - return ErrorReport.WRONG_WEIGHT_VALUE - - elif(xmlsubelem_weight.get('parameter') == 'density'): - weight_value = float(xmlsubelem_weight.text) - if weight_value > 0: - self.__parameters.density = weight_value - return ErrorReport.NONE - else: - return ErrorReport.WRONG_WEIGHT_VALUE - - else: - return ErrorReport.WRONG_WEIGHT_PARAMETER_DESCRIPTION - - def __checkXMLMuFriction(self, xmlsubelem_friction) -> ErrorReport: - """Checking the correctness of loaded parameters - - Args: - xmlsubelem_friction (lxmlsubelem): friction coefficient for surface - - Returns: - error (ErrorReport): zero or error code - """ - if (xmlsubelem_friction is not None): - value = float(xmlsubelem_friction.text) - if (value > 0) or (value <= 1): - self.__parameters.mu_friction = value - return ErrorReport.NONE - else: - return ErrorReport.WRONG_FRICTION_VALUE - else: - return ErrorReport.WRONG_FRICTION_DESCRIPTION - - def __checkXMLGraspingPoses(self, xmlsubelem_grasping_poses) -> ErrorReport: - """Checking the correctness of loaded parameters - - Args: - xmlsubelem_grasping_poses (lxmlsubelem): list of user's grasping poses (desired) - - Returns: - error (ErrorReport): zero or error code - """ - if xmlsubelem_grasping_poses is not None: - for xmlsubelem_pose in xmlsubelem_grasping_poses.getchildren(): - coordinates = xmlsubelem_pose.get('coordinates') - coordinates = coordinates.replace('[', '') - coordinates = coordinates.replace(']', '') - coordinates = [float(x) for x in coordinates.split(',')] - if len(coordinates) != 3: return ErrorReport.WRONG_POSE_COORDINATE_DESCRIPTION - - orientation = xmlsubelem_pose.get('orientation') - orientation = orientation.replace('[', '') - orientation = orientation.replace(']', '') - orientation = [float(x) for x in orientation.split(',')] - if len(orientation) != 4: return ErrorReport.WRONG_POSE_ORIENTATION_DESCRIPTION - - self.__grasping_poses_list.append([coordinates, orientation]) - else: - return ErrorReport.GRASPING_POSES_MISSING - - return ErrorReport.NONE - - def __checkIntersectionGraspingPosesAndMesh(self) -> ErrorReport: - """ !!!_summary_!!! - - Returns: - error (ErrorReport): zero or error code - - TODO: Create check method for detection instersection btw grasping points - adnd mesh. If grasping pose was inside -> remove pos from poses_list - """ - - pass - - def getGraspingPosesNumber(self) -> int: - """Property for getting number of poses - - Returns: - int: number of poses - """ - return len(self.__grasping_poses_list) - - def getGraspingPose(self, index: int) -> list: - """Property for getting pose with index - - Args: - index (int): pose with index - - Returns: - [(x,y,z), (x, y, z, w)]: pose as position and rotation - """ - return self.__grasping_poses_list[index] - - def getGraspingPosesList(self) -> list: - """Property for getting all poses - - Returns: - list[(x,y,z), (x, y, z, w)]: poses as position and rotation - """ - return self.__grasping_poses_list - - def addGraspingPose(self, coord, orient): - """Add new pose in poses list - - Args: - coord (list[(x,y,z)]): place of pose in local coordinate system - orient (list[(x,y,z,w)]): orientation of gripper in local coordinate system - """ - self.__grasping_poses_list.append([coord, orient]) - - def addGraspingPosesList(self, new_poses): - """Add new poses in poses list - - Args: - new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper - """ - self.__grasping_poses_list.extend(new_poses) - - def clearGraspingPosesList(self): - """Clear list of poses. - """ - self.__grasping_poses_list.clear() - - def setGraspingPosesList(self, new_poses): - """Replaces an existing list of capture poses with a new one. - - Args: - new_poses list[(x,y,z), (x, y, z, w)]: new poses of gripper - """ - self.__grasping_poses_list.clear() - self.__grasping_poses_list.extend(new_poses) - - def setDensity(self, density_val: float): - """Setter of density - - Args: - density_val (float): new value in kg/m3 - """ - self.__parameters.density = density_val - - def getDensity(self) -> float: - """Property of density - - Returns: - float: kg/m3 - """ - if self.__parameters.density: - return self.__parameters.density - elif self.__parameters.mass and self._mesh: - return self.__parameters.mass / self._mesh.get_volume() - else: - return None - - def setMuFriction(self, mu_val: float): - """Setter of surface friction coefficient - - Args: - mu_val (float): from 0 to 1 - """ - if mu_val > 0 and mu_val <= 1: self.__parameters.mu_friction = mu_val - - def getMuFriction(self) -> float: - """Property of surface friction coefficient - - Returns: - float: from 0 to 1 or None if not defined - """ - if self.__parameters.mu_friction: - return self.__parameters.mu_friction - else: - return None - - def getName(self): - """Property of linked file name - - Returns: - str: name of testee object - """ - return self.__linked_obj_file - -if __name__ == '__main__': - pass \ No newline at end of file diff --git a/rostok/intexp/utils.py b/rostok/intexp/utils.py new file mode 100644 index 00000000..896174ba --- /dev/null +++ b/rostok/intexp/utils.py @@ -0,0 +1,27 @@ +from pychrono import ChTriangleMeshConnected, ChVectorD +import open3d as o3d +from numpy import asarray + +def o3d_to_chrono_trianglemesh(mesh: o3d.geometry.TriangleMesh) -> ChTriangleMeshConnected: + """Converting the spatial mesh format from O3D to ChTriangleMeshConnected to create + ChBodyEasyMesh simulation object + + Returns: + chrono.ChTriangleMeshConnected: The spatial mesh for describing ChBodyEasyMesh + """ + triangles = asarray(mesh.triangles) + vertices = asarray(mesh.vertices) + ch_mesh = ChTriangleMeshConnected() + + for item in triangles: + vert1_index, vert2_index, vert3_index = item[0], item[1], item[2] + ch_mesh.addTriangle(ChVectorD(vertices[vert1_index][0], + vertices[vert1_index][1], + vertices[vert1_index][2]), + ChVectorD(vertices[vert2_index][0], + vertices[vert2_index][1], + vertices[vert2_index][2]), + ChVectorD(vertices[vert3_index][0], + vertices[vert3_index][1], + vertices[vert3_index][2])) + return ch_mesh From 859941707423012860b67b24e87217803cf050e6 Mon Sep 17 00:00:00 2001 From: Huowl Date: Wed, 28 Dec 2022 19:11:20 +0300 Subject: [PATCH 5/6] Remove unuses imports --- examples/intexp_crutch_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/intexp_crutch_render.py b/examples/intexp_crutch_render.py index 850ffcaf..f6e573fe 100644 --- a/examples/intexp_crutch_render.py +++ b/examples/intexp_crutch_render.py @@ -1,5 +1,4 @@ import open3d as o3d -import trimesh as tm import pychrono as chrono import pychrono.irrlicht as irr from numpy import asarray From afc806ac9f402ccb6724e843aa44f1aa9855686a Mon Sep 17 00:00:00 2001 From: Ivolga Dmitriy Date: Wed, 28 Dec 2022 19:54:01 +0300 Subject: [PATCH 6/6] Rebuild Entity for SMC --- examples/intexp_crutch_render.py | 4 +- ...intexp_external_forces_on_testee_object.py | 6 +- .../intexp_load_and_show_testee_object.py | 4 +- examples/models/custom/body1.xml | 5 +- examples/models/custom/pipe.xml | 5 +- rostok/intexp/chrono_api.py | 22 +++--- rostok/intexp/entity.py | 77 +++++++++++++++---- 7 files changed, 89 insertions(+), 34 deletions(-) diff --git a/examples/intexp_crutch_render.py b/examples/intexp_crutch_render.py index 850ffcaf..2517407d 100644 --- a/examples/intexp_crutch_render.py +++ b/examples/intexp_crutch_render.py @@ -6,7 +6,7 @@ from rostok.intexp.chrono_api import ChTesteeObject, ChCrutch ''' Floor added for clarity ''' -floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceNSC()) +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceSMC()) floor.SetPos(chrono.ChVectorD(0,-0.1,0)) floor.SetBodyFixed(True) floor.SetName('Floor') @@ -25,7 +25,7 @@ obj_db.set_chrono_body_ref_frame_in_point(holder.place_for_object) -system = chrono.ChSystemNSC() +system = chrono.ChSystemSMC() system.Set_G_acc(chrono.ChVectorD(0,-9.8,0)) system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation system.Add(floor) diff --git a/examples/intexp_external_forces_on_testee_object.py b/examples/intexp_external_forces_on_testee_object.py index 68345eb2..9f5b3e15 100644 --- a/examples/intexp_external_forces_on_testee_object.py +++ b/examples/intexp_external_forces_on_testee_object.py @@ -30,18 +30,18 @@ # Switch timer of impact every 2 sec tracking_timer = intexp.chrono_api.ImpactTimerParameters(test_bound=len(IMPACTS_DIR), - clock_bound=2.0, step = 1e-3) + clock_bound=0.5, step = 1e-3) ''' Floor added for clarity ''' -floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceNSC()) +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, True, chrono.ChMaterialSurfaceSMC()) floor.SetPos(chrono.ChVectorD(0,-0.005,0)) floor.SetBodyFixed(True) floor.SetName('Floor') floor.GetVisualShape(0).SetColor(chrono.ChColor(80/255, 80/255, 80/255)) ''' Simulation Solver ''' -system = chrono.ChSystemNSC() +system = chrono.ChSystemSMC() system.Set_G_acc(chrono.ChVectorD(0,0,0)) system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation system.Add(floor) diff --git a/examples/intexp_load_and_show_testee_object.py b/examples/intexp_load_and_show_testee_object.py index f86557f1..b620540c 100644 --- a/examples/intexp_load_and_show_testee_object.py +++ b/examples/intexp_load_and_show_testee_object.py @@ -27,14 +27,14 @@ obj_db.set_chrono_body_on_pose(desired_poses[0], hand_power_grasp_frame) # Position object for grasp pose into point ''' Floor added for clarity ''' -floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, False, chrono.ChMaterialSurfaceNSC()) +floor = chrono.ChBodyEasyBox(1,0.005,1, 1000, True, False, chrono.ChMaterialSurfaceSMC()) floor.SetPos(chrono.ChVectorD(0,-0.005,0)) floor.SetBodyFixed(True) floor.SetName('Floor') floor.GetVisualShape(0).SetColor(chrono.ChColor(80/255, 80/255, 80/255)) ''' Simulation Solver ''' -system = chrono.ChSystemNSC() +system = chrono.ChSystemSMC() system.Set_G_acc(chrono.ChVectorD(0,0,0)) system.Add(obj_db.chrono_body) # Chrono Testee Object added to simulation system.Add(floor) diff --git a/examples/models/custom/body1.xml b/examples/models/custom/body1.xml index aee5c77e..6acc2e7f 100644 --- a/examples/models/custom/body1.xml +++ b/examples/models/custom/body1.xml @@ -2,7 +2,8 @@ body1.obj 1000 - 0.25 - 0.001 + 0.8 + 0.0002 + 0.000001 \ No newline at end of file diff --git a/examples/models/custom/pipe.xml b/examples/models/custom/pipe.xml index ae5bd882..b9df6b31 100644 --- a/examples/models/custom/pipe.xml +++ b/examples/models/custom/pipe.xml @@ -2,8 +2,9 @@ pipe.obj 8000 - 0.4 - 0.01 + 0.8 + 0.0002 + 0.000001 \ No newline at end of file diff --git a/rostok/intexp/chrono_api.py b/rostok/intexp/chrono_api.py index a65e25fe..cd74518b 100644 --- a/rostok/intexp/chrono_api.py +++ b/rostok/intexp/chrono_api.py @@ -18,7 +18,7 @@ def __init__(self, obj_fname:str = "", xml_fname:str = "") -> None: if (not obj_fname) and (not xml_fname): self.chrono_body: chrono.ChBodyEasyMesh = None - self.chrono_material: chrono.ChMaterialSurfaceNSC = None + self.chrono_material: chrono.ChMaterialSurfaceSMC = None return self.create_chrono_body_from_file(obj_fname, xml_fname) @@ -35,9 +35,10 @@ def create_chrono_body_from_file(self, obj_fname: str, xml_fname: str): super().load_object_mesh(obj_fname) super().load_object_description(xml_fname) - self.chrono_material = chrono.ChMaterialSurfaceNSC() - self.chrono_material.SetFriction(super().mu_contact) - self.chrono_material.SetDampingF(super().d_contact) + self.chrono_material = chrono.ChMaterialSurfaceSMC() + self.chrono_material.SetFriction(self.mu_contact) + self.chrono_material.SetKn(self.kn_contact) + self.chrono_material.SetGn(self.gn_contact) self.chrono_body = chrono.ChBodyEasyMesh(o3d_to_chrono_trianglemesh(self.mesh), self.density, @@ -138,15 +139,18 @@ def __init__(self, horn_width=0.05, base_height=0.05, gap=0.01, depth_k=0.05) -> self.__holded_object_place: chrono.ChFrameD = None def build_chrono_body(self, chrono_obj: ChTesteeObject, - mu_contact: float = 0.25, d_contact: float = 0.001, + mu_contact: float = 0.8, + kn_contact: float = 2e4, + gn_contact: float = 1e6, density = 2700, start_pos: list[float] = [0.0, 0.0, 0.0]): super().build_for_testee_object(chrono_obj.mesh) - self.chrono_material = chrono.ChMaterialSurfaceNSC() + self.chrono_material = chrono.ChMaterialSurfaceSMC() self.chrono_material.SetFriction(mu_contact) - self.chrono_material.SetDampingF(d_contact) + self.chrono_material.SetKn(kn_contact) + self.chrono_material.SetGn(gn_contact) self.chrono_body = chrono.ChBodyEasyMesh(o3d_to_chrono_trianglemesh(self.mesh), density, @@ -171,11 +175,11 @@ def set_position(self, desired_ref_to_abs_point: chrono.ChFrameD): desired_cog_to_abs = desired_ref_to_abs_point * cog_to_ref self.chrono_body.SetCoord(desired_cog_to_abs.GetPos(), desired_cog_to_abs.GetRot()) self.chrono_body.SetNoSpeedNoAcceleration() - + @property def place_for_object(self) -> chrono.ChFrameD: return self.__holded_object_place - + @place_for_object.setter def place_for_object(self, new_place: chrono.ChFrameD): self.__holded_object_place = new_place diff --git a/rostok/intexp/entity.py b/rostok/intexp/entity.py index 06dfd09a..7bba2151 100644 --- a/rostok/intexp/entity.py +++ b/rostok/intexp/entity.py @@ -7,8 +7,9 @@ class PhysicsParameters: mass: float = 0 density: float = 0 - mu_contact: float = 0 - d_contact: float = 0.001 + mu_contact: float = 0.8 + kn_contact: float = 2e4 + gn_contact: float = 1e6 class TesteeObject(): """Geometry and physical parameters of the testee object @@ -119,10 +120,14 @@ def load_object_description(self, fname: str): xmlread_error = self.__check_xml_mu_contact( description_file.find('mu_contact') ) if xmlread_error: raise Exception("Not found paramater ") - - xmlread_error = self.__check_xml_d_contact( description_file.find('d_contact') ) + + xmlread_error = self.__check_xml_kn_contact( description_file.find('kn_contact') ) + if xmlread_error: + raise Exception("Not found paramater ") + + xmlread_error = self.__check_xml_gn_contact( description_file.find('gn_contact') ) if xmlread_error: - raise Exception("Not found paramater ") + raise Exception("Not found paramater ") xmlread_error = self.__check_xml_grasping_poses( description_file.find('grasping_poses') ) if xmlread_error: @@ -190,8 +195,27 @@ def __check_xml_mu_contact(self, xmlsubelem_friction) -> bool: return True return True - - def __check_xml_d_contact(self, xmlsubelem_friction) -> bool: + + def __check_xml_kn_contact(self, xmlsubelem_friction) -> bool: + """Checking the correctness of loaded parameters + + Args: + xmlsubelem_friction (lxmlsubelem): damping coefficient for surface + + Returns: + error (bool): the presence of an error + """ + if xmlsubelem_friction is not None: + value = float(xmlsubelem_friction.text) + if (value > 0) or (value <= 1): + self.__parameters.kn_contact = value + return False + + return True + + return True + + def __check_xml_gn_contact(self, xmlsubelem_friction) -> bool: """Checking the correctness of loaded parameters Args: @@ -203,7 +227,7 @@ def __check_xml_d_contact(self, xmlsubelem_friction) -> bool: if xmlsubelem_friction is not None: value = float(xmlsubelem_friction.text) if (value > 0) or (value <= 1): - self.__parameters.d_contact = value + self.__parameters.gn_contact = value return False return True @@ -350,26 +374,51 @@ def mu_contact(self, mu_val: float): raise Exception("Wrong mu friction value") @property - def d_contact(self) -> float: + def kn_contact(self) -> float: + """Property of surface damping coefficient + + Returns: + float: from 0 to 1 + """ + if self.__parameters.kn_contact: + return self.__parameters.kn_contact + + raise Exception("Damping contact not setup") + + @kn_contact.setter + def kn_contact(self, d_val: float): + """Setter of surface friction coefficient + + Args: + d_val (float): from 0 to 1 + """ + if d_val > 0 and d_val <= 1.0: + self.__parameters.kn_contact = d_val + return + + raise Exception("Wrong damping contact value") + + @property + def gn_contact(self) -> float: """Property of surface damping coefficient Returns: float: from 0 to 1 """ - if self.__parameters.d_contact: - return self.__parameters.d_contact + if self.__parameters.gn_contact: + return self.__parameters.gn_contact raise Exception("Damping contact not setup") - @d_contact.setter - def d_contact(self, d_val: float): + @gn_contact.setter + def gn_contact(self, d_val: float): """Setter of surface friction coefficient Args: d_val (float): from 0 to 1 """ if d_val > 0 and d_val <= 1.0: - self.__parameters.d_contact = d_val + self.__parameters.gn_contact = d_val return raise Exception("Wrong damping contact value")