Skip to content

Paper Quality Optimization for Platform Administrators

This demonstration video was done in v5.5.

There may be slight differences with the latest version.

Check the latest documentation for the specific tasks should any feature not quite work as expected.

The Paper Quality Optimization is also known as Multi-Objective Optimization with Machine Learning.

This is the Platform Administration edition of a 4 part tutorial on turning an inhouse machine learning program developed in a Jupyter Notebook into a scalable solution for deployment to the edge in a Kelvin Instance;

  • Overview - Gives an overview workflow over all three detailed tutorials. Especially good for the non-technical who just want to have an understanding how to integrate and scale an inhouse developed machine learning program using Kelvin.
  • Developer - Detailed walk through to take a Jupyter Notebook containing an inhouse machine learning model program and integrating it as a Kelvin SmartApp™ ready for upload and deployment to the edge.
  • Platform Administrator - Detailed walk through to manage all administrative matters for the tutorial example for the Units, Asset Types, Assets, Data Streams, Connections, Upload to App Registry.
  • Process Engineer - Detailed walk through to add Assets to Kelvin SmartApps™, monitor the performance of the Assets and take action on new Recommendations.

Process Engineers edition are coming very soon !

Concepts

Kelvin is a power industrial platform to convert engineering expertise into Smart Apps.

To achieve this level of simplicity in operations for the Developers and Operations Engineers requires some setup of the core components of Kelvin by the Platform Administrators.

Before starting to setup, we'll give a brief overview of Kelvin for two of its core components;

  1. Infrastructure Overview to understand how Kelvin is technically setup for hardware, software components and locations.
  2. How data is defined and stored in Kelvin

Infrastructure Overview

The infrastructure of Kelvin can be split into three main areas;

  1. Users - The people or services that connect to Kelvin either through the Kelvin UI, Kelvin API or Kelvin SDK.
  2. Cloud - The core system services and storage components of Kelvin that reside in the Cloud.
  3. Edge - The operational systems that runs Kelvin SmartApps™ the edge and manages the data between edge to cloud or edge to edge.

Data

Kelvin uses the industrial standard for identifying individual time series data from each sensor of each asset. This is grouped as an asset / data stream pair.

Starting from a Clean Platform

When you first enter your Kelvin Platform you will need to do a number of tasks to setup the Platform to your requirements that will give the right tools for the Developers to connect their programs to the data and deploy the programs. These steps in order of setting up is;

  • Create all units
  • Create all Semantic Types
  • Create Data Streams
  • Create Asset Types
  • Create Assets
  • Create Clusters
  • Create Connections

Create All Units

Kelvin comes with a list of predefined units. This covers all the most common units.

If you need to create a new specific unit using the Kelvin UI, you can add the units at the same time during the create Data Streams process in the Kelvin UI. There is no special requirments to do this first here.

For the Paper Quality Optimization Kelvin SmartApp™, we need to create one extra unit;

Title Name ID Symbol
Meters Cubed per Second meters_cubed_per_second m3/s

At present there is no video tutorial on how to create Units.

You can create units while you are creating the Data Stream itself.

There is no separate feature for managing Units. You can only manage, list and modify Units in the Kelvin API or Kelvin API Client (Python)

API cURL Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X "POST" \
"https://<url.kelvin.ai>/api/v4/datastreams/units/create" \
-H "Authorization: Bearer <Your Current Token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "meters_cubed_per_second",
"symbol": "m3/s",
"title": "Meters Cubed per Second"
}'
API Client (Python) Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Units for Data Stream
unit = client.datastreams.create_data_stream_unit(
    data={
        "name": "meters_cubed_per_second",
        "symbol": "m3/s",
        "title": "Meters Cubed per Second"
    }
)

Create all Semantic Types

Kelvin comes with a list of predefined Semantic Types. This covers all the most common types.

If you need to create a new Semantic Type, you can only add them from Kelvin API or Kelvin API Client (Python).

For the Paper Quality Optimization Kelvin SmartApp™, we need to create four extra Semantic Types;

Title Name ID
Brightness brightness
Material Volume Flow Rate material_volume_flow_rate
Luminance Paper luminance_paper
Tank Level tank_level

At present there is no video tutorial on how to create Semantic Types.

