Run the interactive online version of this notebook (takes 1-2 minutes to load): Binder badge

Welding Example #01: Basics

The goal of this small example is to introduce the main functionalities and interfaces to create and describe a simple welding application using the WelDX package.

Imports

[3]:
# some python imports that will be used throughout the tutorial
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
import pint
import xarray as xr
from mpl_toolkits.mplot3d import Axes3D
[4]:
# importing the weldx package with prevalent default abbreviations
import weldx
import weldx.geometry as geo
import weldx.transformations as tf
import weldx.util
import weldx.visualization as vis
from weldx import Q_
from weldx.transformations import LocalCoordinateSystem as lcs
from weldx.transformations import WXRotation

create a simple welding groove shape

We start of defining our welding application example by defining the base groove shape. For this examples we assume the groove shape to be constant along the entire workpiece.

The groove shall have the following attributes:

  • a workpiece thickness of 5 mm

  • a single sided V-Groove butt weld with 50 degree groove angle

  • root gap and face of 1 mm

To generate the groove shape, we can use the get_groove function of the of iso_groove that implements all groove types and shapes defined in ISO 9692-1:2013. For all available groove types and options take a look at the extensive docstring of get_groove and the groove_type tutorial notebook.

Be aware that we must pass along all groove parameters as Quantities with a specified unit using the default Q_ type imported above. All units are automatically converted to SI units for most mathematical operations in the background so we can specify parameters in any appropriate unit we desire.

[5]:
from weldx.welding.groove.iso_9692_1 import get_groove

groove = get_groove(
    groove_type="VGroove",
    workpiece_thickness=Q_(5, "mm"),
    groove_angle=Q_(50, "deg"),
    root_face=Q_(1, "mm"),
    root_gap=Q_(1, "mm"),
)

Using the plot function of the created groove instance gives a quick overview of the created geometry.

[6]:
groove.plot(raster_width=0.2)
../_images/tutorials_welding_example_01_basics_9_0.svg

create 3d workpiece geometry

Once we have created our desired 2d groove shape, we can simply extend the groove shape into 3d-space to create a volumetric workpiece.

To do this, two steps are missing:

  1. we have to decide on a weld seam length first (we will use 300 mm in this example)

  2. create a trace object that defines the path of our element through space. (we use a simple linear trace in this example)

[7]:
# define the weld seam length in mm
seam_length = Q_(300, "mm")

# create a linear trace segment a the complete weld seam trace
trace_segment = geo.LinearHorizontalTraceSegment(seam_length)
trace = geo.Trace(trace_segment)

Once we have defined the trace object, we can create the workpiece geometry by combining the groove profile with the trace.

[8]:
# create 3d workpiece geometry from the groove profile and trace objects
geometry = geo.Geometry(groove.to_profile(width_default=Q_(5, "mm")), trace)

To visualize the geometry we simply call its plot function. Since it internally rasterizes the data, we need to provide the raster widths:

[9]:
# rasterize geometry
profile_raster_width = 2  # resolution of each profile in mm
trace_raster_width = 30  # space between profiles in mm

Here is the plot:

[11]:
ax = geometry.plot(
    profile_raster_width,
    trace_raster_width,
    color=color_dict["specimen"],
    show_wireframe=True,
    label="groove",
)
ax_setup(ax)
../_images/tutorials_welding_example_01_basics_18_0.svg

Setup the Coordinate System Manager (CSM)

Once we have created the 3d geometry it is now time to describe the movement of the wire during the welding process. To handle different moving coordinate systems and objects we use the CoordinateSystemManager.

Start by creating a new instance of the CSM. When setting up a CSM instance we have to supply a name that indicates the reference coordinate system which is a static Cartesian coordinate system that defines an origin.

[12]:
# crete a new coordinate system manager with default base coordinate system
csm = weldx.transformations.CoordinateSystemManager("base")

The trace we created earlier to extend the groove shape into 3d has its own associated coordinate system that starts in the origin of the groove (see point (0,0) in our first plot of the groove profile) and has the x-axis running along the direction of the weld seam by convention.

We simply add the trace coordinate system to our coordinate system manager defining it as the workpiece coordinate system.

[13]:
# add the workpiece coordinate system
csm.add_cs(
    coordinate_system_name="workpiece",
    reference_system_name="base",
    lcs=trace.coordinate_system,
)

Now that we have added the workpiece coordinate system to the CSM, we can attach a rasterized representation of our geometry to it:

[14]:
csm.assign_data(
    geometry.spatial_data(profile_raster_width, trace_raster_width),
    "specimen",
    "workpiece",
)

