diff --git a/lecture_04/assignment_03/maxence_grangeot/assignment-03.json b/lecture_04/assignment_03/maxence_grangeot/assignment-03.json new file mode 100644 index 0000000..24e7fbf --- /dev/null +++ b/lecture_04/assignment_03/maxence_grangeot/assignment-03.json @@ -0,0 +1 @@ +[{"dtype": "compas.robots/Configuration", "value": {"joint_values": [2.561262838721709, 0.8451018845125493, -2.128157829177538, 5.995444924740527, 4.712388980581042, -5.292718795466213], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [2.2125640907869433, 0.8942197482784214, -2.3657481437474894, 6.183917375542697, 4.712388980617422, -5.641417543400978], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [-0.2970980335982931, -3.7990612214450974, 1.5888955622429972, -5.643815971684596, 4.712388980430257, -1.8678943604431635], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [-0.6726973850771518, -3.785948528761859, 1.5560738824964655, -5.624106984724417, 4.712388979195383, -2.243493711922022], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [-1.051392668584917, -3.705690100785498, 1.3609023953100354, -5.509193926044956, 4.712388977988545, -2.622188995429787], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [-1.392908672652407, -3.5673600645888737, 1.0398093941698567, -5.326430961973991, 4.712388977064929, -2.963704999497277], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [-1.6913937521262539, -3.3847429218167844, 0.6322947612551082, -5.101533472876494, 4.712388976517914, -3.2621900789711242], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [4.32226695375123, -3.872707438192579, 1.7804900707013485, 3.6630136945243064, -4.712388982240333, -0.3901220264358558], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [4.067990148419234, -3.835480364014888, 1.681899123084908, 3.724377568382729, -4.712388982230876, -0.6443988317678508], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [3.821731137105227, -3.791333465793145, 1.5695137145099463, 3.792616079139966, -4.712388982118972, -0.890657843081858], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [3.5897877098359334, -3.7233127276305984, 1.4030353478766215, 3.891073707966185, -4.712388981921602, -1.1226012703511514], "joint_types": [0, 0, 0, 0, 0, 0], "joint_names": ["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"]}}] \ No newline at end of file diff --git a/lecture_04/assignment_03/maxence_grangeot/assignment_03.py b/lecture_04/assignment_03/maxence_grangeot/assignment_03.py new file mode 100644 index 0000000..6bac274 --- /dev/null +++ b/lecture_04/assignment_03/maxence_grangeot/assignment_03.py @@ -0,0 +1,61 @@ +"""Assignment 03: Using inverse kinematics +""" +import os +import compas +from compas_fab.backends import RosClient +from compas_fab.robots import Configuration + +from compas.geometry import Frame +from compas.geometry import Point +from compas.geometry import Vector + +compas.DATA = "/Users/Maxence/opt/anaconda3/envs/fs2022/lib/python3.8/site-packages/compas/data/samples/" + + +def calculate_ik_for_frames(robot, frames): + + configs = [] + configs.append(robot.zero_configuration()) + + for f in frames: + start_configuration = configs[-1] + configs.append(robot.inverse_kinematics(f, start_configuration)) + configs.pop(0) + return configs + + +def store_configurations(configurations, filename): + compas.json_dump(configurations, filename) + # Is it supposed to be more sophiticated? I don't understand the reason to define such function. + # Also, I don't find the difference between compas.json_dump and compas.json_dumps + pass + + +# the solution_viewer.ghx can't find the assignment_03.py (which is in the same folder). +# I fixed this by loading the json into GHPython but I would like to understand why it doesn't work by default. +if __name__ == '__main__': + + frames = [ + Frame(Point(-0.329, 0.059, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(-0.260, 0.129, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(-0.186, 0.194, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(-0.106, 0.252, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(-0.020, 0.299, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.074, 0.329, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.172, 0.330, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.263, 0.295, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.339, 0.233, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.400, 0.155, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000)), + Frame(Point(0.448, 0.070, 0.082), Vector(1.000, 0.000, 0.000), Vector(0.000, -1.000, 0.000))] + + with RosClient('localhost') as client: + robot = client.load_robot() + + configurations = calculate_ik_for_frames(robot, frames) + print("Found {} configurations".format(len(configurations))) + for i in range(0, len(configurations)): + print(configurations[i]) + + filename = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'assignment-03.json') + store_configurations(configurations, filename) + print("Stored results in {}".format(filename)) diff --git a/lecture_04/assignment_03/maxence_grangeot/solution_viewer.ghx b/lecture_04/assignment_03/maxence_grangeot/solution_viewer.ghx new file mode 100644 index 0000000..1af454d --- /dev/null +++ b/lecture_04/assignment_03/maxence_grangeot/solution_viewer.ghx @@ -0,0 +1,2200 @@ + + + + + + + + 0 + 2 + 2 + + + + + + + 1 + 0 + 7 + + + + + + 267186b6-22d4-4301-830e-efc68b78e363 + Shaded + 1 + + 100;150;0;0 + + + 100;0;150;0 + + + + + + 637515172939181553 + + solution_viewer.ghx + + + + + 0 + + + + + + -655 + 473 + + 1.17011452 + + + + + 0 + + + + + + + 0 + + + + + 2 + + + + + GhPython, Version=7.15.22039.13002, Culture=neutral, PublicKeyToken=null + 7.15.22039.13002 + + 00000000-0000-0000-0000-000000000000 + + + + + + + NGonGh, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + 1.0.0.0 + Petras Vestartas + 20563e24-568f-4f4f-b61b-71a1781ef92f + NGon + 3.0.0 + + + + + + + 15 + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + c7d255bb-e9cf-4deb-b4de-248fedfcc9fd + Boolean Toggle + Connect + false + 0 + true + + + + + + 31 + 228 + 107 + 22 + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + 1e891ee1-8c8e-4909-bfa8-9478fe5871e0 + Boolean Toggle + Load + false + 0 + true + + + + + + 29 + 252 + 90 + 22 + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;170;135;255 + + A group of Grasshopper objects + 16792f65-ce66-4c57-8894-6616e5c283b2 + c7d255bb-e9cf-4deb-b4de-248fedfcc9fd + 1e891ee1-8c8e-4909-bfa8-9478fe5871e0 + 84446ac2-e8f7-4c7d-9d31-dab3deade42d + eccb9686-8536-4332-975d-684efb918cee + ed6dd17e-9615-4209-9f27-8c842a439b86 + 6 + ca5efeaf-96ea-4f51-8b8c-044bf95f301c + Group + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + # NOTE: Don't write you solution here! +# Write the code in the assignment_03.py and if +# the file is located in the same folder as this GH file +# then Grasshopper will find it and import it +from compas_ghpython import unload_modules +unload_modules('assignment_03') + +try: + from assignment_03 import calculate_ik_for_frames +except ImportError: + raise Exception("Please make sure you have the file assignment_03.py in the same location as this GH file") + +from compas_fab.ghpython.components import coerce_frame + +frames = [coerce_frame(f) for f in frames] +configuration = calculate_ik_for_frames(robot, frames) + + GhPython provides a Python script component + + 937 + 218 + + + 1254 + 517 + + true + true + false + 205c786a-b8cf-4a07-8455-eea7b91954d2 + false + true + GhPython Script + Python + + + + + + 771 + 244 + 140 + 44 + + + 824 + 266 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + ba3333a4-a4af-41c9-9db8-4a84ea6a37df + robot + robot + true + 0 + true + d4bae7b0-750f-4b86-a929-7bf58d55c307 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 773 + 246 + 36 + 20 + + + 792.5 + 256 + + + + + + + + 1 + true + Script input frames. + c640367c-4d94-4702-8a36-9b422631f663 + frames + frames + true + 1 + true + 716141ca-3bd8-4b27-8dca-290a69e4a2eb + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 773 + 266 + 36 + 20 + + + 792.5 + 276 + + + + + + + + Script output configuration. + 66f1bdea-7701-4476-b28d-6ea50cb1a15e + configuration + configuration + false + 0 + + + + + + 839 + 246 + 70 + 40 + + + 874 + 266 + + + + + + + + + + + + + + 59daf374-bc21-4a5e-8282-5504fb7ae9ae + List Item + + + + + 0 + Retrieve a specific item from a list. + 7198f137-5adf-40bd-9365-ce004651bcdf + List Item + Item + + + + + + 975 + 254 + 58 + 64 + + + 1004 + 286 + + + + + + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 2e3ab970-8545-46bb-836c-1c11e5610bce + cb95db89-6165-43b6-9c41-5702bc5bf137 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + 1 + Base list + a71a26e2-bc83-4c3a-bb49-8f2a379f82e6 + List + L + false + 27b685fd-1cb6-4e8f-a013-e0272446db09 + 1 + + + + + + 977 + 256 + 12 + 20 + + + 984.5 + 266 + + + + + + + + Item index + 7da95829-d7e8-4d73-b01e-2a3b037c616a + Index + i + false + 9c88f1df-80d2-4fc0-a569-528fab1a8314 + 1 + + + + + + 977 + 276 + 12 + 20 + + + 984.5 + 286 + + + + + + 1 + + + + + 1 + {0} + + + + + 0 + + + + + + + + + + + Wrap index to list bounds + 5349e571-6e69-4fef-be96-8ba696ebf207 + Wrap + W + false + 0 + + + + + + 977 + 296 + 12 + 20 + + + 984.5 + 306 + + + + + + 1 + + + + + 1 + {0} + + + + + true + + + + + + + + + + + Item at {i'} + 6c5c47d3-b49d-4dbf-a287-2305b02558e7 + false + Item + i + false + 0 + + + + + + 1019 + 256 + 12 + 60 + + + 1025 + 286 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 9c88f1df-80d2-4fc0-a569-528fab1a8314 + Number Slider + + false + 0 + + + + + + 677 + 341 + 160 + 20 + + + 677.8439 + 341.424 + + + + + + 3 + 1 + 1 + 10 + 0 + 0 + 10 + + + + + + + + + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + Plane + + + + + Contains a collection of three-dimensional axis-systems + 716141ca-3bd8-4b27-8dca-290a69e4a2eb + Plane + Pln + false + 0 + + + + + + 668 + 268 + 50 + 24 + + + 693.3483 + 280.2823 + + + + + + 1 + + + + + 11 + {0;0} + + + + + + -0.329135749340268 + 0.0593002570566496 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + -0.259532974962819 + 0.128934720105255 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + -0.185565954946676 + 0.193899069358061 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + -0.106119000573445 + 0.251995495961069 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + -0.019881644636929 + 0.299315256167275 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.073824690361371 + 0.328769668392487 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.171793907843551 + 0.329595740711827 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.263441542280733 + 0.295140741931803 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.33941677215266 + 0.232991413739069 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.399726680779453 + 0.155320440968069 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + 0.44807481757935 + 0.0696050392049117 + 0.0815052285514345 + 1 + 0 + 0 + 0 + -1 + 0 + + + + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;170;135;255 + + A group of Grasshopper objects + 205c786a-b8cf-4a07-8455-eea7b91954d2 + 7198f137-5adf-40bd-9365-ce004651bcdf + 9c88f1df-80d2-4fc0-a569-528fab1a8314 + 716141ca-3bd8-4b27-8dca-290a69e4a2eb + 4 + a10ab389-c3a2-495c-979b-e89e0f6f17c4 + Group + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + ROS Connect + + + + + """ +Connect or disconnect to ROS. + +COMPAS FAB v0.22.0 +""" +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.backends import RosClient +from compas_fab.ghpython.components import create_id + +import compas + +compas.DATA = "/Users/Maxence/opt/anaconda3/envs/fs2022/lib/python3.8/site-packages/compas/data/samples/" + + +class ROSConnect(component): + def RunScript(self, ip, port, connect): + ros_client = None + + ip = ip or '127.0.0.1' + port = port or 9090 + + key = create_id(self, 'ros_client') + ros_client = st.get(key, None) + + if ros_client: + st[key].close() + if connect: + st[key] = RosClient(ip, port) + st[key].run(5) + + ros_client = st.get(key, None) + is_connected = ros_client.is_connected if ros_client else False + return (ros_client, is_connected) + + Connect or disconnect to ROS. +COMPAS FAB v0.22.0 + + 980 + 235 + + + 600 + 500 + + true + true + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAgFJREFUSA3tkzFy2kAUhpeZFOlQlxLdAOUEKCcInEDKDZwTIHfpDBn3yDUFqmnQDRAUTDrECVDKVOvv10geGUvEpT3jN/Oh3ff+3bf79mHMh72LClhrfUjgf1YgiMF99cUQz87ns51Op3Y4HFoWdjIajexisWCJVaJQSXr66TJEN0VR3Pm+b3a7nSGBGY/HrXJ0JkkSczqdTBAEJo5j6b62iuVkc0cn0ak0bZwsxd+FZfNSrxtLp71ajWC43W6b4gyf0yqunMQ9KA/V7/cZWq1vNX+/3//RKYja4/FoKU051rwi5RvBs6TsOdNt8dvNZmM/Mbi0GEewXq//1QHHccxqtXro9Xp55XP5ejCFEHzIQVa4rlsO2n5CnMquVvvFpuVJqvonLQtqfaYYa/Ruef0O6r7LNTkOIbHqaVXLwWBgJcYSGIPf4J4uY2p/Q1a/m5oDK5ptqlqe4RYiUBI6L/k+mUwMiUwURcbzVJl2U5vO5/NSm2WZoVQ/m0qt1JVuaicJdOVMj6VbVPGrX51cTYHF2qd5A821+AFCKA2hbjaDQKfSH6rL9LjwV3oaIpLuMkGML4BvkMKTVYl8HN6T05jPy+UyPBwOX2jpOf6EjdNG/EUCnTaHPtxCCl3mEohgAD8ghleZiyqFq7Wu4qpXCJ12WaKm0GUirll6LfgRexsVeAQ/Rphrvej61QAAAABJRU5ErkJggg== + + false + eccb9686-8536-4332-975d-684efb918cee + true + true + ROS Connect + ROS + + + + + + 182 + 187 + 145 + 64 + + + 241 + 219 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + The ip address ROS. Defaults to 127.0.0.1. + cbcceb8e-d113-460c-8c91-6474031ab1a7 + ip + ip + true + 0 + true + 0 + 37261734-eec7-4f50-b6a8-b8d1f3c4396b + + + + + + 184 + 189 + 42 + 20 + + + 206.5 + 199 + + + + + + + + true + The port of ROS. Defaults to 9090. + 23ab18d4-0816-4280-b382-e2cbdab4caa9 + port + port + true + 0 + true + 0 + 48d01794-d3d8-4aef-990e-127168822244 + + + + + + 184 + 209 + 42 + 20 + + + 206.5 + 219 + + + + + + + + true + If True, connects to ROS. If False, disconnects from ROS. Defaults to False. + da4e32db-96ba-40c4-b661-67c91677a2ed + connect + connect + true + 0 + true + c7d255bb-e9cf-4deb-b4de-248fedfcc9fd + 1 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 184 + 229 + 42 + 20 + + + 206.5 + 239 + + + + + + + + The ROS client. + 20b3b82c-7d50-4449-b49c-d0195f7b838a + ros_client + ros_client + false + 0 + + + + + + 256 + 189 + 69 + 30 + + + 290.5 + 204 + + + + + + + + True if connection established. + f636540e-bf52-4933-9f75-bdfdc23e479c + is_connected + is_connected + false + 0 + + + + + + 256 + 219 + 69 + 30 + + + 290.5 + 234 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + ROS Robot + + + + + """ +Load robot directly from ROS. + +COMPAS FAB v0.22.0 +""" +from compas_ghpython.artists import RobotModelArtist +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.ghpython.components import create_id + + +class ROSRobot(component): + def RunScript(self, ros_client, load): + key = create_id(self, 'robot') + + if ros_client and ros_client.is_connected and load: + # Load URDF from ROS + st[key] = ros_client.load_robot(load_geometry=True, precision='12f') + st[key].artist = RobotModelArtist(st[key].model) + + robot = st.get(key, None) + if robot: # client sometimes need to be restarted, without needing to reload geometry + robot.client = ros_client + return robot + + Load robot directly from ROS. +COMPAS FAB v0.22.0 + true + true + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAV9JREFUSA3tlLFNxEAQRQ0ixwE5W4JTIpYOXIJLoALkEpyRugSXsCWYhNh04A6O9087h7VanzlkAiS+9G5m/szO2nfSFcW/iqLkS3gGxV/RwNYDzFBv3aCn6CGADnpYU0NjAi23qDyAg6OuLIkxEB/hA+5BegIXIRzV8Gl9zVZgD6fzb9EjfMmRHmCEV3iJdUccYq5+So23VEXhzbi2hFjG/J34AHexVpgXeZqmPdUhHbJ6JLG3sCet8ToIC7TE+jojldCC/AmyqnADaEhLGnCQymG0oOWaHWCKuaKHrDxuAyU4aEELHOSkuQk0I1qQd1YjXTtgMZw54eh5UPyWPFO2eBnl76aeTcvlyifYTY5NM6SXtHi7qWVTeoEu3fwRL3mCKXNJf8mCrVnPQPoWquX/WB0nc0vXPH1t1dpty/8im1kdtoEk3lLXiXcqcxecmnskN5klfcbbsoatgb/b/wQi83WCx3scQQAAAABJRU5ErkJggg== + + false + ed6dd17e-9615-4209-9f27-8c842a439b86 + true + true + ROS Robot + Robot + + + + + + 389 + 229 + 113 + 44 + + + 454 + 251 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + The ROS client. + 9df689c7-f25f-4820-b9e3-e243f1ac71db + ros_client + ros_client + true + 0 + true + 20b3b82c-7d50-4449-b49c-d0195f7b838a + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 391 + 231 + 48 + 20 + + + 416.5 + 241 + + + + + + + + true + If True, loads the robot from ROS. Defaults to False. + 28374d3d-390d-4b67-88a5-0746309c0629 + load + load + true + 0 + true + 1e891ee1-8c8e-4909-bfa8-9478fe5871e0 + 1 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 391 + 251 + 48 + 20 + + + 416.5 + 261 + + + + + + + + The robot. + d4bae7b0-750f-4b86-a929-7bf58d55c307 + robot + robot + false + 0 + + + + + + 469 + 231 + 31 + 40 + + + 484.5 + 251 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + Robot Visualize + + + + + """ +Visualizes the robot. + +COMPAS FAB v0.22.0 +""" +from compas.geometry import Frame +from compas.geometry import Transformation +from compas_ghpython.artists import FrameArtist +from compas_ghpython.artists import MeshArtist +from ghpythonlib.componentbase import executingcomponent as component + + +class RobotVisualize(component): + def RunScript(self, robot, group, configuration, attached_collision_meshes, show_visual, show_collision, + show_frames, show_base_frame, show_end_effector_frame, show_acm): + + visual = None + collision = None + attached_meshes = None + frames = None + base_frame = None + ee_frame = None + + if robot: + show_visual = True if show_visual is None else show_visual + configuration = configuration or robot.zero_configuration() + + robot.update(configuration, visual=show_visual, collision=show_collision) + compas_frames = robot.transformed_frames(configuration, group) + + if show_visual: + visual = robot.artist.draw_visual() + + if show_collision: + collision = robot.artist.draw_collision() + + if show_base_frame: + base_compas_frame = compas_frames[0] + artist = FrameArtist(base_compas_frame) + base_frame = artist.draw() + + if show_end_effector_frame: + ee_compas_frame = robot.forward_kinematics(configuration, group, options=dict(solver='model')) + artist = FrameArtist(ee_compas_frame) + ee_frame = artist.draw() + + if show_frames: + frames = [] + for compas_frame in compas_frames[1:]: + artist = FrameArtist(compas_frame) + frame = artist.draw() + frames.append(frame) + + if show_acm: + attached_meshes = [] + for acm in attached_collision_meshes: + frame = robot.forward_kinematics(configuration, options=dict(solver='model', link_name=acm.link_name)) + T = Transformation.from_frame_to_frame(Frame.worldXY(), frame) + mesh = acm.collision_mesh.mesh.transformed(T) + attached_meshes.append(MeshArtist(mesh).draw()) + + return (visual, collision, attached_meshes, frames, base_frame, ee_frame) + + Visualizes the robot. +COMPAS FAB v0.22.0 + true + true + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAvZJREFUSA3lVM9LVHEQn68s6UaySkJgWU9DIZB2s4JkkZ4WBV6ysEMHa0/RqfTUse0faDckjE6WdCkJiegQZZv0qKDCrTByQV/YLyJyZYN1U/o2n7dvlreu7a1LDXzfzHzm8535fufNe0T/vWita0yt+6H/SjO2vdRjmy481puHJtPHtO5ZWUR5AZxi+xzFF1Nk+GopfXAHxeNKJbwcsbu1jkyN2NHlbx+3+Oo2vodGzB8MP7rXRZFGpWz4RQWaH+hENmnt9W7oHQh3ThMZ33lhA+TLiB2RhODO9hkhm6jmwDgNY/8aozU5c6QmBK4PD8is1kZHLB+s7go8WRswPs/FrPMTy9STfU1GJmEdyjOLn+19Rr9SKs0olnlJ61CKi8VdWoXQ7zII298amMqML7Qv5qhOYr46Z7O4RXpDPnEB62bf21bFfTf4BDYYTbfSkz/tN0FcERpYx0D48LtlMjMTFIIPyU0nQ7+yPwKwpR14f+1E/R9i1jm0zT7eaCBOHDAdgx+4Ht6DTAVeJFoncdHA9mgdxYHAxSQZ12Zt2NA8s6ZwiwoggKQ4jSTBptWKIAF4khg8FAVWSM4GWmRyixICSpvEh8bopfYp04uJjeIXecLOENmNbqslBl00pgBwi9GY9RC2VzCu3pfnjZWzC1MkJCSp7Q1fFV/02Ig97NpHWWt3nXUxqFpe/Lk4sRsAICUFAN5uoGiFf90CbBF8WOgx+zd5zbv4TldD7eeFIhBwHFm1AHpZfyoo34pw6dPlpPzUrrggbiNJpRiKly+AzU+VimKeYYtg9vGvYr+QgG2cHHIyr0iKO+6qN3CJ1NNnRMQWPT9qneBrVLM/42IogNPLTe67uKNKpqhtSce/Dlo8deUlM36dFu4MEb8rXdncNph9NXGad6DoVu/Okht4fwle4krbH+x0IG6bWnz7TNrjbZ0TLymwMtGffN/6eqps2eWE9VKuyuWVFCj8riVRcxcNpygsblld1bK7ITf9vMklvWCN9Y/Jb6iJVEHfYlmCAAAAAElFTkSuQmCC + + false + 10a49928-eb17-4269-a1f5-98ee19233fed + true + true + Robot Visualize + Robot Visualize + + + + + + 1120 + 234 + 255 + 204 + + + 1268 + 336 + + + + + + 10 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 6 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + The robot. + 5665115c-dc6f-4b96-b649-90363f6f156d + robot + robot + true + 0 + true + d4bae7b0-750f-4b86-a929-7bf58d55c307 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 2 + + + + + + 1122 + 236 + 131 + 20 + + + 1189 + 246 + + + + + + + + true + The planning group used for end-effector and base visualization. + 19283912-83ba-4eb0-ba1c-6f5ac3311327 + group + group + true + 0 + true + 0 + 37261734-eec7-4f50-b6a8-b8d1f3c4396b + + + + + + 1122 + 256 + 131 + 20 + + + 1189 + 266 + + + + + + + + true + The robot's full configuration. + 1fca49ea-0635-4a0a-b95b-c9f257682b15 + configuration + configuration + true + 0 + true + 6c5c47d3-b49d-4dbf-a287-2305b02558e7 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1122 + 276 + 131 + 20 + + + 1189 + 286 + + + + + + + + 1 + true + A list of attached collision meshes. + 651066a0-4de6-4d81-960c-74dd5ebba2c2 + attached_collision_meshes + attached_collision_meshes + true + 1 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1122 + 296 + 131 + 20 + + + 1189 + 306 + + + + + + + + true + Whether or not to show the robot's visual meshes. + fd6c0bb5-7a57-4ca6-81c0-035515585a87 + show_visual + show_visual + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 316 + 131 + 20 + + + 1189 + 326 + + + + + + + + true + Whether or not to show the robot's collision meshes. + dac981a0-3655-427f-83bb-869a928c361c + show_collision + show_collision + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 336 + 131 + 20 + + + 1189 + 346 + + + + + + + + true + Whether or not to show the robot's joint frames. + de2c0813-2228-47c0-a1fb-25fecad1fc57 + show_frames + show_frames + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 356 + 131 + 20 + + + 1189 + 366 + + + + + + + + true + Whether or not to show the robot's base frame. + 7639b92d-56d4-4e95-8462-1a3570422d31 + show_base_frame + show_base_frame + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 376 + 131 + 20 + + + 1189 + 386 + + + + + + + + true + Whether or not to show the robot's end-effector frame. + f438ee13-4ba8-4090-9a27-aaec8905fe02 + show_end_effector_frame + show_end_effector_frame + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 396 + 131 + 20 + + + 1189 + 406 + + + + + + + + true + Whether or not to show the attached collision meshes (if any). + 9222d70d-5e4f-4408-b727-43a01bb10333 + show_acm + show_acm + true + 0 + true + 0 + d60527f5-b5af-4ef6-8970-5f96fe412559 + + + + + + 1122 + 416 + 131 + 20 + + + 1189 + 426 + + + + + + + + The robot's visual Rhino meshes (if any). + d172c507-69c0-458a-97b9-642920bb140f + visual + visual + false + 0 + + + + + + 1283 + 236 + 90 + 33 + + + 1328 + 252.6667 + + + + + + + + The robot's collision Rhino meshes (if any). + d53f0b28-0a87-4e89-932c-575be550c6f7 + collision + collision + false + 0 + + + + + + 1283 + 269 + 90 + 33 + + + 1328 + 286 + + + + + + + + The attached collision Rhino meshes (if any). + 2069798a-a5d1-4168-afa3-d64cf6264cd8 + attached_meshes + attached_meshes + false + 0 + + + + + + 1283 + 302 + 90 + 34 + + + 1328 + 319.3333 + + + + + + + + The robot's joint frames as Rhino planes (if any). + 8913b320-9e60-4c95-b721-c90ef3174981 + frames + frames + false + 0 + + + + + + 1283 + 336 + 90 + 33 + + + 1328 + 352.6667 + + + + + + + + The robot's base frame as a Rhino plane (if any). + ba6264fe-2f01-4ec7-abb3-24d0af02a5c3 + base_frame + base_frame + false + 0 + + + + + + 1283 + 369 + 90 + 33 + + + 1328 + 386 + + + + + + + + The robot's end-effector frame as a Rhino plane (if any). + cc6cd5a6-9a99-4006-be91-e55a54aacf7e + ee_frame + ee_frame + false + 0 + + + + + + 1283 + 402 + 90 + 34 + + + 1328 + 419.3333 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import compas + +a = compas.json_load("/Users/Maxence/Documents/COMPAS-II-FS2022/lecture_04/assignment_03/maxence_grangeot/assignment-03.json") + GhPython provides a Python script component + + 974 + 368 + + + 1134 + 830 + + true + true + false + 49374fcf-eff6-4bcc-8f21-bf7cf8e1df07 + false + true + GhPython Script + Python + + + + + + 862 + 191 + 43 + 28 + + + 876 + 205 + + + + + + 0 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + Script output a. + 27b685fd-1cb6-4e8f-a013-e0272446db09 + a + a + false + 0 + + + + + + 891 + 193 + 12 + 24 + + + 897 + 205 + + + + + + + + + + + + + + 356848b4-a6c0-4b02-9aa1-2967b950d5d0 + 20563e24-568f-4f4f-b61b-71a1781ef92f + Mesh Edges + + + + + Get All Mesh + 8b08b7fc-3082-4cbd-8fd2-9862a6cdbcd2 + Mesh Edges + MeshEdges + + + + + + 1418 + 230 + 64 + 44 + + + 1446 + 252 + + + + + + Mesh + 2312e122-de64-4bce-9417-93948c482d50 + Mesh + M + true + d172c507-69c0-458a-97b9-642920bb140f + 1 + + + + + + 1420 + 232 + 11 + 40 + + + 1427 + 252 + + + + + + + + 2 + All mesh edges + 0a7b12ff-b27e-4ec0-93b8-47013c98811e + AllE + AE + false + 0 + + + + + + 1461 + 232 + 19 + 20 + + + 1470.5 + 242 + + + + + + + + 2 + Naked mesh edges + 74819f10-d9de-47eb-b4e2-6c0c23ea934f + NakedE + NE + false + 0 + + + + + + 1461 + 252 + 19 + 20 + + + 1470.5 + 262 + + + + + + + + + + + + 537b0419-bbc2-4ff4-bf08-afe526367b2c + Custom Preview + + + + + Allows for customized geometry previews + true + ee06776b-ccfa-4365-ba7c-b41d61322922 + Custom Preview + Preview + + + + + + + 1546 + 269 + 42 + 44 + + + 1574 + 291 + + + + + + Geometry to preview + true + 36ad8378-8a90-4d7d-bc68-bb6866ae5503 + Geometry + G + false + 0a7b12ff-b27e-4ec0-93b8-47013c98811e + 1 + + + + + + 1548 + 271 + 11 + 20 + + + 1555 + 281 + + + + + + + + The material override + 1e9b7366-6569-49c0-a150-4349ea3a3322 + Material + M + false + 5d65e2c0-8b2a-4d0e-b6d9-e0a312b83165 + 1 + + + + + + 1548 + 291 + 11 + 20 + + + 1555 + 301 + + + + + + 1 + + + + + 1 + {0} + + + + + + 255;228;178;227 + + + 255;68;53;68 + + 0.5 + + 255;254;255;255 + + 0 + + + + + + + + + + + + + + + 9c53bac0-ba66-40bd-8154-ce9829b9db1a + Colour Swatch + + + + + Colour (palette) swatch + 5d65e2c0-8b2a-4d0e-b6d9-e0a312b83165 + Colour Swatch + Swatch + false + 0 + + 255;145;145;145 + + + + + + + 1427 + 309 + 86 + 20 + + + 1427.493 + 309.5197 + + + + + + + + + + + + + + + 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 + + + + + \ No newline at end of file