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;
- Infrastructure Overview to understand how Kelvin is technically setup for hardware, software components and locations.
- How data is defined and stored in Kelvin
Infrastructure Overview
The infrastructure of Kelvin can be split into three main areas;
- Users - The people or services that connect to Kelvin either through the Kelvin UI, Kelvin API or Kelvin SDK.
- Cloud - The core system services and storage components of Kelvin that reside in the Cloud.
- 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)
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"
}'
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.
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"
}'
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.
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.
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.
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"
}'
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.
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"
}
]
}'
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.
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
}
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. csv file of data that we use for the data stream here You can download the .
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.
This is an example for the first Semantic Type. You will need to do the same for each Semantic Type.
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.
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™.