You can only create new Semantic Types in Kelvin API and Kelvin API Client (Python)

This is an example for the first Semantic Type. You will need to do the same for each Semantic Type.

API cURL Example
1
2
3
4
5
6
7
8
9
curl -X "POST" \
"https://<url.kelvin.ai>/api/v4/datastreams/semantic-types/create" \
-H "Authorization: Bearer <Your Current Token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "brightness",
"title": "Brightness"
}'
API Client (Python) Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Semantic Types for Data Stream
semantic_data = [
    {
    "name": "brightness",
    "title": "Brightness",
    },
    {
    "name": "material_volume_flow_rate",
    "title": "Material Volume Flow Rate",
    },
    {
    "name": "luminance_paper",
    "title": "Luminance Paper",
    },
    {
    "name": "tank_level",
    "title": "Tank Level",
    }
]

for semantic in semantic_data:
    print(f'Creating Semantic Type : {semantic["name"]} ...')
    sd = client.datastreams.create_data_stream_semantic_type(
        data=semantic
        )

Create Data Streams

Data Stream forms one part of the Asset / Data Stream pair. It is usual to combine common measurement types like Temperature into one Data Stream name.

For the Paper Quality Optimization Kelvin SmartApp™, we need to create sixteen Data Streams;

Title Name ID Description Data Type Name Semantic Type Name Type Unit Name
Paper Substance Weight paper_substance_weight Paper Substance Weight number mass measurement gram
Paper Brightness Top Side paper_substance_weight Paper Brightness Top Side number brightness measurement percent
Luminance Value Top Side luminance_value_top_side Luminance Value Top Side number luminance_paper measurement percent
Luminance Value Bottom Side luminance_value_bottom_side Luminance Value Bottom Side number luminance_paper measurement percent
Exhaust Fan 3 Burner Temperature Set Point exhaust_fan_3_burner_temperature_set_point Exhaust Fan 3 Burner Temperature Set Point number temperature measurement degree_celsius
Paper Machine Speed Set Point paper_machine_speed_set_point Paper Machine Speed Set Point number velocity measurement metre_per_second
Primary Screen Reject Flow Rate Set Point primary_screen_reject_flow_rate_set_point Primary Screen Reject Flow Rate Set Point number material_volume_flow_rate measurement meters_cubed_per_second
Turbo 3 Vacuum Control Output Set Point turbo_3_vacuum_control_output_set_point Turbo 3 Vacuum Control Output Set Point number pressure measurement pascal
Shoe Press Hydration Tank Level shoe_press_hydration_tank_level Shoe Press Hydration Tank Level number tank_level measurement percent
Low Pressure Steam Flow Rate Set Point low_pressure_steam_flow_rate_set_point Low Pressure Steam Flow Rate Set Point number mass_flow_rate measurement kilogram_per_hour
Air Dryer Temperature Set Point air_dryer_temperature_set_point Air Dryer Temperature Set Point number temperature measurement degree_celsius
Jw Ratio Volume Flow jw_ratio_volume_flow Jw Ratio Volume Flow number volume_flow_rate measurement litre_per_second
3P Load Top Side Set Point 3p_load_top_side_set_point 3P Load Top Side Set Point number force measurement newton
Mix Pipe Flow Set Point mix_pipe_flow_set_point Mix Pipe Flow Set Point number volume_flow_rate measurement litre_per_second
Top Dryers Steam Pressure Set Point top_dryers_steam_pressure_set_point Top Dryers Steam Pressure Set Point number pressure measurement bar
Spray Starch Standby Pump Rate Set Point spray_starch_standby_pump_rate_set_point Spray Starch Standby Pump Rate Set Point number volume_flow_rate measurement litre_per_second

This video tutorial only creates a generic Data Stream. You will need to substitute the generic information with the information from the table above.

You can create Data Streams using the bulk import feature. You can find a full tutorial here that uses generic information. Remember to replace the generic information with the information in the table above.

This is an example for creation of all Data Streams with one API request.