generate the tcp movement of the wire tip

For this example, we want the tip of the wire (i.e. the robot TCP during welding) to move along the center of the groove at 2 mm from the bottom of the workpiece with a speed of 10 mm/s.

We begin by defining the start and end points relative to our workpiece coordinate system. Note that the z-axis of the workpiece coordinate system is pointing upwards (see Figure 1). Hence we use a positive offset of 2 mm in z direction from our workpiece. For the x-axis we start the weld 5 mm into the weldseam and 5 mm before reaching the end of the weldseam.

[15]:
tcp_start_point = Q_([5.0, 0.0, 2.0], "mm")
tcp_end_point = Q_([-5.0, 0.0, 2.0], "mm") + np.append(seam_length, Q_([0, 0], "mm"))

To completely describe the TCP movement in space and time we need to supply time information for the start and end point. Lets say the weld starts on 2020-04-20 10:00:00. We calculate the time

[16]:
v_weld = Q_(10, "mm/s")
s_weld = (tcp_end_point - tcp_start_point)[0]  # length of the weld
t_weld = s_weld / v_weld

t_start = pd.Timedelta("0s")
t_end = pd.Timedelta(str(t_weld.to_base_units()))

The two points and timestamps are enough to create the linear moving coordinate system. We can interpolate the movement with a higher resolution later.

The orientation of the wire has the z coordinate facing downwards towards the workpiece. The workpiece z-coordinate is facing upwards. We add a constant 180 degree rotation around the x-axis to orientate the wire coordinate system correctly. Orientations can be described using the scipy Rotation objects

[17]:
rot = WXRotation.from_euler(seq="x", angles=180, degrees=True)

With the defined coordinates, the constant orientation and the associated times we can create the coordinate system for the wire tcp.

[18]:
coords = [tcp_start_point.magnitude, tcp_end_point.magnitude]

tcp_wire = lcs(coordinates=coords, orientation=rot, time=[t_start, t_end])

Add the new coordinate system to the coordinate system manager relative to the workpiece.

[19]:
# add the workpiece coordinate system
csm.add_cs(
    coordinate_system_name="tcp_wire",
    reference_system_name="workpiece",
    lcs=tcp_wire,
)

Lets say The wire extends 10 mm from the contact tip. We can add the contact tip as another point using the coordinate system manager. To simplify things we now use the tcp_wire coordinate system as reference. All we need to add is the z-offset along the wire. Note that we have to provide a negative offset along the z-axis since the wire-tcp z-axis is pointing downwards.

[20]:
tcp_contact = lcs(coordinates=[0, 0, -10])
[21]:
# add the workpiece coordinate system
csm.add_cs(
    coordinate_system_name="tcp_contact",
    reference_system_name="tcp_wire",
    lcs=tcp_contact,
)

We can create a simple plot of the relations between our our coordinate systems

[22]:
csm
../_images/tutorials_welding_example_01_basics_41_0.svg

plot the TCP trajectory

To examine the movement of our wire TCP and contact tip, lets create a simple plot. We only have a linear movement so we don’t have to add additional timestamps to the moving coordinate systems to increase the resolution of the traces.

[23]:
ax = csm.plot(
    coordinate_systems=["tcp_contact", "tcp_wire"],
    colors=color_dict,
    show_vectors=False,
    show_wireframe=True,
)
ax_setup(ax)
../_images/tutorials_welding_example_01_basics_43_0.svg

Add static temperature measurement points

With everything setup we can now start adding some measurements with associated points in space. We add a temperature measurements T1, T2, T3 to the surface of the weld seam.

[24]:
# add the workpiece coordinate system
csm.add_cs("T1", "workpiece", lcs(coordinates=[200, 3, 5]))
csm.add_cs("T2", "T1", lcs(coordinates=[0, 1, 0]))
csm.add_cs("T3", "T2", lcs(coordinates=[0, 1, 0]))
[25]:
ax = csm.plot(
    coordinate_systems=["tcp_contact", "tcp_wire", "T1", "T2", "T3"],
    reference_system="workpiece",
    colors=color_dict,
    show_vectors=False,
    show_wireframe=True,
)
ax_setup(ax)
../_images/tutorials_welding_example_01_basics_46_0.svg
[26]:
csm
../_images/tutorials_welding_example_01_basics_47_0.svg

using ASDF

