diff --git a/lecture_04/assignment_03/hanbing_zhao/assignment_03.json b/lecture_04/assignment_03/hanbing_zhao/assignment_03.json new file mode 100644 index 0000000..a5f41ba --- /dev/null +++ b/lecture_04/assignment_03/hanbing_zhao/assignment_03.json @@ -0,0 +1 @@ +[{"dtype": "compas.robots/Configuration", "value": {"joint_values": [0.0, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [1.5707963267948966, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [3.141592653589793, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [4.71238898038469, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [6.283185307179586, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [7.853981633974483, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [9.42477796076938, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [10.995574287564276, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [12.566370614359172, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [14.137166941154069, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}, {"dtype": "compas.robots/Configuration", "value": {"joint_values": [15.707963267948966, 0.0, 0.0], "joint_types": [0, 0, 0], "joint_names": []}}] \ No newline at end of file diff --git a/lecture_04/assignment_03/hanbing_zhao/assignment_03.py b/lecture_04/assignment_03/hanbing_zhao/assignment_03.py new file mode 100644 index 0000000..e815571 --- /dev/null +++ b/lecture_04/assignment_03/hanbing_zhao/assignment_03.py @@ -0,0 +1,63 @@ +"""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 + +import math +import json + + +# 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_configuration = Configuration.from_revolute_values([math.pi/2, 0., 0., 0.,0.,0.]) + + for i in range(len(frames)): + # config = Configuration.from_revolute_values([i * math.pi/2, 0., 0., 0.,0.,0.]) + config = robot.inverse_kinematics(frames[i], start_configuration, group=None, return_full_configuration=False, options=None) + # config.joint_values + configs.append(config) + 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) # not the 'filename' + +# 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)) \ No newline at end of file diff --git a/lecture_04/assignment_03/hanbing_zhao/solution_viewer.ghx b/lecture_04/assignment_03/hanbing_zhao/solution_viewer.ghx new file mode 100644 index 0000000..984ddf4 --- /dev/null +++ b/lecture_04/assignment_03/hanbing_zhao/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 + + + + + + 76 + 184 + + 0.7676566 + + + + + 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 + false + + + + + + 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 + false + + + + + + 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 + + 905 + -745 + + + 685 + 571 + + 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/xhBQAAAAlwSFlzAAALDwAACw8BkvkDpQAAAdZJREFUSEu1lD2O4kAQRrkBcwP2BnADfARuABEhcAMcEEyCQKuVCCEkY27A3ADCyUAimQyHm/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/xhBQAAAAlwSFlzAAALDwAACw8BkvkDpQAAAVtJREFUSEu9lMFRwzAQRVMCJaSElJASUgIlUAGTEhguXHPinBJSAiWkBDow/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/xhBQAAAAlwSFlzAAALDwAACw8BkvkDpQAAAdhJREFUSEu1lLFKA0EQhlUIQVBMOhGLgGAtxAcIYmmRJpWNrZ2dhSCWqURtxC6VRSofIYWlhS8QyBMEOzvP/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