API cURL Example
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
curl -X "POST" \
"https://<url.kelvin.ai>/api/v4/datastreams/bulk/create" \
-H "Authorization: Bearer <Your Current Token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"datastreams": [
    {
        "name": "paper_substance_weight",
        "title": "Paper Substance Weight",
        "description": "Paper Substance Weight",
        "type": "measurement",
        "semantic_type_name": "mass",
        "data_type_name": "number",
        "unit_name": "gram"
    },
    {
        "name": "paper_brightness_top_side",
        "title": "Paper Brightness Top Side",
        "description": "Paper Brightness Top Side",
        "type": "measurement",
        "semantic_type_name": "brightness",
        "data_type_name": "number",
        "unit_name": "percent"
    },
    {
        "name": "luminance_value_top_side",
        "description": "Luminance Value Top Side",
        "title": "Luminance Value Top Side",
        "type": "measurement",
        "semantic_type_name": "luminance_paper",
        "data_type_name": "number",
        "unit_name": "percent"
    },
    {
        "name": "luminance_value_bottom_side",
        "title": "Luminance Value Bottom Side",
        "description": "Luminance Value Bottom Side",
        "type": "measurement",
        "semantic_type_name": "luminance_paper",
        "data_type_name": "number",
        "unit_name": "percent"
    },
    {
        "name": "wire_part_vacuum_foil_level_set_point",
        "title": "Wire Part Vacuum Foil Level Set Point",
        "description": "Wire Part Vacuum Foil Level Set Point",
        "type": "measurement",
        "semantic_type_name": "pressure",
        "data_type_name": "number",
        "unit_name": "pascal",
    },
    {
        "name": "exhaust_fan_3_burner_temperature_set_point",
        "title": "Exhaust Fan 3 Burner Temperature Set Point",
        "description": "Exhaust Fan 3 Burner Temperature Set Point",
        "type": "measurement",
        "semantic_type_name": "temperature",
        "data_type_name": "number",
        "unit_name": "degree_celsius",
    },
    {
        "name": "paper_machine_speed_set_point",
        "title": "Paper Machine Speed Set Point",
        "description": "Paper Machine Speed Set Point",
        "type": "measurement",
        "semantic_type_name": "velocity",
        "data_type_name": "number",
        "unit_name": "metre_per_second",
    },
    {
        "name": "primary_screen_reject_flow_rate_set_point",
        "title": "Primary Screen Reject Flow Rate Set Point",
        "description": "Primary Screen Reject Flow Rate Set Point",
        "type": "measurement",
        "semantic_type_name": "material_volume_flow_rate",
        "data_type_name": "number",
        "unit_name": "meters_cubed_per_second",
    },
    {
        "name": "turbo_3_vacuum_control_output_set_point",
        "title": "Turbo 3 Vacuum Control Output Set Point",
        "description": "Turbo 3 Vacuum Control Output Set Point",
        "type": "measurement",
        "semantic_type_name": "pressure",
        "data_type_name": "number",
        "unit_name": "pascal",
    },
    {
        "name": "shoe_press_hydration_tank_level",
        "title": "Shoe Press Hydration Tank Level",
        "description": "Shoe Press Hydration Tank Level",
        "type": "measurement",
        "semantic_type_name": "tank_level",
        "data_type_name": "number",
        "unit_name": "percent",
    },
    {
        "name": "low_pressure_steam_flow_rate_set_point",
        "title": "Low Pressure Steam Flow Rate Set Point",
        "description": "Low Pressure Steam Flow Rate Set Point",
        "type": "measurement",
        "semantic_type_name": "mass_flow_rate",
        "data_type_name": "number",
        "unit_name": "kilogram_per_hour",
    },
    {
        "name": "air_dryer_temperature_set_point",
        "title": "Air Dryer Temperature Set Point",
        "description": "Air Dryer Temperature Set Point",
        "type": "measurement",
        "semantic_type_name": "temperature",
        "data_type_name": "number",
        "unit_name": "degree_celsius",
    },
    {
        "name": "jw_ratio_volume_flow",
        "title": "Jw Ratio Volume Flow",
        "description": "Jw Ratio Volume Flow",
        "type": "measurement",
        "semantic_type_name": "volume_flow_rate",
        "data_type_name": "number",
        "unit_name": "litre_per_second",
    },
    {
        "name": "3p_load_top_side_set_point",
        "title": "3P Load Top Side Set Point",
        "description": "3P Load Top Side Set Point",
        "type": "measurement",
        "semantic_type_name": "force",
        "data_type_name": "number",
        "unit_name": "newton",
    },
    {
        "name": "mix_pipe_flow_set_point",
        "title": "Mix Pipe Flow Set Point",
        "description": "Mix Pipe Flow Set Point",
        "type": "measurement",
        "semantic_type_name": "volume_flow_rate",
        "data_type_name": "number",
        "unit_name": "litre_per_second",
    },
    {
        "name": "top_dryers_steam_pressure_set_point",
        "title": "Top Dryers Steam Pressure Set Point",
        "description": "Top Dryers Steam Pressure Set Point",
        "type": "measurement",
        "semantic_type_name": "pressure",
        "data_type_name": "number",
        "unit_name": "bar",
    },
    {
        "name": "spray_starch_standby_pump_rate_set_point",
        "title": "Spray Starch Standby Pump Rate Set Point",
        "description": "Spray Starch Standby Pump Rate Set Point",
        "type": "measurement",
        "semantic_type_name": "volume_flow_rate",
        "data_type_name": "number",
        "unit_name": "litre_per_second",
    }
]
}'

