Skip to content

Commit 4f7fab5

Browse files
committed
constrained-hmm: Dataset troubles...
1 parent e5b10a7 commit 4f7fab5

File tree

3 files changed

+214
-55
lines changed

3 files changed

+214
-55
lines changed

handson/constrained-hmm/CNCMillSmartMichicanExplore.ipynb

Lines changed: 160 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,38 @@
9494
"id": "1672296a-1751-4fd5-a2cb-31ba03cba26d",
9595
"metadata": {},
9696
"outputs": [],
97-
"source": []
97+
"source": [
98+
"\n",
99+
"# Add experiment info to sensor data, for ease of analysis\n",
100+
"enrich = pandas.merge(data.reset_index(), experiments, left_on='experiment', right_index=True).set_index(['experiment', 'time'])\n",
101+
"enrich.head()\n",
102+
"\n"
103+
]
104+
},
105+
{
106+
"cell_type": "code",
107+
"execution_count": null,
108+
"id": "b133f8fb-7f6e-4e9a-9855-2861246d994d",
109+
"metadata": {},
110+
"outputs": [],
111+
"source": [
112+
"power_columns = list(enrich.columns[enrich.columns.str.contains('Power')])\n",
113+
"def p99(s):\n",
114+
" return s.quantile(0.99)\n",
115+
"\n",
116+
"power_stats = enrich[power_columns].agg(['min', 'max', 'median', p99])\n",
117+
"print(power_stats)\n",
118+
"\n",
119+
"for c in power_columns:\n",
120+
" s = numpy.maximum(enrich[c], 0.0)\n",
121+
" s = s / s.quantile(0.99)\n",
122+
" s = numpy.minimum(s, 1.0)\n",
123+
" enrich[c+'_Scaled'] = s\n",
124+
"\n",
125+
"scaled_power_columns = [ c+'_Scaled' for c in power_columns ]\n",
126+
"power_stats = enrich[scaled_power_columns].agg(['min', 'max', 'median', p99])\n",
127+
"power_stats"
128+
]
98129
},
99130
{
100131
"cell_type": "code",
@@ -186,24 +217,11 @@
186217
]
187218
},
188219
{
189-
"cell_type": "code",
190-
"execution_count": null,
191-
"id": "7f333d84-042a-4a68-a2f3-5cff5b05fc89",
192-
"metadata": {},
193-
"outputs": [],
194-
"source": [
195-
"enrich = pandas.merge(data.reset_index(), experiments, left_on='experiment', right_index=True).set_index(['experiment', 'time'])\n",
196-
"enrich.head()"
197-
]
198-
},
199-
{
200-
"cell_type": "code",
201-
"execution_count": null,
202-
"id": "35c99d2f-e049-437c-8842-6b10df5b15e2",
220+
"cell_type": "markdown",
221+
"id": "cdc83bdd-f65f-46cd-9ab8-a0a8cda9af72",
203222
"metadata": {},
204-
"outputs": [],
205223
"source": [
206-
"plotly.express.histogram(enrich.reset_index().sort_values('feedrate'), x='S1_OutputPower', facet_col='feedrate', color='experiment')"
224+
"## Spindle power vs different conditions"
207225
]
208226
},
209227
{
@@ -214,10 +232,7 @@
214232
"outputs": [],
215233
"source": [
216234
"spindleactive = enrich[enrich['S1_OutputPower'] > 0.03]\n",
217-
"import seaborn\n",
218-
"\n",
219-
"seaborn.displot(kind='kde', data=spindleactive, x='S1_OutputPower', hue='Machining_Process', row='feedrate', clip=(0.1, 0.250), aspect=2.0, height=4.0, common_norm=False)\n",
220-
"#(spindleactive.reset_index().sort_values('Machining_Process'), x='S1_OutputPower', color='Machining_Process')\n"
235+
"import seaborn"
221236
]
222237
},
223238
{
@@ -257,65 +272,76 @@
257272
{
258273
"cell_type": "code",
259274
"execution_count": null,
260-
"id": "88c01b43-c535-41ac-a97f-a189e456fe90",
275+
"id": "f9af841d-6ebb-454f-8fce-cbea5f8e2981",
261276
"metadata": {},
262277
"outputs": [],
263278
"source": [
264-
"experiments"
279+
"seaborn.displot(kind='kde', data=spindleactive, x='S1_OutputPower', hue='clamp_pressure', clip=(0.1, 0.250), aspect=2.0, height=4.0, common_norm=False)\n",
280+
"#(spindleactive.reset_index().sort_values('Machining_Process'), x='S1_OutputPower', color='Machining_Process')\n"
265281
]
266282
},
267283
{
268284
"cell_type": "code",
269285
"execution_count": null,
270-
"id": "03a06816-37c6-4229-b864-46b2d65bf500",
286+
"id": "206f74c7-f714-4871-bd8e-3feba4453fa8",
271287
"metadata": {},
272288
"outputs": [],
273289
"source": [
274-
"experiments.groupby('feedrate')['machining_finalized'].value_counts().reset_index()"
290+
"seaborn.displot(kind='kde', data=spindleactive, x='S1_OutputPower', hue='Machining_Process', row='feedrate', clip=(0.1, 0.250), aspect=2.0, height=2.0, common_norm=False)\n",
291+
"#(spindleactive.reset_index().sort_values('Machining_Process'), x='S1_OutputPower', color='Machining_Process')\n"
292+
]
293+
},
294+
{
295+
"cell_type": "markdown",
296+
"id": "c2e86bd4-0376-46be-a971-45c11a63eea1",
297+
"metadata": {},
298+
"source": [
299+
"## Time-series view"
275300
]
276301
},
277302
{
278303
"cell_type": "code",
279304
"execution_count": null,
280-
"id": "18caa430-ed15-4e3a-af56-3a19ac213afa",
305+
"id": "a90745b1-f9a3-4287-8640-f782fe6c2528",
281306
"metadata": {},
282307
"outputs": [],
283308
"source": [
284-
"experiments.groupby('clamp_pressure')['machining_finalized'].value_counts().reset_index()"
309+
"# TODO: show the different labeled sections in Machining_Process column\n",
310+
"# TODO: normalize powers for all axes, and plot together\n",
311+
"plot_timeseries(data, y_column='Y1_OutputPower')"
285312
]
286313
},
287314
{
288315
"cell_type": "code",
289316
"execution_count": null,
290-
"id": "05db06c0-fa0f-43cb-b3bd-76ea38629327",
317+
"id": "be42389a-b4a1-4721-adad-08b701f07330",
291318
"metadata": {},
292319
"outputs": [],
293320
"source": [
294-
"\n",
295-
"seaborn.displot(kind='kde', data=spindleactive, x='S1_OutputPower', hue='clamp_pressure', clip=(0.1, 0.250), aspect=2.0, height=4.0, common_norm=False)\n",
296-
"#(spindleactive.reset_index().sort_values('Machining_Process'), x='S1_OutputPower', color='Machining_Process')\n"
321+
"for column in scaled_power_columns:\n",
322+
" \n",
323+
" seaborn.displot(data=enrich.reset_index(), kind='kde', x=column, hue='feedrate', height=2.0, aspect=2.0)"
297324
]
298325
},
299326
{
300327
"cell_type": "code",
301328
"execution_count": null,
302-
"id": "4e44b6c4-2617-48ff-b44b-ef23886a9411",
329+
"id": "3a97c242-8b3d-4e26-bbbe-6211ff5b4975",
303330
"metadata": {},
304331
"outputs": [],
305332
"source": [
306-
"plotly.express.histogram(spindleactive.reset_index().sort_values('feedrate'), x='S1_OutputPower', facet_col='feedrate', color='experiment')"
333+
"spindle_active = enrich[enrich['S1_OutputPower'] > 0.01]\n",
334+
"seaborn.pairplot(data=spindle_active.reset_index(), vars=scaled_power_columns, hue='feedrate', height=3.6, aspect=1.5, diag_kws=dict(common_norm=False))"
307335
]
308336
},
309337
{
310338
"cell_type": "code",
311339
"execution_count": null,
312-
"id": "a90745b1-f9a3-4287-8640-f782fe6c2528",
340+
"id": "8471ed38-36a9-4938-8326-575a0e887ca8",
313341
"metadata": {},
314342
"outputs": [],
315343
"source": [
316-
"# TODO: show the different labeled sections in Machining_Process column\n",
317-
"# TODO: normalize powers for all axes, and plot together\n",
318-
"plot_timeseries(data, y_column='Y1_OutputPower')"
344+
"seaborn.pairplot(data=spindle_active.reset_index(), vars=scaled_power_columns, hue='machining_finalized', height=3.6, aspect=1.5, diag_kws=dict(common_norm=False))"
319345
]
320346
},
321347
{
@@ -327,30 +353,113 @@
327353
},
328354
"outputs": [],
329355
"source": [
330-
"# TODO: plot time-series\n",
331-
"\n",
332356
"\n",
333-
"def plot_timeseries(data, y_column = 'S1_OutputPower', x_column = 'time'):\n",
334357
"\n",
358+
"def plot_timeseries(data, y, time_column = 'time', row_column='experiment', row_order=None):\n",
359+
" import plotly.graph_objects as go\n",
360+
" \n",
335361
" data = data.reset_index()\n",
336-
" data['time'] = data['time'] / pandas.Timedelta('1sec')\n",
362+
" # convert to seconds, Plotly default time markers are bad with Timedelta\n",
363+
" data[time_column] = data[time_column] / pandas.Timedelta('1sec')\n",
364+
"\n",
365+
" x_range = data[time_column].min(), data[time_column].max()\n",
366+
"\n",
367+
" if row_order is None:\n",
368+
" row_order = sorted(list(data[row_column].unique()))\n",
369+
" else:\n",
370+
" row_order = list(row_order)\n",
337371
" \n",
338-
" #traces = []\n",
339-
" #titles = []\n",
340-
" for experiment, d in data.groupby('experiment'):\n",
372+
" for experiment in row_order:\n",
373+
" df = data[data[row_column] == experiment]\n",
374+
" df = df.sort_values(time_column) # plotly lines connect badly without sorting by time\n",
375+
" \n",
341376
" ex = experiments.loc[experiment]\n",
342377
" describe = f\"Ex {experiment}: f={ex['feedrate']} c={ex['clamp_pressure']} t={ex['tool_condition']} f={ex['machining_finalized']} p={ex['passed_visual_inspection']}\"\n",
378+
"\n",
379+
" fig = go.Figure()\n",
380+
" fig.update_layout(title=describe, xaxis=dict(range=x_range))\n",
381+
" for column in y:\n",
382+
" fig.add_trace(go.Scatter(x=df[time_column], y=df[column], name=column))\n",
343383
" \n",
344-
" fig = plotly.express.scatter(d, x=x_column, y=y_column, title=describe)\n",
345384
" fig.show()\n",
346-
" \n",
347-
" #print(ex) \n",
348-
" #traces.append(trace)\n",
349-
" #titles.append(describe)\n",
350385
"\n",
351-
" \n",
386+
"plot_timeseries(enrich.sort_values(['feedrate']), y=scaled_power_columns)"
387+
]
388+
},
389+
{
390+
"cell_type": "code",
391+
"execution_count": null,
392+
"id": "876f7376-af98-411a-af20-966a444ca9d0",
393+
"metadata": {},
394+
"outputs": [],
395+
"source": [
396+
"exx = experiments.sort_values(['feedrate', 'clamp_pressure', 'tool_condition'])\n",
397+
"exx"
398+
]
399+
},
400+
{
401+
"cell_type": "code",
402+
"execution_count": null,
403+
"id": "fa8665f0-0767-4e5b-8f35-b99c83eae49c",
404+
"metadata": {},
405+
"outputs": [],
406+
"source": [
352407
"\n",
353-
"plot_timeseries(data)"
408+
"plot_timeseries(data.reset_index(), y=['Y1_ActualPosition', 'X1_ActualPosition'], row_order=exx.index)\n"
409+
]
410+
},
411+
{
412+
"cell_type": "code",
413+
"execution_count": null,
414+
"id": "26499011-f015-47a4-ad9c-a6e23e4b0c1b",
415+
"metadata": {},
416+
"outputs": [],
417+
"source": [
418+
"plot_timeseries(enrich.sort_values(['feedrate']), y=['M1_CURRENT_FEEDRATE', 'S1_CurrentFeedback'])"
419+
]
420+
},
421+
{
422+
"cell_type": "code",
423+
"execution_count": null,
424+
"id": "9deb06d4-5327-440b-9f5d-826ce9357a15",
425+
"metadata": {},
426+
"outputs": [],
427+
"source": [
428+
"plot_timeseries(enrich.sort_values(['feedrate']), y=['M1_CURRENT_FEEDRATE', 'S1_OutputPower_Scaled'])\n"
429+
]
430+
},
431+
{
432+
"cell_type": "code",
433+
"execution_count": null,
434+
"id": "e5973658-2893-4a56-8cd1-fdd92d57ac0b",
435+
"metadata": {},
436+
"outputs": [],
437+
"source": [
438+
"p = enrich.sort_values(['feedrate'])\n",
439+
"p['S1_CommandVelocity_Scaled'] = p['S1_CommandVelocity'] / 50.0\n",
440+
"p['S1_Power_Calc'] = p['S1_OutputCurrent'] * p['S1_OutputVoltage']\n",
441+
"#plot_timeseries(p, y=['S1_CommandVelocity_Scaled', 'S1_OutputPower_Scaled'])\n",
442+
"plot_timeseries(p, y=['S1_OutputCurrent', 'S1_OutputPower'])"
443+
]
444+
},
445+
{
446+
"cell_type": "code",
447+
"execution_count": null,
448+
"id": "4f1d8383-3180-49e7-b2be-bfef897be023",
449+
"metadata": {},
450+
"outputs": [],
451+
"source": [
452+
"scaled_power_columns"
453+
]
454+
},
455+
{
456+
"cell_type": "code",
457+
"execution_count": null,
458+
"id": "b756b69a-5b3f-4c5a-b040-24356cae483c",
459+
"metadata": {},
460+
"outputs": [],
461+
"source": [
462+
"data['S1_CommandVelocity'].hist()"
354463
]
355464
},
356465
{

handson/constrained-hmm/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Some real-world examples are referenced at the bottom of this page.
2626

2727
- Provide an example on real data.
2828
For example fitting a repeated sequential (cyclic) process, such as those found in automation/manufacturing.
29-
The CNC Mill Tool Wear looks highly relevant.
3029
Alternative with sound could be MMII dataset?
3130

3231
## Implementation

0 commit comments

Comments
 (0)