반응형

마쓰루 사의 Cool Muscle 제품의 통신 제어를 위한 클래스 코드 입니다. 

시리얼을 통해 모터를 제어하는 제품 입니다. 

public class CCoolMuscle
{
    public enum MOTOR_DIRECT { CW, CCW }

    private SerialPort m_ComPort;
    private int m_uiSpeed;
    private int m_uiAcceleration;

    public CCoolMuscle(SerialPort ComPort)
    {
        m_ComPort = ComPort;
        Timeout = 60;
    }

    public int Speed
    {
        set
        {
            m_uiSpeed = value;
            string message = string.Format("S.1={0}\r", m_uiSpeed);
            m_ComPort.Write(message);
        }
    }

    public int Acceleration
    {
        set
        {
            m_uiAcceleration = value;
            string message = string.Format("A.1={0}\r", m_uiAcceleration);
            m_ComPort.Write(message);
        }
    }

    public uint Timeout { set; get; } = 60;     // 60 sec

    public void A_Move(int Position)
    {
        string message = string.Format("P.1={0},^\r", Position);
        m_ComPort.Write(message);
    }

    public void R_Move(int Distance, MOTOR_DIRECT Direct)
    {
        string message;

        if (Direct == MOTOR_DIRECT.CW) message = string.Format("P+.1={0},^\r", Distance);
        else message = string.Format("P-.1={0},^\r", Distance);

        m_ComPort.Write(message);
    }

    public void Set_Zero_Position()
    {
        m_ComPort.Write("|2\r");
    }

    public int Get_Position(out int Position)
    {
        int result = 0;
        Position = 0;

        m_ComPort.Write("?96\r");
        Thread.Sleep(100);

        try
        {
            string str = m_ComPort.ReadLine(); 

            if (str.Length == 0) result = -1;
            else
            {
                Position = int.Parse(str.Remove(0, str.IndexOf('=') + 1));
            }
        }
        catch
        {
            Position = 0;
            result = -2;
        }

        return result;
    }

    public int Check_Motor_Stop()
    {
        int result;

        m_ComPort.Write("?99\r");

        try
        {
            string str = m_ComPort.ReadLine();

            if (str.Contains("Ux.1=8") == true) result = 0;    // Motor is Stop
            else result = 1;    // Motor is moving
        }
        catch
        {
            result = -1;
        }

        return result;
    }

    public void Motor_Stop()
    {
        m_ComPort.Write("]\r");
        m_ComPort.Write("]\r");
    }

    public void Move_Zero_Position()
    {
        m_ComPort.Write("|1\r");
    }

    public void Return_Origin_Position()
    {
        // 원점 복귀, 원점의 위치를 새로 갱신 
        m_ComPort.Write("|\r");
    }
}
반응형
반응형

엠에프씨코리아 업체의 Digital MFC 제품인 VIC - D 시리즈의 통신 제어를 위한 클래스 코드 입니다. 

시리얼 통신을 통해 유량을 제어하고 현재 유량값을 읽어 올수 있습니다.

 

 

public class CVIC_DSeries
{
    /*******************************************************************************
    * CLASS: CVIC_DSeries
    * Version : 1.0.0
    * MFC (Flow Controller) Program  (Manufactured by MFC Korea)
    * Programed by Sihwan Kim
    * Copyright (C) 2019 Photondays, Inc.  All rights reserved.
    * PROTOCOL   :  START, ADDRESS, COMMAND, DATA TYPE, DATA, CHECKSUM, END   (ASCII 형식) 
    * Bytes Num. :    1       2        2          2     0~64      2      1 
    *******************************************************************************/

    private SerialPort m_ComPort;
    private byte[] m_Address = new byte[2];
    private float m_MaxFlow;

    public float MaxFlow
    {
        get { return m_MaxFlow; }
    }

    public CVIC_DSeries(SerialPort ComPort, byte Address)
    {
        m_ComPort = ComPort;
        m_Address = ByteToHexAscii(Address);
    }

    private byte[] ByteToHexAscii(byte value)       // 1바이트를 2바이트 아스키로 변환 
    {
        string strHex = value.ToString("X2");
        byte[] StrByte = Encoding.UTF8.GetBytes(strHex);

        return StrByte;
    }

    private byte[] CheckSum(byte[] Data, int Count)
    {
        byte[] result = new byte[2];
        byte checksum = Data[0];

        for (int loop = 1; loop < Count; loop++)
        {
            checksum = (byte)(checksum ^ Data[loop]);
        }

        result = ByteToHexAscii(checksum);

        return result;
    }