There is a legacy key primitive_type_name which you need to give even though it is no longer used and will be removed soon. This is now replaced with data_type_name.

API Client (Python) Example
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Data Streams
print("Creating Data Streams ...")
datastream_data={
    "datastreams": [
        {
            "name": "paper_substance_weight",
            "title": "Paper Substance Weight",
            "description": "Paper Substance Weight",
            "type": "measurement",
            "semantic_type_name": "mass",
            "data_type_name": "number",
            "unit_name": "gram"
        },
        {
            "name": "paper_brightness_top_side",
            "title": "Paper Brightness Top Side",
            "description": "Paper Brightness Top Side",
            "type": "measurement",
            "semantic_type_name": "brightness",
            "data_type_name": "number",
            "unit_name": "percent"
        },
        {
            "name": "luminance_value_top_side",
            "description": "Luminance Value Top Side",
            "title": "Luminance Value Top Side",
            "type": "measurement",
            "semantic_type_name": "luminance_paper",
            "data_type_name": "number",
            "unit_name": "percent"
        },
        {
            "name": "luminance_value_bottom_side",
            "title": "Luminance Value Bottom Side",
            "description": "Luminance Value Bottom Side",
            "type": "measurement",
            "semantic_type_name": "luminance_paper",
            "data_type_name": "number",
            "unit_name": "percent"
        },
        {
            "name": "wire_part_vacuum_foil_level_set_point",
            "title": "Wire Part Vacuum Foil Level Set Point",
            "description": "Wire Part Vacuum Foil Level Set Point",
            "type": "measurement",
            "semantic_type_name": "pressure",
            "data_type_name": "number",
            "unit_name": "pascal",
        },
        {
            "name": "exhaust_fan_3_burner_temperature_set_point",
            "title": "Exhaust Fan 3 Burner Temperature Set Point",
            "description": "Exhaust Fan 3 Burner Temperature Set Point",
            "type": "measurement",
            "semantic_type_name": "temperature",
            "data_type_name": "number",
            "unit_name": "degree_celsius",
        },
        {
            "name": "paper_machine_speed_set_point",
            "title": "Paper Machine Speed Set Point",
            "description": "Paper Machine Speed Set Point",
            "type": "measurement",
            "semantic_type_name": "velocity",
            "data_type_name": "number",
            "unit_name": "metre_per_second",
        },
        {
            "name": "primary_screen_reject_flow_rate_set_point",
            "title": "Primary Screen Reject Flow Rate Set Point",
            "description": "Primary Screen Reject Flow Rate Set Point",
            "type": "measurement",
            "semantic_type_name": "material_volume_flow_rate",
            "data_type_name": "number",
            "unit_name": "meters_cubed_per_second",
        },
        {
            "name": "turbo_3_vacuum_control_output_set_point",
            "title": "Turbo 3 Vacuum Control Output Set Point",
            "description": "Turbo 3 Vacuum Control Output Set Point",
            "type": "measurement",
            "semantic_type_name": "pressure",
            "data_type_name": "number",
            "unit_name": "pascal",
        },
        {
            "name": "shoe_press_hydration_tank_level",
            "title": "Shoe Press Hydration Tank Level",
            "description": "Shoe Press Hydration Tank Level",
            "type": "measurement",
            "semantic_type_name": "tank_level",
            "data_type_name": "number",
            "unit_name": "percent",
        },
        {
            "name": "low_pressure_steam_flow_rate_set_point",
            "title": "Low Pressure Steam Flow Rate Set Point",
            "description": "Low Pressure Steam Flow Rate Set Point",
            "type": "measurement",
            "semantic_type_name": "mass_flow_rate",
            "data_type_name": "number",
            "unit_name": "kilogram_per_hour",
        },
        {
            "name": "air_dryer_temperature_set_point",
            "title": "Air Dryer Temperature Set Point",
            "description": "Air Dryer Temperature Set Point",
            "type": "measurement",
            "semantic_type_name": "temperature",
            "data_type_name": "number",
            "unit_name": "degree_celsius",
        },
        {
            "name": "jw_ratio_volume_flow",
            "title": "Jw Ratio Volume Flow",
            "description": "Jw Ratio Volume Flow",
            "type": "measurement",
            "semantic_type_name": "volume_flow_rate",
            "data_type_name": "number",
            "unit_name": "litre_per_second",
        },
        {
            "name": "3p_load_top_side_set_point",
            "title": "3P Load Top Side Set Point",
            "description": "3P Load Top Side Set Point",
            "type": "measurement",
            "semantic_type_name": "force",
            "data_type_name": "number",
            "unit_name": "newton",
        },
        {
            "name": "mix_pipe_flow_set_point",
            "title": "Mix Pipe Flow Set Point",
            "description": "Mix Pipe Flow Set Point",
            "type": "measurement",
            "semantic_type_name": "volume_flow_rate",
            "data_type_name": "number",
            "unit_name": "litre_per_second",
        },
        {
            "name": "top_dryers_steam_pressure_set_point",
            "title": "Top Dryers Steam Pressure Set Point",
            "description": "Top Dryers Steam Pressure Set Point",
            "type": "measurement",
            "semantic_type_name": "pressure",
            "data_type_name": "number",
            "unit_name": "bar",
        },
        {
            "name": "spray_starch_standby_pump_rate_set_point",
            "title": "Spray Starch Standby Pump Rate Set Point",
            "description": "Spray Starch Standby Pump Rate Set Point",
            "type": "measurement",
            "semantic_type_name": "volume_flow_rate",
            "data_type_name": "number",
            "unit_name": "litre_per_second",
        }
    ]
}

