使用 C# 创建 SPC CP 和 CPK 图表






4.90/5 (23投票s)
使用 C# 创建一个简单的 SP、CP 和 CPK 图表
引言
在本文中,我们可以详细了解如何创建一个简单的SPC(统计过程控制)CP、CPK 图表。
我一直在从事几个自动化项目。如今,汽车行业对自动化测量设备感兴趣,以确保质量并在全球产业中竞争。任何自动化软件的主要部分都是获得准确的质量检查结果,为此,我们使用 SPC(统计过程控制)来查找质量结果。
您还可以查看我之前与工厂自动化相关的文章,例如:
能力是获取任何零件的连续数据,并将结果与Cp、Cpk、Pp 和 Ppk 进行比较。其中Cp、Cpk 是过程能力,Pp、Ppk 是过程性能。让我们以我一个正在制造自动化测量机器中使用的实际项目为例。现在我们以汽车发动机的凸轮轴、曲轴或任何零件为例,这些零件需要进行质量控制检查。检查零件将使用某种传感器进行测量,例如,在我们的项目中,我们使用数字传感器测量凸轮轴和曲轴。使用传感器,我们获得连续数据,并将实时数据与 SPC Cp、Cpk、Pp 和 Ppk 图表进行检查。将最终输出显示给操作员和质量控制工程师。这是我的实际项目屏幕。所有测量数据均已从数字传感器接收。
我隐藏了 SPC Cp、Cpk、Pp 和 Ppk 图表,因为当时我们使用了一些第三方图表控件。市面上的 SPC 图表控件很少,而且没有免费的 SPC Cp、Cpk、Pp 和 Ppk 图表。我想创建一个简单的 SPC Cp、Cpk、Pp 和 Ppk 图表。结果,在对 SPC 及其所有功能进行了长时间研究后,我创建了一个简单的 SPC Cp、Cpk、Pp 和 Ppk 图表。希望大家都会喜欢。
我将 SPC 图表创建为用户控件。您只需将我的用户控件 DLL 添加到项目中即可使用。
Shanu CpCpkChart 用户控件特性
- 显示带有总和、均值和极差值的图表
- Cp、Cpk、Pp、Ppk 带有警报结果文本,NG(不合格)为红色,OK(合格)为绿色
- 均值 (XBAR) 和极差 (RBAR) 图表,带有警报图像,NG 为红色,OK 为绿色
- 自动计算并显示 XBAR 和 RBAR 值的上限 (UCL)、下限 (LCL)
- 将图表保存为图像(注意:要保存图表图像,请双击图表或右键单击并选择“另存为图像”)。
- 实时数据收集并在图表中显示
- 用户可以添加图表水印文本
首先,我们来看看 Cp
和 Cpk
是什么。
Cp 和 Cpk -> 过程能力
- Cp - 衡量数据在规格限(USL、LSL)内拟合得如何
- Cpk - 衡量数据在规格限之间的中心度
Cp、Cpk 公式
Cp=(USL-LSL/6SIGMA) -> USL-LSL/6*RBAR/d2
Cpk=min(USL-XBAR/3Sigma,LSL-XBAR/3Sigma)
Pp 和 Ppk -> 过程性能
- Pp - 衡量数据在规格限(USL、LSL)内拟合得如何
- Ppk - 衡量数据在规格限之间的中心度
Pp、Ppk 公式
Pp=(USL-LSL/6SIGMA) -> USL-LSL/6 STDEV
Ppk=min(USL-XBAR/3STDEV,LSL-XBAR/3STDEV)
参考网站
- CP、CPK、PP 和 PPK:了解如何以及何时使用它们
- 过程能力指数 - Cp 与 Cpk 可视动画
- Cp Cpk 公式与 Pp Ppk 公式
- Cpk
- CP、CPK、PP 和 PPK:了解如何以及何时使用它们
- 如何计算 Cp 和 Cpk
- 什么是过程能力?
现在我们来看看我是如何创建一个 SPC Cp、Cpk 图表的。我的主要目标是制作一个非常简单的 SPC 图表,供最终用户使用。
我已将 SPC Cp、Cpk 图表创建为用户控件,以便在所有项目中都能轻松使用。
我附加了一个名为 ShanuSPCCpCpkChart.zip 的 zip 文件,您可以在本文顶部的链接处下载。它包含我的 SPC 图表用户控件源代码和一个演示程序。
- "ShanuCPCPKChart.dll":您可以在项目中使用此用户控件,并将数据作为
DataTable
传递给userControl
。
在用户控件中,我将获取DataTable
值并计算结果以显示为:
Sum
平均
Range
Cp
Cpk
Pp
Ppk
将所有结果绑定到带有笑脸警报图像的图表中。如果数据良好,则显示绿色笑脸;如果数据为 NG(不合格)。使用均值和极差值,我将绘制均值和极差图表以及警报图像。
用户可以向 ShanuCPCPKChart
用户控件传递 USL(上规格限)、LSL(下规格限)Cpk 限值。使用 USL、LSL 和 Cpk 值,将计算结果并显示相应的警报,如果 Cp、Cpk、Pp、Ppk 值不合格则为红色。如果结果良好,则在图表中为 Cp、Cpk、Pp、Ppk 显示绿色文本。
"ShanuSPCCpCPK_Demo" 文件夹 - 此文件夹包含演示程序,其中包括带有随机数据样本的 ShanuCPCPKChart
用户控件。
注意:我使用 DataTable
作为用户控件的数据输入。从 Windows 窗体,我们需要将 DataTable
传递给用户控件,以绘制 Cp、Cpk、Pp 和 Ppk 结果与 SPC 极差图。
保存图表 用户可以通过双击图表控件或右键单击图表并选择保存来保存图表。
Using the Code
我使用的是 Visual Studio 2010。
1) SPC 用户控件程序
首先,我们将从用户控件开始。要创建用户控件,
- 创建一个新的 Windows 控件库项目。
- 设置项目名称并点击确定(此处,我的用户控件名称是
ShanuCPCPKChart
)。 - 添加所有需要的控件。
- 在代码隐藏中,声明所有
public
变量和Public
方法。在用户控件中,我添加了一个面板和一个 Picture Box 控件。
public DataTable dt = new DataTable();
Font f12 = new Font("arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
Pen B1pen = new Pen(Color.Black, 1);
Pen B2pen = new Pen(Color.Black, 2);
Double XDoublkeBAR = 0;
Double RBAR = 0;
Double XBARUCL = 0;
Double XBARLCL = 0;
Double RANGEUCL = 0;
Double RANGELCL = 0;
Double[] intMeanArrayVals;
Double[] intRangeArrayVals;
Double[] intSTDEVArrayVals;
Double[] intXBARArrayVals;
int First_chartDatarectHeight = 80;
Font f10 = new Font("arial", 10, FontStyle.Bold, GraphicsUnit.Pixel);
LinearGradientBrush a2 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19),
Color.DarkGreen, Color.Green, LinearGradientMode.Horizontal);
LinearGradientBrush a3 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19),
Color.DarkRed, Color.Red, LinearGradientMode.Horizontal);
LinearGradientBrush a1 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19),
Color.Blue, Color.DarkBlue, LinearGradientMode.Horizontal);
声明变量后,我创建了一个 public
函数 Bindgrid
。此函数将从 Windows 窗体使用,用于传递 DataTable
。在此函数中,我将检查 DataTable
,如果 DataTable
不为 null
,我将刷新 PictureBox
,这将调用 PictureBox paint
方法来绘制 SPC 图表的所有新值。
public void Bindgrid(DataTable dtnew)
{
if (dtnew != null)
{
dt = dtnew;
PicBox.Refresh();
}
}
在 PictureBox paint
事件中,我将检查 DataTable
数据。使用数据,我计算并绘制 SPC 图表中的所有总和、均值、极差、Cp、Cpk、Pp 和 Ppk 的结果。使用这些信息,我通过标准公式创建了UCL 和 LCL。有关 UCL 和 LCL 计算的详细信息,请参阅上面的链接。在所有值计算完成后,我将在 PictureBox
中使用 GDI+ 绘制 SPC 图表,并将结果显示给最终用户。
public void PicBox_Paint(object sender, PaintEventArgs e)
{
if (dt.Rows.Count <= 0)
{
return;
}
int opacity = 68; // 50% opaque (0 = invisible, 255 = fully opaque)
e.Graphics.DrawString(ChartWaterMarkText,
new Font("Arial", 72),
new SolidBrush(Color.FromArgb(opacity, Color.OrangeRed)),
80,
PicBox.Height / 2 - 15);
int NoofTrials = dt.Rows.Count;
int NoofParts = dt.Columns.Count - 1;
intMeanArrayVals = new Double[NoofParts];
intRangeArrayVals = new Double[NoofParts];
intSTDEVArrayVals = new Double[NoofParts];
intXBARArrayVals = new Double[NoofParts];
if (dt.Rows.Count <= 0)
{
return;
}
PicBox.Width = dt.Columns.Count * 50 + 40;
// 1) For the Chart Data Display ---------
e.Graphics.DrawRectangle(Pens.Black, 10, 10, PicBox.Width - 20,
First_chartDatarectHeight + 78);
// for the chart data Horizontal Line Display
e.Graphics.DrawLine(B1pen, 10, 25, PicBox.Width - 10, 25);
e.Graphics.DrawLine(B1pen, 10, 45, PicBox.Width - 10, 45);
e.Graphics.DrawLine(B1pen, 10, 65, PicBox.Width - 10, 65);
e.Graphics.DrawLine(B1pen, 10, 85, PicBox.Width - 10, 85);
e.Graphics.DrawLine(B1pen, 10, 105, PicBox.Width - 10, 105);
e.Graphics.DrawLine(B1pen, 10, 125, PicBox.Width - 10, 125);
e.Graphics.DrawLine(B1pen, 10, 145, PicBox.Width - 10, 145);
// e.Graphics.DrawLine(B1pen, 10, 165, PicBox.Width - 10, 165);
// for the chart data Vertical Line Display
e.Graphics.DrawLine(B1pen, 60, 10, 60, First_chartDatarectHeight + 87);
e.Graphics.DrawLine(B1pen, 110, 10, 110, First_chartDatarectHeight + 87);
//-------------
// DrawItemEventArgs String
e.Graphics.DrawString("SUM", f12, a1, 14, 10);
e.Graphics.DrawString("MEAN", f12, a1, 14, 30);
e.Graphics.DrawString("Range", f12, a1, 14, 50);
e.Graphics.DrawString("Cp", f12, a1, 14, 70);
e.Graphics.DrawString("Cpk", f12, a1, 14, 90);
e.Graphics.DrawString("Pp", f12, a1, 14, 110);
e.Graphics.DrawString("Ppk", f12, a1, 14, 130);
// load data
//Outer Loop for Columns count
int xLineposition = 110;
int xStringDrawposition = 14;
Double[] locStdevarr;
for (int iCol = 1; iCol <= dt.Columns.Count - 1; iCol++)
{
//inner Loop for Rows count
Double Sumresult = 0;
Double Meanresult = 0;
Double Rangeresult = 0;
Double minRangeValue = int.MaxValue;
Double maxRangeValue = int.MinValue;
locStdevarr = new Double[NoofTrials];
for (int iRow = 0; iRow < dt.Rows.Count; iRow++)
{
Sumresult = Sumresult +
System.Convert.ToDouble(dt.Rows[iRow][iCol].ToString());
Double accountLevel = System.Convert.ToDouble(dt.Rows[iRow][iCol].ToString());
minRangeValue = Math.Min(minRangeValue, accountLevel);
maxRangeValue = Math.Max(maxRangeValue, accountLevel);
locStdevarr[iRow] = System.Convert.ToDouble(dt.Rows[iRow][iCol].ToString());
}
xLineposition = xLineposition + 50;
xStringDrawposition = xStringDrawposition + 50;
e.Graphics.DrawLine(B1pen, xLineposition, 10, xLineposition,
First_chartDatarectHeight + 87);
//Sum Data Display
e.Graphics.DrawString(Math.Round(Sumresult, 3).ToString(), f10,
a2, xStringDrawposition, 12);
//MEAN Data Display
Meanresult = Sumresult / NoofTrials;
e.Graphics.DrawString(Math.Round(Meanresult, 3).ToString(),
f10, a2, xStringDrawposition, 30);
//RANGE Data Display
Rangeresult = maxRangeValue - minRangeValue;
e.Graphics.DrawString(Math.Round(Rangeresult, 3).ToString(),
f10, a2, xStringDrawposition, 50);
//XDoubleBar used to display in chart
XDoublkeBAR = XDoublkeBAR + Meanresult;
//RBAR used to display in chart
RBAR = RBAR + Rangeresult;
intMeanArrayVals[iCol - 1] = Meanresult;
intRangeArrayVals[iCol - 1] = Rangeresult;
intSTDEVArrayVals[iCol - 1] = StandardDeviation(locStdevarr);
}
//End 1 ) -------------------
// 2) --------------------------
// XdoubleBAr/RBAR/UCL and LCL Calculation.
//XDoubleBar used to display in chart
XDoublkeBAR = XDoublkeBAR / NoofParts;
//RBAR used to display in chart
RBAR = RBAR / NoofParts;
//XBARUCL to display in chart
XBARUCL = XDoublkeBAR + UCLLCLTYPE("A2", RBAR, NoofTrials);
//XBARLCL to display in chart
XBARLCL = XDoublkeBAR - UCLLCLTYPE("A2", RBAR, NoofTrials);
//XBARUCL to display in chart
RANGEUCL = UCLLCLTYPE("D4", RBAR, NoofTrials);
//XBARLCL to display in chart
RANGELCL = UCLLCLTYPE("D3", RBAR, NoofTrials);
//2.1) Status Display inside pic grid +++++++++++++++++++++++++++
int XCirclegDrawposition = 24;
int YCirclegDrawposition = 147;
xStringDrawposition = 14;
for (int i = 0; i < intMeanArrayVals.Length; i++)
{
Color pointColor = new Color();
pointColor = Color.YellowGreen;
XCirclegDrawposition = XCirclegDrawposition + 50;
Point p1 = new Point();
p1.X = XCirclegDrawposition;
p1.Y = YCirclegDrawposition;
if (intMeanArrayVals[i] < XBARLCL)
{
pointColor = Color.Red;
}
else if (intMeanArrayVals[i] > XBARUCL)
{
pointColor = Color.Red;
}
Pen pen = new Pen(Color.SeaGreen);
e.Graphics.DrawPie(pen, p1.X, p1.Y, 18, 18, 0, 360);
e.Graphics.FillPie(new SolidBrush(pointColor), p1.X, p1.Y, 18, 18, 10, 360);
pen = new Pen(Color.Black);
e.Graphics.DrawPie(pen, p1.X + 3, p1.Y + 4, 2, 2, 10, 360);
e.Graphics.DrawPie(pen, p1.X + 11, p1.Y + 4, 2, 2, 10, 360);
e.Graphics.DrawPie(pen, p1.X + 5, p1.Y + 12, 8, 4, 10, 180);
// 1)
//Cp Calculation (((((((((((((((((((((((((((
//Cp=(USL-LSL/6SIGMA) -> USL-LSL/6*RBAR/d2
Double d2 = d2Return(NoofTrials);
Double USLResult = USLs - LSLs;
Double RBARS = intRangeArrayVals[i] / NoofTrials;
Double Sigma = RBARS / d2;
Double CpResult = USLResult / 6 * Sigma;
xStringDrawposition = xStringDrawposition + 50;
e.Graphics.DrawString(Math.Round(CpResult, 3).ToString(),
f10, a2, xStringDrawposition, 70);
//End Cp Calculation ))))))))))))))
// 2)
//Cpk Calculation \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//Cpk=min(USL-XBAR/3Sigma,LSL-XBAR/3Sigma)
Double CpU = USLs - intMeanArrayVals[i] / 3 * Sigma;
Double CpL = intMeanArrayVals[i] - LSLs / 3 * Sigma;
Double CpkResult = Math.Min(CpU, CpL);
if (CpkResult < CpkPpKAcceptanceValue)
{
e.Graphics.DrawString(Math.Round(CpkResult, 3).ToString(),
f10, a3, xStringDrawposition, 90);
}
else
{
e.Graphics.DrawString(Math.Round(CpkResult, 3).ToString(),
f10, a2, xStringDrawposition, 90);
}
//End Cpk Calculation \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 3)
//Pp Calculation {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
//Pp=(USL-LSL/6SIGMA) -> USL-LSL/6 STDEV
Double PpResult = USLResult / 6 * intSTDEVArrayVals[i];
e.Graphics.DrawString(Math.Round(PpResult, 3).ToString(),
f10, a2, xStringDrawposition, 110);
//End Pp Calculation }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
// 4)
//PpK Calculation ``````````````````````````````````````````````````````
//PpK=min(USL-XBAR/3STDEV,LSL-XBAR/3STDEVa)
Double PpU = USLs - intMeanArrayVals[i] / 3 * intSTDEVArrayVals[i];
Double PpL = intMeanArrayVals[i] - LSLs / 3 * intSTDEVArrayVals[i];
Double PpkResult = Math.Min(PpU, PpL);
if (PpkResult < CpkPpKAcceptanceValue)
{
e.Graphics.DrawString(Math.Round(PpkResult, 3).ToString(),
f10, a3, xStringDrawposition, 130);
}
else
{
e.Graphics.DrawString(Math.Round(PpkResult, 3).ToString(),
f10, a2, xStringDrawposition, 130);
}
// end of Ppk `````````````````````````````````````````````````````````````
}
//end of 2.1) ++++++++++++++++
//---------------------------------
//3) Average chart Display ---------------
// e.Graphics.DrawRectangle(Pens.Black, 10, 10,
picSpcChart.Width - 20, First_chartDatarectHeight);
int chartAvarageDatarectHeight = 18;
e.Graphics.DrawRectangle(Pens.Black, 10, First_chartDatarectHeight + 96,
PicBox.Width - 20, chartAvarageDatarectHeight);
e.Graphics.DrawLine(B2pen, 476, 194, 480, 176);
e.Graphics.DrawString
("MEAN CHART", f12, a1, 14, First_chartDatarectHeight + 98);
e.Graphics.DrawString
("XBarS:", f12, a1, 160, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(XDoublkeBAR, 3).ToString(),
f12, a2, 202, First_chartDatarectHeight + 98);
e.Graphics.DrawString("UCL:", f12, a1, 300, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(XBARUCL, 3).ToString(),
f12, a2, 330, First_chartDatarectHeight + 98);
e.Graphics.DrawString("LCL:", f12, a1, 400, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(XBARLCL, 3).ToString(),
f12, a2, 430, First_chartDatarectHeight + 98);
e.Graphics.DrawString("RANGE CHART", f12, a1, 490, First_chartDatarectHeight + 98);
e.Graphics.DrawString
("RBar : ", f12, a1, 600, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(RBAR, 3).ToString(), f12,
a2, 638, First_chartDatarectHeight + 98);
e.Graphics.DrawString("UCL : ", f12, a1, 700, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(RANGEUCL, 3).ToString(),
f12, a2, 734, First_chartDatarectHeight + 98);
e.Graphics.DrawString("LCL : ", f12, a1, 800, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(RANGELCL, 3).ToString(),
f12, a2, 834, First_chartDatarectHeight + 98);
// vertical Line
e.Graphics.DrawLine(B2pen, 860, 194, 866, 176);
e.Graphics.DrawString("USL : ", f12, a1, 880, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(USLs, 3).ToString(),
f12, a2, 910, First_chartDatarectHeight + 98);
e.Graphics.DrawString("LSL : ", f12, a1, 960, First_chartDatarectHeight + 98);
e.Graphics.DrawString(Math.Round(LSLs, 3).ToString(),
f12, a2, 990, First_chartDatarectHeight + 98);
//Mean Line Chart
DrawLineChart(e.Graphics, intMeanArrayVals, XBARUCL, XBARLCL,
PicBox.Width - 70, 154, 60, 170, "MEAN", XDoublkeBAR);
DrawLineChart(e.Graphics, intRangeArrayVals, RANGEUCL,
RANGELCL, PicBox.Width - 70, 154, 60, 340, "RANGE", RBAR);
//End 3)---------------------
}
2) 演示程序
现在我们创建一个 Windows 应用程序,添加并测试我们的 "ShanuCPCPKChart
" 用户控件。
- 创建一个新的 Windows 项目。
- 打开您的窗体,然后在工具箱 > 右键单击 > 选择项 > 浏览并选择您的用户控件 DLL 并添加。
- 将用户控件拖到您的 Windows 窗体上。
- 调用用户控件的 "
Bindgrid
" 方法,并将Datatable
传递给Usercontrol
并检查结果。
全局变量声明
#region Local Vairables
DataTable dt = new DataTable();
private static readonly Random random = new Random();
Double gridMinvalue = 1.2;
Double gridMaxvalue = 2.4;
int totalColumntoDisplay = 20;
Double USLs = 2.27;
Double LSLs = 1.26;
Double CpkPpkAcceptanceValue = 1.33;
#endregion
窗体加载事件
在窗体加载事件中,我调用 loadGridColumn()
方法。在此函数中,我为 datatable
添加了列。我使用了 20 列,将用于添加 20 个样本数据,每个样本包含 5 次试验。
//Create Datatable Colums.
public void loadGridColums()
{
dt.Columns.Add("No");
for (int jval = 1; jval <= totalColumntoDisplay; jval++)
{
dt.Columns.Add(jval.ToString());
}
}
接下来在窗体加载事件中,我调用 loadgrid()
方法。在此方法中,我生成 20 列的样本随机数来绘制我们的图表。
public void loadgrid()
{
dt.Clear();
dt.Rows.Clear();
for (int i = 1; i <= 5; i++)
{
DataRow row = dt.NewRow();
row["NO"] = i.ToString();
for (int jval = 1; jval <= totalColumntoDisplay; jval++)
{
row[jval.ToString()] = RandomNumberBetween(gridMinvalue, gridMaxvalue);
}
dt.Rows.Add(row);
}
dataGridView1.AutoResizeColumns();
dataGridView1.DataSource = dt;
dataGridView1.AutoResizeColumns();
}
接下来在窗体加载中,我已将 USL、LSL、Cpk 值和 Datatable 传递给 "shanuCPCPKChart
" 用户控件,以生成 XBAR 和极差图表,并显示 Cp、Cpk、Pp 和 Ppk 结果。
private void Form1_Load(object sender, EventArgs e)
{
loadGridColums();
loadgrid();
USLs = Convert.ToDouble(txtusl.Text);
LSLs = Convert.ToDouble(txtLSL.Text);
CpkPpkAcceptanceValue = Convert.ToDouble(txtData.Text);
shanuCPCPKChart.USL = USLs;
shanuCPCPKChart.LSL = LSLs;
shanuCPCPKChart.CpkPpKAcceptanceValue = CpkPpkAcceptanceValue;
shanuCPCPKChart.Bindgrid(dt);
}
为了显示实时数据图表结果,我在这里使用了计时器,可以通过随机数据绑定数据,并将所有结果传递给用户控件以刷新和显示结果。
private void timer1_Tick(object sender, EventArgs e)
{
loadgrid();
USLs = Convert.ToDouble(txtusl.Text);
LSLs = Convert.ToDouble(txtLSL.Text);
CpkPpkAcceptanceValue = Convert.ToDouble(txtData.Text);
shanuCPCPKChart.USL = USLs;
shanuCPCPKChart.LSL = LSLs;
shanuCPCPKChart.CpkPpKAcceptanceValue = CpkPpkAcceptanceValue;
shanuCPCPKChart.Bindgrid(dt);
}
关注点
我很乐意地说,这将是第一个带有源代码的免费 SPC Cp、Cpk、Pp 和 Ppk 图表控件。如果您喜欢这些控件,请为本文投票。
历史
- 2015 年 7 月 2 日:初始版本