    public bool Initialize()
    {
        bool result = true ;
        byte[] bTemp = new byte[2];
        byte[] send_buffer = new byte[] { 0x3a, m_Address[0], m_Address[1], 0x32, 0x31, 0x30, 0x30, 0x33, 0x39, 0x0d };
        byte[] Receive_Buff = new byte[50];

        bTemp = CheckSum(send_buffer, 7);
        send_buffer[7] = bTemp[0];
        send_buffer[8] = bTemp[1];

        m_ComPort.Write(send_buffer, 0, 10);
        Thread.Sleep(200);
       
        try
        {
            m_ComPort.Read(Receive_Buff, 0, 50);

            if (Receive_Buff[0] == 0x3A && Receive_Buff[2] == m_Address[1]) result = true;
            else result = false;
        }
        catch
        {
            result = false;
        }
        
        return result;
    }

    public bool Serial_Mode()
    {
        bool result = true;
        byte[] bTemp = new byte[] { 0x00, 0x00 };
        byte[] send_buffer = new byte[] { 0x3A, m_Address[0], m_Address[1], 0x35, 0x38, 0x30, 0x32, 0x30, 0x31, 0x33, 0x38, 0x0D };
        byte[] Receive_Buff = new byte[50];

        bTemp = CheckSum(send_buffer, 9);
        send_buffer[9] = bTemp[0];
        send_buffer[10] = bTemp[1];

        m_ComPort.Write(send_buffer, 0, 12);
        Thread.Sleep(50);

        try
        {
            m_ComPort.Read(Receive_Buff, 0, 50);
        }
        catch { }

        return result;
    }

    public bool Set_Flow_Point(int FlowValue)
    {
        bool result = false;
        int index = 7;
        float percentage;
        byte[] bTemp = new byte[2];
        byte[] send_buffer = new byte[] { 0x3A, m_Address[0], m_Address[1], 0x30, 0x31, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d};
        byte[] Receive_Buff = new byte[50];

        percentage = ((float)FlowValue * 100.0f) / m_MaxFlow;          // 유량설정은 퍼센트로 한다. 
        byte[] dByte = BitConverter.GetBytes(percentage);       // IEEE754 형식의 float

        for (int loop = 3; loop > -1; loop--)
        {
            bTemp = ByteToHexAscii(dByte[loop]);
            send_buffer[index++] = bTemp[0];
            send_buffer[index++] = bTemp[1];
        }

        bTemp = CheckSum(send_buffer, 15);

        send_buffer[15] = bTemp[0];
        send_buffer[16] = bTemp[1];   

        m_ComPort.Write(send_buffer, 0, 18);
        Thread.Sleep(100);

        try
        {
            m_ComPort.Read(Receive_Buff, 0, 50);   // 라즈베리파이는 데이타가 들어오지 않으면 예외를 발생시킨다.

            if (Receive_Buff[0] == 0x3A && Receive_Buff[17] == 0x0D)
            {
                result = true;
            }
        }
        catch
        {
            result = false;
        }

        return result;
    }

    public int Read_Flow_Value(ref float flowValue)
    {
        int result = 0;
        byte[] send_buffer = new byte[] { 0x3A, m_Address[0], m_Address[1], 0x30, 0x33, 0x30, 0x30, 0x33, 0x39, 0x0D };
        byte[] bcheck = CheckSum(send_buffer, 7);
        send_buffer[7] = bcheck[0];
        send_buffer[8] = bcheck[1];
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

        m_ComPort.Write(send_buffer, 0, send_buffer.Count());
        sw.Start();

        try
        {
            while (true)
            {
                Thread.Sleep(10);

                if (m_ComPort.BytesToRead > 0)
                {
                    Thread.Sleep(10);
                    byte[] Receive_Buff = new byte[m_ComPort.BytesToRead + 2];

                    m_ComPort.Read(Receive_Buff, 0, m_ComPort.BytesToRead);

                    if (Receive_Buff[0] == 0x3A && Receive_Buff[17] == 0x0D)
                    {
                        byte[] bTemp = new byte[8];
                        Array.Copy(Receive_Buff, 7, bTemp, 0, 8);

                        try
                        {
                            var percentage = FromHexString(Encoding.Default.GetString(bTemp));      // IEEE754 -> float 
                            flowValue = (percentage * m_MaxFlow) / 100.0f;

                            if (flowValue <= 0) flowValue = 0f;
                        }
                        catch
                        {
                            flowValue = 0f;
                            result = -2;
                        }
                    }

                    break;
                }

                var time = (int)(sw.ElapsedMilliseconds / 1000);
                if (time > 5)           //  Timeout 5sec 시간 경과 
                {
                    result = -1;
                    break;  
                }
            }
        }
        finally 
        { 
            sw.Stop(); 
        }

        return result;
    }