print('Creating all Data Streams')
ds = client.datastreams.create_bulk_data_stream(
    data=datastream_data
)

Create Asset Types

Before you can create the Assets, you need to have valid Asset Types..

If you need to create a new semantic types, you can only add them from Kelvin API or Kelvin API Client (Python).

For the Paper Quality Optimization Kelvin SmartApp™, we need to create four extra semantic types;

Title Name ID
Machine Learning Type machine_learning_type

This video tutorial only creates a generic Asset Type. You will need to substitute the generic information with the information from the table above.

You can create Asset Types by following the documentation here that uses generic information. Remember to replace the generic information with the information in the table above.

API cURL Example
1
2
3
4
5
6
7
8
9
curl -X "POST" \
"https://<url.kelvin.ai>/api/v4/assets/types/create" \
-H "Authorization: Bearer <Your Current Token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "machine_learning_type",
"title": "Machine Learning Type"
}'
API Client (Python) Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Asset Type
print("Creating Asset Type ...")
asset_type = client.asset.create_asset_type(data={
    "name": "machine_learning_type",
    "title": "Machine Learning Type"
})

Create Assets

Assets forms one part of the Asset / Data Stream pair. Each Asset must have a unique name.

For the Paper Quality Optimization Kelvin SmartApp™, let's create eight Assets that represent each production line for paper.

Title Name ID Asset Type Properties
Press Line 01 press_line_01 machine_learning_type None
Press Line 02 press_line_02 machine_learning_type None
Press Line 03 press_line_03 machine_learning_type None
Press Line 04 press_line_04 machine_learning_type None
Press Line 05 press_line_05 machine_learning_type None
Press Line 06 press_line_06 machine_learning_type None
Press Line 07 press_line_07 machine_learning_type None
Press Line 08 press_line_08 machine_learning_type None

