{ "cells": [ { "cell_type": "markdown", "id": "c17221d8", "metadata": {}, "source": [ "# Coordinate Systems\n", "\n", "[<< PREVIOUS TUTORIAL](01_03_geometry.ipynb) | [NEXT TUTORIAL >>](01_01_introduction.ipynb)\n", "\n", "## Overview \n", "**This tutorial covers:**\n", "\n", "The relationship of different coordinate systems towards each other and how they are handled by WelDX\n", "\n", "**Requirements:**\n", "\n", "- Opening and navigating through WelDX files ([tutorial](01_01_introduction.ipynb))" ] }, { "cell_type": "code", "execution_count": null, "id": "c8c641f8", "metadata": {}, "outputs": [], "source": [ "# download the example file for this tutorial\n", "\n", "from util import download_tutorial_input_file\n", "\n", "download_tutorial_input_file(print_status=False)" ] }, { "cell_type": "markdown", "id": "54b9a3aa", "metadata": {}, "source": [ "## Dependency graph\n", "\n", "All spatial data like specimen geometries or the trajectory of a welding torch is defined in a coordinate system that serves as an anchor point.\n", "But this isn't necessarily always the same coordinate system.\n", "In fact, it is often more convenient to use different coordinate systems for individual data sets.\n", "This raises the problem that we need to know how the different systems are oriented towards each other.\n", "WelDX ensures this by a special tree like structure called the `CoordinateSystemManager` (we will use the abbreviation CSM from here on) that needs to be part of almost every WelDX file.\n", "Let's find it in our example file and extract it to see what we can do with it:" ] }, { "cell_type": "code", "execution_count": null, "id": "c814abb4", "metadata": {}, "outputs": [], "source": [ "from weldx import WeldxFile\n", "\n", "wxfile = WeldxFile(\"single_pass_weld.wx\")" ] }, { "cell_type": "code", "execution_count": null, "id": "ad43ed6b", "metadata": {}, "outputs": [], "source": [ "wxfile.info()" ] }, { "cell_type": "markdown", "id": "bac36c1a", "metadata": {}, "source": [ "Examining the `info` output, we find that the object we are looking for is stored under the key `coordinate_systems`.\n", "We extract it into a local variable:" ] }, { "cell_type": "code", "execution_count": null, "id": "0b7a2248", "metadata": {}, "outputs": [], "source": [ "csm = wxfile[\"coordinate_systems\"]" ] }, { "cell_type": "markdown", "id": "cfa89eed", "metadata": {}, "source": [ "As previously mentioned, the CSM is based on a tree structure.\n", "Every element added to it needs to define the transformation between itself and its parent node.\n", "In combination with the tree structure this ensures that there is always a transformation path between all contained coordinate systems.\n", "We can get a nice overview of all coordinate systems and their relationship towards each other by calling the `plot_graph` method:" ] }, { "cell_type": "code", "execution_count": null, "id": "63908e4c", "metadata": {}, "outputs": [], "source": [ "csm.plot_graph()" ] }, { "cell_type": "markdown", "id": "06d943ec", "metadata": {}, "source": [ "> HINT: In a jupyter session it is sufficient to just type the variable name at the end of a cell (here `csm`). \n", " This will also plot the graph\n", "\n", "The plot shows us multiple things.\n", "The most important information are the coordinate system names.\n", "Arrows indicate that there is a transformation defined between two coordinate systems.\n", "The direction of the arrows has no practical relevance for using the CSM.\n", "It just specifies the transformation direction that was originally provided by the creator of the file.\n", "Because we can always calculate the inverse transformation, it can be considered as pure information.\n", "More important is the color of the arrows.\n", "Black arrows indicate that the transformation remains constant during the experiment. \n", "For example, the coordinate system of a thermal sensor (`T1`) that is attached at a fixed position on the specimen will not change its relative position towards the specimen coordinate system (`workpiece`).\n", "Therefore, the transformation remains constant.\n", "Yellow arrows represent time dependent transformations.\n", "A good example for this is the torch in a welding application (here `TCP` -> tool center point).\n", "During an experiment, it moves in relation to the workpiece.\n", "Note that in the tree above, we used a base coordinate system called `user_frame`.\n", "The `TCP` movement is defined in relation to this system and not directly to the `workpiece`." ] }, { "cell_type": "markdown", "id": "4cadc45c", "metadata": {}, "source": [ "## Default 3d plot and time interpolation\n", "\n", "The graph plot is nice to get a quick overview of the defined coordinate systems and how they depend on each other.\n", "But it doesn't provide us any information on how they are arranged in 3d space.\n", "For this purpose, the CSM also has a `plot` method.\n", "Before we use this method we have to take a small detour and talk about how time dependencies are treated by the CSM.\n", "\n", "The number of data points for the torch trajectory (TCP) is relatively high because we are dealing with real measurement data here that was recorded with a high frequency.\n", "If we use the default rendering backend (matplotlib), each time dependent data point is rendered individually.\n", "With huge amounts of data, this takes a rather long time to compute and will possibly clutter the image with data points so that it is hard to recognize anything.\n", "The solution to both problems is to resample the time dependent data of the CSM.\n", "\n", "We do this in three steps.\n", "First we get the `time_union` of the CSM:" ] }, { "cell_type": "code", "execution_count": null, "id": "89fe935a", "metadata": {}, "outputs": [], "source": [ "time_union = csm.time_union()" ] }, { "cell_type": "markdown", "id": "26e9474d", "metadata": {}, "source": [ "The `time_union` method collects every time value of all the time dependent coordinate systems of the CSM and merges them into a `Time` object.\n", "`Time` is a simple class that handles all time related operations.\n", "One of its methods is called `resample`.\n", "With this method we can create a new `Time` object with a specified number of data points that still has the same time boundaries as the original one.\n", "So let's say we want to plot our time dependent data with 10 time steps.\n", "We create a corresponding `Time` object with:" ] }, { "cell_type": "code", "execution_count": null, "id": "fe1451ec", "metadata": {}, "outputs": [], "source": [ "time_resampled = time_union.resample(10)" ] }, { "cell_type": "markdown", "id": "c81b609c", "metadata": {}, "source": [ "We pass the resampled time object to the `interp_time` method of the CSM.\n", "This will create a new CSM instance where all time dependent data is interpolated to the passed time values:" ] }, { "cell_type": "code", "execution_count": null, "id": "fd9618a8", "metadata": {}, "outputs": [], "source": [ "csm_interp = csm.interp_time(time_resampled)" ] }, { "cell_type": "markdown", "id": "4769e26b", "metadata": {}, "source": [ "Now we can call `plot` of the interpolated CSM without having to worry, that the calculation takes several minutes to create the plot.\n", "Note that we use `data_sets=[]` as additional argument.\n", "This suppresses the rendering of the attached specimen geometry that would clutter the plot." ] }, { "cell_type": "code", "execution_count": null, "id": "298313d8", "metadata": {}, "outputs": [], "source": [ "# uncomment next line to enable interactive matplotlib plots\n", "# %matplotlib widget" ] }, { "cell_type": "code", "execution_count": null, "id": "7436b3ef", "metadata": {}, "outputs": [], "source": [ "csm_interp.plot(data_sets=[]);" ] }, { "cell_type": "markdown", "id": "fed4b332", "metadata": {}, "source": [ "The plot shows us the locations of all coordinate systems in 3d space.\n", "Time dependent coordinate systems are represented by multiple control points that are connected by an equally colored line.\n", "Each control point marks the position at one of the time steps we used to interpolate the original CSM.\n", "\n", "Depending on the number of coordinate systems, it might be beneficial to render just the ones we are interested in.\n", "For this purpose, we can use the `coordinate_systems` parameter.\n", "It expects a `list` with the names of the coordinate systems we like to render: " ] }, { "cell_type": "code", "execution_count": null, "id": "63500721", "metadata": {}, "outputs": [], "source": [ "csm_interp.plot(coordinate_systems=[\"user_frame\", \"TCP\", \"flange\"], data_sets=[]);" ] }, { "cell_type": "markdown", "id": "030c509a", "metadata": {}, "source": [ "By checking the coordinate $x=0$, $y=0$, $z=0$ in the plot, you can derive that all data is plotted in the `user_frame` coordinate system.\n", "This is because it is the root coordinate system of the CSM as can be seen in the graph we plotted earlier.\n", "If you want to change the reference coordinate system, you can do this with the parameter `reference_system`.\n", "This is especially interesting if time dependencies are involved.\n", "Let's see how everything looks from the `TCP`s perspective:" ] }, { "cell_type": "code", "execution_count": null, "id": "354bd20c", "metadata": {}, "outputs": [], "source": [ "csm_interp.plot(\n", " coordinate_systems=[\"user_frame\", \"TCP\", \"flange\"],\n", " data_sets=[],\n", " reference_system=\"TCP\",\n", ");" ] }, { "cell_type": "markdown", "id": "68506636", "metadata": {}, "source": [ "Now the `user_frame` has suddenly become time dependent, and the flange remains static.\n", "This does make sense considering that the torch is mounted on the flange and both move in relation to the `user_frame` coordinate system.\n", "The `plot` method has a lot more parameters to tweak the plot to your liking.\n", "However, demonstrating them all would exceed the scope of this tutorial.\n", "Checkout the [corresponding API documentation page](https://weldx.readthedocs.io/en/latest/_autosummary/weldx.CoordinateSystemManager.plot.html#weldx.CoordinateSystemManager.plot) to learn about the other options." ] }, { "cell_type": "markdown", "id": "6c6d1956", "metadata": {}, "source": [ "## Interactive 3d plots with k3d\n", "\n", "As in [the previous tutorial](01_03_geometry.ipynb) about 3d geometries, the CSM also supports `k3d` as rendering backend inside a jupyter notebook session.\n", "This backend is much more powerful and doesn't require us to make any compromises by interpolating the data.\n", "It can handle huge datasets quite well.\n", "So we can safely use the original CSM that we stored in the variable `csm`.\n", "\n", "The CSM's `plot`method offers a high level of interactivity when using `k3d` as renderer.\n", "This reduces the amount of parameters you need to remember or look up if you want to modify the graphical output to your liking.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "495bcf86", "metadata": {}, "outputs": [], "source": [ "csm.plot(backend=\"k3d\")" ] }, { "cell_type": "markdown", "id": "99954600", "metadata": {}, "source": [ "The first, most obvious thing you will notice is that the 3d scan of the geometry is now visualized because we didn't disable it anymore.\n", "The representation of the coordinate systems is similar to the one used in the matplotlib plots.\n", "However, the individual data points of time dependent coordinate systems are not rendered anymore.\n", "Instead, the \"Time\" slider of the control panel below the plot lets you pick a specific point in time.\n", "All coordinate systems are placed at the correct positions in accordance with the selected time step.\n", "\n", "The control panel also lets you modify the data representation to your liking.\n", "It covers most of the options that can be passed as parameters to the `plot` function.\n", "Try them out, to get a feeling of what you can do.\n", "\n", "Of course, you can also use the parameters of the `plot` method to disable certain data and to set the initial values of the control panel:" ] }, { "cell_type": "code", "execution_count": null, "id": "1b0a48e3", "metadata": {}, "outputs": [], "source": [ "csm.plot(\n", " coordinate_systems=[\"user_frame\", \"TCP\", \"flange\"],\n", " data_sets=[],\n", " reference_system=\"TCP\",\n", " backend=\"k3d\",\n", ")" ] }, { "cell_type": "markdown", "id": "dc9b8023", "metadata": {}, "source": [ "## Conclusion\n", "\n", "In this tutorial we have learned about the `CoordinateSystemManager` class.\n", "We have gotten a basic overview over the contained coordinate systems and how they depend on each other by plotting the dependency graph using `plot_graph`.\n", "Furthermore, we saw how to use the `plot` method and its different rendering backends to visualize the spatial arrangement of the different coordinate systems.\n", "\n", "## Further Readings\n", "\n", "- [API documentation: CoordinateSystemManager](https://weldx.readthedocs.io/en/latest/tutorials/transformations_02_coordinate_system_manager.html)\n", "\n", "[<< PREVIOUS TUTORIAL](01_03_geometry.ipynb) | [NEXT TUTORIAL >>](01_01_introduction.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "id": "474c6a1e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "", "language": "python", "name": "" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.5" } }, "nbformat": 4, "nbformat_minor": 5 }