    public int Get_Max_Flow()
    {
        int result = 0;

        byte[] bTemp = new byte[8];
        byte[] send_buffer = new byte[] { 0x3A, m_Address[0], m_Address[1], 0x33, 0x33, 0x30, 0x30, 0x33, 0x39, 0x0D };
        byte[] Receive_Buff = new byte[50];                                     

        byte[] bcheck = CheckSum(send_buffer, 7);
        send_buffer[7] = bcheck[0];
        send_buffer[8] = bcheck[1];

        m_ComPort.Write(send_buffer, 0, send_buffer.Count());
        Thread.Sleep(100);

        try
        {
            m_ComPort.Read(Receive_Buff, 0, 50);

            if (Receive_Buff[0] == 0x3A && Receive_Buff[17] == 0x0D)
            {
                Array.Copy(Receive_Buff, 7, bTemp, 0, 8);

                try
                {
                    m_MaxFlow = FromHexString(Encoding.Default.GetString(bTemp));      // IEEE754 -> float 
                }
                catch
                {
                    m_MaxFlow = -999.99f;
                    result = -2;
                }
            }
        }
        catch
        {
            m_MaxFlow = -999.99f;
            result = -1;
        }

        return result;
    }
    private float FromHexString(string s)
    {
        var i = Convert.ToInt32(s, 16);
        var bytes = BitConverter.GetBytes(i);
        return BitConverter.ToSingle(bytes, 0);
    }
}
반응형
반응형

지난 글에서 영상의 일부 영역을 이미지로 저장하는 방법에 대해 기술하였습니다.

이번에는 저장된 이미지를 패턴으로 등록하여 화면상에서 동일한 패턴을 패턴매칭을 통해 찾는 코드를 추가 합니다. 

 

1. 지난 4회에 걸친 카메라 제어 및 테스트에 대한 전체 코드를 올려 놓습니다.

CameraControl.zip
0.46MB

 

2. 아래 2개의 멤버 함수를 추가합니다. 

   * 첫번째 메서드는 내부 메모리에 등록되어 있는 패턴을 이용하는 함수 입니다. 

   * 두번째 메서드는 저장된 패턴 파일을 불러와서 매칭에 사용하는 함수 입니다. 

public class CameraThread
{
    private Mat patternFrame = new();

    public bool PatternSearch(int MatchRate, ref OpenCvSharp.Point offset, ref double SearchRate)
    {
        bool result = false;
        double rate = (double)(MatchRate / 100);   // matching rate 0 ~ 1 

        using (Mat matchResult = new())
        {
            Cv2.MatchTemplate(sourceFrame, patternFrame, matchResult, TemplateMatchModes.CCoeffNormed);   // 이미지 템플릿 매치
            Cv2.MinMaxLoc(matchResult, out double minval, out double maxval, out OpenCvSharp.Point minloc, out OpenCvSharp.Point maxloc);

            if (maxval >= rate)  // 타겟 이미지랑 유사 정도 1에 가까울 수록 같음
            {
                offset = maxloc;             // 패턴을 찾은 위치값
                SearchRate = maxval * 100;   // 패턴 매칭율

                result = true;
            }
        }

        return result;
    }
    //-------------------------------------------------------------------------------------
    public bool PatternSearch(int MatchRate, string patternFile, ref OpenCvSharp.Point offset, ref double SearchRate)
    {
        bool result = false;
        double rate = (double)(MatchRate / 100);   // matching rate 0 ~ 1 

        using (Mat pattern = new (patternFile))  // Pattern 파일을 읽는다. 
        using (Mat matchResult = new ())
        {
            Cv2.MatchTemplate(sourceFrame, pattern, matchResult, TemplateMatchModes.CCoeffNormed);   // 이미지 템플릿 매치
            Cv2.MinMaxLoc(matchResult, out double minval, out double maxval, out OpenCvSharp.Point minloc, out OpenCvSharp.Point maxloc);

            if (maxval >= rate)  // 타겟 이미지랑 유사 정도 1에 가까울 수록 같음
            {
                offset = maxloc;   // 패턴을 찾은 위치값
                SearchRate = maxval * 100 ;  // 패턴 매칭율

                result = true;
            }
        }

        return result;
    }
    //-------------------------------------------------------------------------------------
}

 