This video tutorial only creates a generic Asset. You will need to substitute the generic information with the information from the table above.

You can create Assets by following the documentation here that uses generic information. Remember to replace the generic information with the information in the table above.

API cURL Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
curl -X "POST" \
  "https://<url.kelvin.ai>/api/v4/assets/bulk/create?dry_run=true" \
  -H "Authorization: Bearer <Your Current Token>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
  "assets": [
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_01",
      "properties": [
      ],
      "title": "Press Line 01"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_02",
      "properties": [
      ],
      "title": "Press Line 03"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_03",
      "properties": [
      ],
      "title": "Press Line 03"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_04",
      "properties": [
      ],
      "title": "Press Line 04"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_05",
      "properties": [
      ],
      "title": "Press Line 05"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_06",
      "properties": [
      ],
      "title": "Press Line 06"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_07",
      "properties": [
      ],
      "title": "Press Line 07"
    },
    {
      "asset_type_name": "machine_learning_type",
      "name": "press_line_08",
      "properties": [
      ],
      "title": "Press Line 08"
    }
  ]
}'
API Client (Python) Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Asset
asset_data={
    "assets": [
        {
            "name": "press_line_01",
            "title": "Press Line 01",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_02",
            "title": "Press Line 02",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_03",
            "title": "Press Line 03",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_04",
            "title": "Press Line 04",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_05",
            "title": "Press Line 05",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_06",
            "title": "Press Line 06",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_07",
            "title": "Press Line 07",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        },
        {
            "name": "press_line_08",
            "title": "Press Line 08",
            "asset_type_name": "machine_learning_type",
            "properties": [],
        }
    ]
}

print('Creating all Assets')
client.asset.create_asset_bulk(
    dry_run=False, 
    data=asset_data
)

Create Cluster

Before you can create the Cluster, you need to have a bare-metal computer ready with Ubuntu Server installed. Alternatively instead of a physical computer you can use a virtual machine hosted in the Cloud.

For the Paper Quality Optimization Kelvin SmartApp™, we need to create one K3s Cluster.

You can use your own Kubernetes Cluster if you wish. The method is slightly different and you can find documentation at these links for Kubernetes, AWS EKS and Azure AKS.

Title Name ID Type
Sales Cluster Online sales-01-cluster K3S

This video tutorial only creates a generic Cluster. You will need to substitute the generic information with the information from the table above.

You can create Clusters by following the documentation here that uses generic information. Remember to replace the generic information with the information in the table above.

API cURL Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X "POST" \
"https://<url.kelvin.ai>/api/v4/orchestration/clusters/create" \
-H "Authorization: Bearer <Your Current Token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "sales-01-cluster",
"title": "Sales Cluster Online",
"type": "k3s"
}'

You will get a response which will have the provision script that you will need to use in the terminal of the Cluster computer or virtual machine.

If you do not use the --service-account option, then you will need to enter your login credentials and the name of the Cluster to link it to the new Cluster you have created in the Platform.

{
  "name": "sales-01-cluster",
  "title": "Sales Cluster Online",
  "type": "k3s",
  "ready": false,
  "status": null,
  "last_seen": null,
  "sync_scrape_interval": 30,
  "manifests_scrape_interval": 86400,
  "manifests_scrape_enabled": true,
  "version": {
    "k8s_version": "",
    "kelvin_version": ""
  },
  "created": "2024-04-09T01:11:58.09001Z",
  "updated": "2024-04-09T01:11:58.09001Z",
  "service_account_token": "bm9kZS1jbGllbnQtYmxhaGJsYWg6S2k4bGJIaGdqd2tXOTdReDZ1MGUzTnp0MnlNWHA1UmM=",
  "provision_script": "bash <(curl -sfS https://<url.kelvin.ai>/provision) --service-account bm9kZS1jbGllbnQtYmxhaGJsYWg6S2k4bGJIaGdqd2tXOTdReDZ1MGUzTnp0MnlNWHA1UmM=",
  "join_script": "bash <(curl -sfS https://<url.kelvin.ai>/provision) --join --ip <main_cluster_node_ip_address> --token ",
  "telemetry_scrape_interval": 30,
  "telemetry_enabled": true,
  "telemetry_buffer_size": 5,
  "forward_logs_enabled": true,
  "forward_logs_buffer_size": 5,
  "upgrade_status": {
    "state": "",
    "message": ""
  },
  "upgrade_pre_download": false,
  "upgrade_instantly_apply": true
}
API Client (Python) Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Cluster
print("Creating Cluster ...")
client.orchestration.create_orchestration_clusters(data={
    "name":"sales-01-cluster",
    "title":"Sales Cluster Online", 
    "type":"k3s"
})
provision_script = response.provision_script
print(f'Enter this command into the terminal of the target cluster computer: {provision_script}')