[28]:
tree = {"workpiece": {"groove": groove, "length": seam_length}, "CSM": csm}
[29]:
buffer = weldx.asdf.util._write_buffer(tree)
[30]:
weldx.asdf.util.notebook_fileprinter(buffer)
[30]:
#ASDF 1.0.0
#ASDF_STANDARD 1.5.0
%YAML 1.1
%TAG ! tag:stsci.edu:asdf/
--- !core/asdf-1.1.0
asdf_library: !core/software-1.0.0 {author: Space Telescope Science Institute, homepage: 'http://github.com/spacetelescope/asdf',
  name: asdf, version: 2.7.3}
history:
  extensions:
  - !core/extension_metadata-1.0.0
    extension_class: weldx.asdf.extension.WeldxExtension
    software: !core/software-1.0.0 {name: weldx, version: 0.3.1}
  - !core/extension_metadata-1.0.0
    extension_class: asdf.extension.BuiltinExtension
    software: !core/software-1.0.0 {name: asdf, version: 2.7.3}
  - !core/extension_metadata-1.0.0
    extension_class: weldx.asdf.extension.WeldxAsdfExtension
    software: !core/software-1.0.0 {name: weldx, version: 0.3.1}
CSM: !<tag:weldx.bam.de:weldx/core/transformations/coordinate_system_hierarchy-1.0.0>
  name: Coordinate system manager 0
  root_system_name: base
  subsystems: []
  coordinate_systems:
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: workpiece
    reference_system: base
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0> {}
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: tcp_wire
    reference_system: workpiece
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0>
      time: !<tag:weldx.bam.de:weldx/time/timedeltaindex-1.0.0>
        values: !core/ndarray-1.0.0
          source: 1
          datatype: int64
          byteorder: little
          shape: [2]
        start: !<tag:weldx.bam.de:weldx/time/timedelta-1.0.0> {value: P0DT0H0M0S}
        end: !<tag:weldx.bam.de:weldx/time/timedelta-1.0.0> {value: P0DT0H0M29S}
        min: !<tag:weldx.bam.de:weldx/time/timedelta-1.0.0> {value: P0DT0H0M0S}
        max: !<tag:weldx.bam.de:weldx/time/timedelta-1.0.0> {value: P0DT0H0M29S}
      orientations: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: orientations
        dimensions: [c, v]
        dtype: <f8
        data: !core/ndarray-1.0.0
          data:
          - [1.0, 0.0, 0.0]
          - [0.0, -1.0, -1.2246467991473532e-16]
          - [0.0, 1.2246467991473532e-16, -1.0]
          datatype: float64
          shape: [3, 3]
      coordinates: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: coordinates
        dimensions: [time, c]
        dtype: <f8
        data: !core/ndarray-1.0.0
          source: 0
          datatype: float64
          byteorder: little
          shape: [2, 3]
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: tcp_contact
    reference_system: tcp_wire
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0>
      coordinates: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: coordinates
        dimensions: [c]
        dtype: <f8
        data: !core/ndarray-1.0.0
          data: [0.0, 0.0, -10.0]
          datatype: float64
          shape: [3]
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: T1
    reference_system: workpiece
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0>
      coordinates: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: coordinates
        dimensions: [c]
        dtype: <f8
        data: !core/ndarray-1.0.0
          data: [200.0, 3.0, 5.0]
          datatype: float64
          shape: [3]
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: T2
    reference_system: T1
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0>
      coordinates: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: coordinates
        dimensions: [c]
        dtype: <f8
        data: !core/ndarray-1.0.0
          data: [0.0, 1.0, 0.0]
          datatype: float64
          shape: [3]
  - !<tag:weldx.bam.de:weldx/core/transformations/coordinate_transformation-1.0.0>
    name: T3
    reference_system: T2
    transformation: !<tag:weldx.bam.de:weldx/core/transformations/local_coordinate_system-1.0.0>
      coordinates: !<tag:weldx.bam.de:weldx/core/variable-1.0.0>
        name: coordinates
        dimensions: [c]
        dtype: <f8
        data: !core/ndarray-1.0.0
          data: [0.0, 1.0, 0.0]
          datatype: float64
          shape: [3]
  subsystem_names: []
workpiece:
  groove: !<tag:weldx.bam.de:weldx/groove/iso_9692_1_2013_12/VGroove-1.0.0>
    t: !unit/quantity-1.1.0 {unit: millimeter, value: 5}
    alpha: !unit/quantity-1.1.0 {unit: degree, value: 50}
    b: !unit/quantity-1.1.0 {unit: millimeter, value: 1}
    c: !unit/quantity-1.1.0 {unit: millimeter, value: 1}
    code_number: ['1.3', '1.5']
  length: !unit/quantity-1.1.0 {unit: millimeter, value: 300}
...

Generated by nbsphinx from a Jupyter notebook.