반응형
반응형

void __fastcall Sleep_uS(int NS_Time)
{
    __int64 freq, start, end;
    float fGap ;
    QueryPerformanceFrequency((LARGE_INTEGER*)(&freq)); //주파수를 얻음
    QueryPerformanceCounter((LARGE_INTEGER*)&start); // 시작 시간을 얻음
    while(true)
    {
        QueryPerformanceCounter((LARGE_INTEGER*)&end); //종료 시간을 얻음
        fGap = (((float)end - (float)start)/(float)freq) * 1000000 ; // <= 프로세싱시간
        if(fGap > NS_Time) break ;
    }
}

반응형
반응형

Canvas 에 원을 그리기 위해서 Ellipse 멤버함수를 사용하는데 이 함수가 사각형에 내접하는 원을 그리는 방식이라 
타원이 그려지게 되죠.. 정원을 그리기 위해서는 정사각형을 구현해야 합니다. 

그래서 원의 중심점과 반지름 위치의 점을 이용하여 정원을 그리는 방식으로 코드를 만들어 보았습니다. 

void Calcu_Circle_Position(POINT Center, POINT Radius, POINT* StartPos, POINT* EndPos) 

    int dx, dy, radius ; 

    dx = (Radius.x - Center.x) * (Radius.x - Center.x) ; 
    dy = (Radius.y - Center.y) * (Radius.y - Center.y) ; 
    radius = sqrt((double)(dx+dy)) ; 

    StartPos->x = Center.x - radius ; 
    StartPos->y = Center.y - radius ; 
    EndPos->x   = Center.x + radius ; 
    EndPos->y   = Center.y + radius ; 


함수 사용방법은 

POINT Center, Radius, StartPos, EndPos ; 

/**************** 
Center : 원의 중심점의 좌표값 
Radius : 반지름에 해당하는 좌표값 
*****************/ 

Calcu_Circle_Position(Center, Radius, &StartPos, &EndPos) ; 
Canvas->Ellipse(StartPos.x, StartPos.y, EndPos.x, EndPos.y) ; 

이렇게 사용하시면 됩니다.

반응형
반응형

OpenCVSharp 3번째로 영상을 캡쳐하는 방법을 설명합니다. 

 

1. 지난 포스팅에서 Line Generator 를 그리는 방법을 설명을 했는데 Line Generator 를 이용하여 이미지의 특정 영역을 설정하고 설정된 영역을 이미지로 저장하는 코드를 추가 합니다. 

 

2. 화면 전체를 캡쳐하고 싶으시면 화면 전체를 영역을 설정하시면 됩니다. 

 

3. 아래 2개 함수를 지난번 클래스에 추가 합니다. 

public class CameraThread
{
    public bool PatternCapture(String Filename, Rect rect)
    {
        bool result;

        var cutChangeImage = sourceFrame.Clone(rect);     // cut Image                                                     

        result = Cv2.ImWrite(Filename, cutChangeImage);      // save Image

        return result;
    }
    //-------------------------------------------------------------------------------------
    public bool PatternCapture(ref Bitmap image, Rect rect)
    {
        bool result = false ;
        var cutChangeImage = sourceFrame.Clone(rect);     // cut Image                                                     

        image = BitmapConverter.ToBitmap(cutChangeImage);

        if (image != null) result = true;

        return result;
    }
}

 

* public bool PatternCapture(String Filename, Rect rect) : 설정된 영역을 파일로 저장하는 함수 

* public bool PatternCapture(ref Bitmap image, Rect rect) : 설정된 영역을 이미지 파라메터로 넘기는 함수 

 

반응형
반응형

실행중인 화면의 영역을 설정해서 jpeg 이미지 파일로 저장하기위한 코드 입니다. 

void __fastcall Save_Meas_Image(UnicodeString FileName)
{
	TImage *CAPTURE = new TImage(NULL) ;
	HDC hdHandle = GetWindowDC(this->Handle) ;

	try
	{
		CAPTURE->Width  = this->Width ;
		CAPTURE->Height = this->Height ;
		CAPTURE->Picture->Bitmap = NULL ;

		BitBlt(CAPTURE->Canvas->Handle, 0, 0, this->Width, this->Height, hdHandle, 0, 0, SRCCOPY) ;
		JpegSave(FileName, CAPTURE->Picture->Bitmap) ;
	}
	__finally
	{
		delete CAPTURE ;
	}
}
//---------------------------------------------------------------------------