Copy the terminal command, then paste or type it into your computer or virtual machine's terminal to initiate the cluster installation.

If you do not use the --service-account option, then you will need to enter your login credentials and the name of the Cluster to link it to the new Cluster you have created in the Platform.

Create Connections

Finally you can create the Connection that will pull the data from the Asset itself.

As we don't have any physical Assets to collect data, we created a simulation OPC UA server which has been deployed to the server which acts as the Asset and delivers a stream of data through the OPC UA Server.

You can download the csv file of data that we use for the data stream here.

You can also deploy a CSV Publisher using the CSV file. In this case you do not need to add a connection, though you will also not learn how to deploy an OPC UA Connection, which is the intention of this tutorial.

For the Paper Quality Optimization Kelvin SmartApp™, we need to create one connection with with seventeen metic maps.

The connection information is;

Title Name ID Cluster Name Protocol App Version Authentication Connection
Paper Quality Optimization Connection pqo-opcua-connection sales-01-cluster opc-ua 3.4.0 None opc.tcp://docs-simulation-data-opcua.app:4840

The metrics map information is;

Metric Maps Data Stream Name Metric Maps Node ID
paper_substance_weight ns=2;i=15
3p_load_top_side_set_point ns=2;i=11
air_dryer_temperature_set_point ns=2;i=9
exhaust_fan_3_burner_temperature_set_point ns=2;i=3
jw_ratio_volume_flow ns=2;i=10
low_pressure_steam_flow_rate_set_point ns=2;i=8
luminance_value_bottom_side ns=2;i=18
luminance_value_top_side ns=2;i=17
mix_pipe_flow_set_point ns=2;i=12
paper_brightness_top_side ns=2;i=16
paper_machine_speed_set_point ns=2;i=4
primary_screen_reject_flow_rate_set_point ns=2;i=5
shoe_press_hydration_tank_level ns=2;i=7
spray_starch_standby_pump_rate_set_point ns=2;i=14
top_dryers_steam_pressure_set_point ns=2;i=13
turbo_3_vacuum_control_output_set_point ns=2;i=6
wire_part_vacuum_foil_level_set_point ns=2;i=2

For the Metrics Maps, the following remains the same for all;

Key Value
access "RW"
polling_rate 1
scale_multiplier None
data_type number

This video tutorial only creates a generic OPC UA connection. You will need to substitute the generic information with the information from the table above.

You can create Connections to an OPC UA Server by following the documentation here that uses generic information. Remember to replace the generic information with the information in the table above.docker com

This is an example for the first Semantic Type. You will need to do the same for each Semantic Type.

API cURL Example
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
curl -X "POST" \
  "https://<url.kelvin.ai>/api/v4/bridges/deploy" \
  -H "Authorization: Bearer <Your Current Token>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
   "name":"pqo-opcua-bridge",
   "title":"Paper Quality Optimization Bridge",
   "cluster_name":"sales-01-cluster",
   "workload_name":"pqo-opcua-bridge",
   "app_name":"kelvin-bridge-opcua-client",
   "app_version":"3.4.4",
   "payload":{
      "configuration":{
         "authentication":{
            "type":"none"
         },
         "connection":{
            "endpoint_url":"opc.tcp://docs-simulation-data-opcua.app:4840",
            "message_security":"none",
            "security_policy":"none",
            "verify_ssl":false
         }
      },
      "logging_level":"INFO",
      "metrics_map":[
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=15",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"paper_substance_weight"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=11",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"3p_load_top_side_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=9",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"air_dryer_temperature_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=3",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"exhaust_fan_3_burner_temperature_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=10",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"jw_ratio_volume_flow"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=8",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"low_pressure_steam_flow_rate_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=18",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"luminance_value_bottom_side"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=17",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"luminance_value_top_side"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=12",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"mix_pipe_flow_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=16",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"paper_brightness_top_side"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=4",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"paper_machine_speed_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=5",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"primary_screen_reject_flow_rate_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=7",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"shoe_press_hydration_tank_level"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=14",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"spray_starch_standby_pump_rate_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=13",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"top_dryers_steam_pressure_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=6",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"turbo_3_vacuum_control_output_set_point"
         },
         {
            "access":"RW",
            "asset_name":"press_line_01",
            "configuration":{
               "node":"ns=2;i=2",
               "polling_rate":1,
               "scale_multiplier":"None"
            },
            "data_type":"raw.float64",
            "name":"wire_part_vacuum_foil_level_set_point"
         }
      ]
   }
}'

