diff --git a/lecture_04/assignment_03/ananya_kango/assignment-03.json b/lecture_04/assignment_03/ananya_kango/assignment-03.json new file mode 100644 index 0000000..a2365ff --- /dev/null +++ b/lecture_04/assignment_03/ananya_kango/assignment-03.json @@ -0,0 +1,321 @@ +[ + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -6.057747560698094, + 4.186381649930286, + -2.128169050660379, + 5.795769035492999, + -4.712388971897831, + 1.7962340734893134 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -6.2752833395775465, + 4.290723852532892, + -2.365750309119598, + 5.929008089454119, + -4.712388971838435, + 1.5786982946098613 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -0.29709798154273676, + -1.9446282916825937, + -2.480754159211547, + -0.2870065396566978, + -4.712388974290103, + -5.009486962008193 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -0.672703287978784, + -1.9547210433746867, + -2.4559602539291374, + -0.3017076954774477, + -4.712388978268733, + -5.38509226844424 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -1.0513929194895173, + -2.0141374192441255, + -2.315268365985418, + -0.3829832084206043, + -4.71238898304272, + -5.7637818999549735 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -1.3929065846634088, + -2.104166227211656, + -2.1118545399914344, + -0.49636822568933664, + -4.712388987657746, + -6.105295565128865 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -5.552737458744558, + -0.9481634130909733, + 1.9159366141372824, + -2.5385695284470624, + -1.5707963263603117, + 5.442836828618871 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -5.778493871741615, + -0.8855879856353794, + 1.780491145781097, + -2.4656994874970084, + -1.5707963262684945, + 5.2170804156218145 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -6.005258766433345, + -0.8397376379484895, + 1.681899508424523, + -2.4129581977559553, + -1.5707963261874696, + 4.990315520930085 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -6.223955465496162, + -0.7872421679803526, + 1.5695141088518056, + -2.353068268064482, + -1.5707963261246773, + 4.771618821867268 + ] + } + }, + { + "dtype": "compas.robots/Configuration", + "value": { + "joint_names": [ + "shoulder_pan_joint", + "shoulder_lift_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "joint_types": [ + 0, + 0, + 0, + 0, + 0, + 0 + ], + "joint_values": [ + -2.6933975641036167, + 3.8507438100444116, + -1.4030352459531346, + 5.406273142528284, + -4.712388799291535, + -1.1226012363178763 + ] + } + } +] \ No newline at end of file diff --git a/lecture_04/assignment_03/ananya_kango/assignment_03.py b/lecture_04/assignment_03/ananya_kango/assignment_03.py new file mode 100644 index 0000000..592a31e --- /dev/null +++ b/lecture_04/assignment_03/ananya_kango/assignment_03.py @@ -0,0 +1,66 @@ +import os +#from xml.dom.expatbuilder import FragmentBuilderNS +import compas +import compas_fab +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 + + +# Step 1: Inside this function, complete the main part of the solution for the assignment: +# - Taking a robot and a list of frames as parameter, calculate a feasible configuration for each of the frames +# - Try to find an optimal start_configuration for each so that the motion from one config to the next is minimized +def calculate_ik_for_frames(robot, frames): + configs = [] + start_config = robot.zero_configuration() + options = {"solver": "ur3e", "check_collision": True, "keep_order": True} + base_configs = [] + #list of start configurations from zero configuration + for it in robot.iter_inverse_kinematics(frames[0], options=options): + base_configs.append(it) + #sort list to limit movement in 'shoulder lift' andd 'wrist 1' to a maximum value, so as to avoid unfeasible configurations and singularities during motion + start_config = sorted(base_configs,key = lambda values:values.joint_values[1]+values.joint_values[3])[-1] + #use optimal start config to get list of feasible configurations on each frame + for f in frames: + ik = robot.inverse_kinematics(f, start_config, group = robot.main_group_name, return_full_configuration = True,options = options) + start_config = ik + configs.append(ik) + return configs + + +# Step 2: store all found configurations in a JSON file using compas.json_dump or compas.json_dumps +def store_configurations(configurations, filename): + compas.json_dump(configurations,filename,pretty=True) + +# Use the following to test from the command line +# Or copy solution_viewer.ghx next to the folder where you created assignment_03.py to visualize the same in Grasshopper +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))] + + # Loads the robot from ROS + with RosClient('localhost') as client: + robot = client.load_robot() + + # Step 1: calculate IK solutions for each frame + configurations = calculate_ik_for_frames(robot, frames) + print("Found {} configurations".format(len(configurations))) + + # Step 2: store all configurations in a JSON file + 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/ananya_kango/solution_viewer.ghx b/lecture_04/assignment_03/ananya_kango/solution_viewer.ghx new file mode 100644 index 0000000..a550030 --- /dev/null +++ b/lecture_04/assignment_03/ananya_kango/solution_viewer.ghx @@ -0,0 +1,1824 @@ + + + + + + + + 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 + + + + + + -1876 + -509 + + 2.87352824 + + + + + 0 + + + + + + + 0 + + + + + 1 + + + + + GhPython, Version=7.15.22039.13001, Culture=neutral, PublicKeyToken=null + 7.15.22039.13001 + + 00000000-0000-0000-0000-000000000000 + + + + + + + + + 11 + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + c7d255bb-e9cf-4deb-b4de-248fedfcc9fd + Boolean Toggle + Connect + false + 0 + true + + + + + + 31 + 228 + 110 + 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 + 94 + 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 + + 891 + 131 + + + 1373 + 900 + + true + true + false + 205c786a-b8cf-4a07-8455-eea7b91954d2 + false + true + GhPython Script + Python + + + + + + 787 + 233 + 149 + 60 + + + 844 + 263 + + + + + + 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 + + + + + + 789 + 235 + 40 + 28 + + + 810.5 + 249 + + + + + + + + 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 + + + + + + 789 + 263 + 40 + 28 + + + 810.5 + 277 + + + + + + + + Script output configuration. + 66f1bdea-7701-4476-b28d-6ea50cb1a15e + configuration + configuration + false + 0 + + + + + + 859 + 235 + 75 + 56 + + + 896.5 + 263 + + + + + + + + + + + + + + 59daf374-bc21-4a5e-8282-5504fb7ae9ae + List Item + + + + + 0 + Retrieve a specific item from a list. + 7198f137-5adf-40bd-9365-ce004651bcdf + List Item + Item + + + + + + 990 + 251 + 64 + 64 + + + 1024 + 283 + + + + + + 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 + 66f1bdea-7701-4476-b28d-6ea50cb1a15e + 1 + + + + + + 992 + 253 + 17 + 20 + + + 1002 + 263 + + + + + + + + Item index + 7da95829-d7e8-4d73-b01e-2a3b037c616a + Index + i + false + 9c88f1df-80d2-4fc0-a569-528fab1a8314 + 1 + + + + + + 992 + 273 + 17 + 20 + + + 1002 + 283 + + + + + + 1 + + + + + 1 + {0} + + + + + 0 + + + + + + + + + + + Wrap index to list bounds + 5349e571-6e69-4fef-be96-8ba696ebf207 + Wrap + W + false + 0 + + + + + + 992 + 293 + 17 + 20 + + + 1002 + 303 + + + + + + 1 + + + + + 1 + {0} + + + + + true + + + + + + + + + + + Item at {i'} + 6c5c47d3-b49d-4dbf-a287-2305b02558e7 + false + Item + i + false + 0 + + + + + + 1039 + 253 + 13 + 60 + + + 1045.5 + 283 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 9c88f1df-80d2-4fc0-a569-528fab1a8314 + Number Slider + + false + 0 + + + + + + 697 + 338 + 160 + 20 + + + 697.8439 + 338.424 + + + + + + 3 + 1 + 1 + 10 + 0 + 0 + 0 + + + + + + + + + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + Plane + + + + + Contains a collection of three-dimensional axis-systems + 716141ca-3bd8-4b27-8dca-290a69e4a2eb + Plane + Pln + false + 0 + + + + + + 688 + 267 + 50 + 20 + + + 713.3483 + 277.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 + + +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 + true + true + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDQAACw0B7QfALAAAAdZJREFUSEu1lD2O4kAQRrkBcwP2BnADfARuABEhcAMcEEyCQKuVCCEkY27A3ADCyUAimQyHm/V+r9bNNp5iSLwtPWF/9dvdZRohhP+KK9aJK9aJK9aJKzpk4k08W4XYiB/CYquJPJbX6zVMp9PQbrcV0XhIt9sN6/VaIVZoQLyXMGVC8piYXwp5jMfj0Gq1zK/f7yvUVsdLGnkRBV0RlHT2/g2WHH+KonmJI4PD4ZA6HwVFPd9IR1hTzWZTj76Tcjay1Wr1QWIKnE6n0Ov17DnhXeSiWnTJbvHZ7/d3BkNrg3E2m/2OBbiH4XCITkLg+YhNnMVtakRuib0CWgMMYlMUxetutzPH8vwZ00f+HB8auznHe6CxagDdnMt3ztPOkunAWYsiPcF3YYxGo19ZloXtdvtT78d4b9yDVpEmf8Eg8qiJt7gLCi0WC9v2IxjV6Mu9aU3SAh2MYhI1wZaPBMcZfwadl8n5oqX8Sya7OZkhgSJoge1Xu04pE/Ot3E4hTUQBmyDB+d7ZBIU4f4INBmE+n3/meR4ul8tS2pe4+5e/91CURRjH7BuYIIYCX/vf8fgqaKYFHxEvz6CZh8nBFUGLQl7nN7y4Kq5YJ65YJ65YJ65YH6HxB7b14P1B22tpAAAAAElFTkSuQmCC + + false + eccb9686-8536-4332-975d-684efb918cee + true + true + ROS Connect + ROS + + + + + + 178 + 187 + 153 + 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 + + + + + + 180 + 189 + 46 + 20 + + + 204.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 + + + + + + 180 + 209 + 46 + 20 + + + 204.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 + + + + + + 180 + 229 + 46 + 20 + + + 204.5 + 239 + + + + + + + + The ROS client. + 20b3b82c-7d50-4449-b49c-d0195f7b838a + ros_client + ros_client + false + 0 + + + + + + 256 + 189 + 73 + 30 + + + 292.5 + 204 + + + + + + + + True if connection established. + f636540e-bf52-4933-9f75-bdfdc23e479c + is_connected + is_connected + false + 0 + + + + + + 256 + 219 + 73 + 30 + + + 292.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 + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDQAACw0B7QfALAAAAVtJREFUSEu9lMFRwzAQRVMCJaSElJASUgIlUAGTEhguXHPinBJSAiWkBDow/9n6ZmWtQzwYduaNpdVqtf5redN13Z+SOtckdS5B9iCeeKbrmXMJsrNg8CkOzXo1Gao5iYtg4z6uR2SP4iqY+Ans3Y5xk00sTjfsBcmOgbjOmMK2wvs/mgNKQL8o3sRzmb8Iy5BRySLbifHNpwsM3gWHvJY5ByAb44xKRtkoTz+vJkNiBn7CQXAIr29oqNd7OWTIhHxMrmNOD0oQb2EdSYL2VUUlDjlJ5kKQMDa8lahsdEPdNFfUHFLiiYsNJ766D9WG3lHLYy7TOCOjEArLi2gcQ7ATR2bvxC1yZ/7VjI1bQu4cXjt+KeaYxd8idQLJQmLDoelPbY7UaWTxCzGnLHaO1Glkv2546xhubUz2E8i2m+YxreP7Ji9htvmt4x8O4Ffhn9q93C/R2qTO9eg2XxfZxOCoCLXEAAAAAElFTkSuQmCC + + false + ed6dd17e-9615-4209-9f27-8c842a439b86 + true + true + ROS Robot + Robot + + + + + + 384 + 224 + 124 + 54 + + + 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 + + + + + + 386 + 226 + 53 + 25 + + + 414 + 238.5 + + + + + + + + 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 + + + + + + 386 + 251 + 53 + 25 + + + 414 + 263.5 + + + + + + + + The robot. + d4bae7b0-750f-4b86-a929-7bf58d55c307 + robot + robot + false + 0 + + + + + + 469 + 226 + 37 + 50 + + + 487.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 + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDQAACw0B7QfALAAAAdhJREFUSEu1lLFKA0EQhlUIQVBMOhGLgGAtxAcIYmmRJpWNrZ2dhSCWqURtxC6VRSofIYWlhS8QyBMEOzvP/zt2jtm9VSLEg+9ud2bnn9nZu1spiuJfyRqXSdb4F3ZvX1vigmfOXzP8FQm/iEJ8iH7qjyZUIUZiIgjseb9HvjMxE4jbE4jt2Lo0CGca0BOI3Ti8nzGFdYTFv9cShAWlUzyK6zC/E9aGHFFbND8Q1c5TBwHPgiQPYU4C2uZFPVEbNa/aA9wqg5wIE2RP6AuSsH2DAzV/2Q49aRPtwzYzTW5VBXKwC+sjIvQ+qiiso52IWSG00B941CKfwA7UDs0qqiUJ61nnD5z10ffALe2hb48x8Ws88lEIhWWLqBnC4jQBRIUsStYosdxbUx6croFgAJcWo6st5sE+ruw28EiMbfs3xbjBnxWKEw8quw1SEEvEgaQtCQydWJv1zjb3OpFoisT8G2KMJNINYlBWq8t2NfQakWCKxH48cAlNg+CT8AmPvUYkCArmq82JRmydnJeCa+sbX42dvfsgPk31ogko2L7kX9m+GpcJYLXR/AzjqD0QTUDBCyWA5v5hlSTQTfWiCSiQX4X91H5l8+jUzgHeUi2oGZZN1rg8ipVvhzuVIxcFIsQAAAAASUVORK5CYII= + + false + 10a49928-eb17-4269-a1f5-98ee19233fed + true + true + Robot Visualize + Robot Visualize + + + + + + 1235 + 231 + 261 + 204 + + + 1386 + 333 + + + + + + 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 + + + + + + 1237 + 233 + 134 + 20 + + + 1305.5 + 243 + + + + + + + + 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 + + + + + + 1237 + 253 + 134 + 20 + + + 1305.5 + 263 + + + + + + + + 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 + + + + + + 1237 + 273 + 134 + 20 + + + 1305.5 + 283 + + + + + + + + 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 + + + + + + 1237 + 293 + 134 + 20 + + + 1305.5 + 303 + + + + + + + + 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 + + + + + + 1237 + 313 + 134 + 20 + + + 1305.5 + 323 + + + + + + + + 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 + + + + + + 1237 + 333 + 134 + 20 + + + 1305.5 + 343 + + + + + + + + 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 + + + + + + 1237 + 353 + 134 + 20 + + + 1305.5 + 363 + + + + + + + + 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 + + + + + + 1237 + 373 + 134 + 20 + + + 1305.5 + 383 + + + + + + + + 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 + + + + + + 1237 + 393 + 134 + 20 + + + 1305.5 + 403 + + + + + + + + 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 + + + + + + 1237 + 413 + 134 + 20 + + + 1305.5 + 423 + + + + + + + + The robot's visual Rhino meshes (if any). + d172c507-69c0-458a-97b9-642920bb140f + visual + visual + false + 0 + + + + + + 1401 + 233 + 93 + 33 + + + 1447.5 + 249.6667 + + + + + + + + The robot's collision Rhino meshes (if any). + d53f0b28-0a87-4e89-932c-575be550c6f7 + collision + collision + false + 0 + + + + + + 1401 + 266 + 93 + 33 + + + 1447.5 + 283 + + + + + + + + The attached collision Rhino meshes (if any). + 2069798a-a5d1-4168-afa3-d64cf6264cd8 + attached_meshes + attached_meshes + false + 0 + + + + + + 1401 + 299 + 93 + 34 + + + 1447.5 + 316.3333 + + + + + + + + The robot's joint frames as Rhino planes (if any). + 8913b320-9e60-4c95-b721-c90ef3174981 + frames + frames + false + 0 + + + + + + 1401 + 333 + 93 + 33 + + + 1447.5 + 349.6667 + + + + + + + + The robot's base frame as a Rhino plane (if any). + ba6264fe-2f01-4ec7-abb3-24d0af02a5c3 + base_frame + base_frame + false + 0 + + + + + + 1401 + 366 + 93 + 33 + + + 1447.5 + 383 + + + + + + + + The robot's end-effector frame as a Rhino plane (if any). + cc6cd5a6-9a99-4006-be91-e55a54aacf7e + ee_frame + ee_frame + false + 0 + + + + + + 1401 + 399 + 93 + 34 + + + 1447.5 + 416.3333 + + + + + + + + + + + + + + + + + + + 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 + + + + + \ No newline at end of file