void __fastcall JpegSave(UnicodeString Filename, Graphics::TBitmap *SaveBmp)
{
	TJPEGImage *Hjpeg = new TJPEGImage() ;

	try
	{
		Hjpeg->Assign(SaveBmp) ;
		Hjpeg->SaveToFile(Filename) ;
	}
	__finally
	{
		delete Hjpeg ;
	}
}
//---------------------------------------------------------------------------
반응형
반응형

지난 포스팅에 이어 이번에는 출력되는 영상에 Line을 그려주는 코드를 넣어보도록 하겠습니다. 

 

1. Main 화면에 지난번에 이어  HScrollBar 2개(min:0, max:640), VScrollBar 2개(min:0, max:480) 를 올립니다. 

   * 각 ScrollBar 의 Maximum 크기는 화면의 크기와 동일하게 설정합니다. 

 

2. TextBox 4 개의 올려 줍니다. 각각의 Name 속성을 다음과 같이 설정합니다. 

   * textOffsetX, textOffsetY, textSizeX, textSizeY

 

3. CheckBox 를 1개 올려 줍니다. Name 속성은 checkLineDisplay 로 설정합니다. 

 

4. 지난 포스팅에 기술한 카메라 제어 클래스에 아래의 코드를 추가해 줍니다. 

public class CameraThread
{

    public int displayLineX1 { get; set; }
    public int displayLineX2 { get; set; }
    public int displayLineY1 { get; set; }
    public int displayLineY2 { get; set; }
    public bool displayLine { get; set; } = true;
//-------------------------------------------------------------------------------------
    public void Run()
    {
        capture.Open(cameraNumber);

        using (Mat displayFrame = new())
        {
            while (isCameraRunning)
            {
                capture.Read(sourceFrame);

                if (!sourceFrame.Empty())
                {
                    Cv2.CopyTo(sourceFrame, displayFrame);

                    if (displayLine == true) DrawLine(displayFrame);
                    UpdateFrame(BitmapConverter.ToBitmap(displayFrame));
                }
            }
        }

        capture.Release();
        GC.Collect();
    }
    //-------------------------------------------------------------------------------------
    private void DrawLine(Mat displayFrame)
    {
        Cv2.Line(displayFrame, 0, displayLineY1, displaySize.X, displayLineY1, Scalar.Yellow, 1, LineTypes.AntiAlias);
        Cv2.Line(displayFrame, 0, displayLineY2, displaySize.X, displayLineY2, Scalar.Yellow, 1, LineTypes.AntiAlias);
        Cv2.Line(displayFrame, displayLineX1, 0, displayLineX1, displaySize.Y, Scalar.Yellow, 1, LineTypes.AntiAlias);
        Cv2.Line(displayFrame, displayLineX2, 0, displayLineX2, displaySize.Y, Scalar.Yellow, 1, LineTypes.AntiAlias);
    }
    //-------------------------------------------------------------------------------------
}

 

5. 스크롤바 4개의 scroll 이벤트 함수로 Line_Scroll을 모두 동일하게 설정합니다. 

 

6. Main 화면의 코드를 추가 합니다. 

private void checkLineDisplay_CheckedChanged(object sender, EventArgs e)
{
    cameraThread.displayLine = checkLineDisplay.Checked;
}
//--------------------------------------------------------------------------------
private void Line_Scroll(object sender, ScrollEventArgs e)
{
    if (sender is ScrollBar scroll)
    {
        var Size   = new System.Drawing.Point();
        var Offset = new System.Drawing.Point();

        switch (int.Parse(scroll.Tag.ToString()))
        {
            case 0: cameraThread.displayLineX1 = scroll.Value; break;
            case 1: cameraThread.displayLineX2 = scroll.Value; break;
            case 2: cameraThread.displayLineY1 = scroll.Value; break;
            case 3: cameraThread.displayLineY2 = scroll.Value; break;
        }

        Size.X = Math.Abs(cameraThread.displayLineX2 - cameraThread.displayLineX1);
        Size.Y = Math.Abs(cameraThread.displayLineY2 - cameraThread.displayLineY1);

        Func<int, int, int> Compare = (int x, int y) => { if (x < y) return x; else return y; };

        Offset.X = Compare(cameraThread.displayLineX1, cameraThread.displayLineX2);
        Offset.Y = Compare(cameraThread.displayLineY1, cameraThread.displayLineY2);

        textOffsetX.Text = Offset.X.ToString();
        textOffsetY.Text = Offset.Y.ToString();

        textSizeX.Text = Size.X.ToString();
        textSizeY.Text = Size.Y.ToString(); 
    }
}
반응형

+ Recent posts