In this Python script we create the full connection information for the first Asset, then replicate the Data Stream connections for all eight Assets that we have created earlier.

API Client (Python) Example
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
from kelvin.api.client import Client

# Login
client = Client(config={"url": "https://<url.kelvin.ai>", "username": "<your_username>"})
client.login(password="<your_password>")

# Create Bridge
data_request = {
    "name": "pqo-opcua-connection",
    "title": "Paper Quality Optimization Connection",
    "cluster_name": "sales-01-cluster",
    "app_name": "kelvin-bridge-opcua-client",
    "app_version": "3.4.0",
    "payload": {
            "configuration": {
            "authentication": {
                "type": "none"
            },
            "connection": {
                "endpoint_url": "opc.tcp://docs-simulation-data-opcua.app:4840",
                "message_security": "none",
                "security_policy": "none",
                "verify_ssl": False
            },
            "read_timeout": 10,
            "write_timeout": 10
            },
            "logging_level": "DEBUG",
            "metrics_map": [
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=15",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "paper_substance_weight"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=11",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "3p_load_top_side_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=9",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "air_dryer_temperature_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=3",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "exhaust_fan_3_burner_temperature_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=10",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "jw_ratio_volume_flow"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=8",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "low_pressure_steam_flow_rate_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=18",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "luminance_value_bottom_side"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=17",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "luminance_value_top_side"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=12",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "mix_pipe_flow_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=16",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "paper_brightness_top_side"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=4",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "paper_machine_speed_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=5",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "primary_screen_reject_flow_rate_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=7",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "shoe_press_hydration_tank_level"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=14",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "spray_starch_standby_pump_rate_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=13",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "top_dryers_steam_pressure_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=6",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "turbo_3_vacuum_control_output_set_point"
                },
                {
                    "access": "RW",
                    "asset_name": "press_line_01",
                    "configuration": {
                    "node": "ns=2;i=2",
                    "polling_rate": 1,
                    "scale_multiplier": None
                    },
                    "data_type": "raw.float64",
                    "name": "wire_part_vacuum_foil_level_set_point"
                }
            ]
        }
    }

# Add as many Assets as you want
for i in range(2, 8):
    press_line = f"press_line_0{i}"
    for metric in data_request["payload"]["metrics_map"][:]:
        if metric["asset_name"] == "press_line_01":
            new_metric = metric.copy()
            new_metric["asset_name"] = press_line
            data_request["payload"]["metrics_map"].append(new_metric)

print("Creating Bridge ...")
client.bridge.deploy_bridge(
    data = data_request
)

Conclusion

In this part of the Multi-Objective Optimization with Machine Learning, we have shown you how to setup the Kelvin Infrastructure so that the Developers and Operations Engineers can do their tasks smoothly.

You can also checkout the other tutorials related to this one;

  • Multi-Objective Optimization with Machine Learning Overview : A fast run through from creating the Kelvin SmartApp™, upload to the App Registry, deployment to the edge and managing Recommendations.
  • Multi-Objective Optimization with Machine Learning for Developers : A detailed step-by-step process to go from a jupyter Notebook concept machine learning model to a fully tested Kelvin SmartApp™ ready for upload to the Cloud.
  • Multi-Objective Optimization with Machine Learning for Operations : A detailed step-by-step process to Add Assets to Kelvin SmartApps™, monitor Kelvin SmartApps™ and Asset performance and respond to RecommendatioKelvin SmartApps™ Kelvin SmartApps™.