반응형

용도: 원형 물체를 측정할 때 사용됩니다.

트리거

회전체를 각도별로 측정하려면 트리거 신호 입력 기능이 있는지 확인해야 합니다.

데이터시트의 External Digital Triggers, General-Purpose Counters/Timers 항목에서 확인할 수 있습니다.

  • 회전체의 기준이 되는 신호를 검출하려면 Start Trigger가 필요합니다. 인코더의 Z상을 읽습니다.
  • 회전체의 각도와 동기화 되는 신호를 검출하려면 Sample Clock이 필요합니다. 인코더의 A 또는 B상을 읽습니다.
  • 회전체의 절대 각도의 신호를 검출하려면 Quadrature가 필요합니다. 인코더의 A, B상을 모두 읽습니다.

 

모델명은 6X1X 시리즈가 해당됩니다. 예) USB-6211, USB-6421, PCIe-6321

USB-6211
USB-6212

 

측정 속도

회전체를 측정할 때에는 샘플링 속도가 중요합니다. 역기전력을 측정할 때에는 1000RPM 또는 3600RPM으로 회전시킨 후 측정합니다.

USB-6210의 샘플링 속도는 250,000 샘플/초입니다.

3600PLS 인코더를 사용할 경우, 단방향 측정일 때에는 3600 펄스로 계산하며, 양방향 절대 측정일 때에는 4체배를 하여 14,400 펄스로 계산합니다.

250,000 샘플/초에서 3600 펄스를 기준으로 계산한 회전 속도는 다음과 같습니다:

\[ \text{회전 속도 (RPM)} = \frac{250,000 \, \text{펄스/초}}{3600 \, \text{펄스/회전}} = 69.44 \, \text{회전/초} \]

이를 분당 회전수 (RPM)로 변환하면:

\[ \text{회전 속도 (RPM)} = 69.44 \, \text{회전/초} \times 60 = 4166.4 \, \text{RPM} \]

 

250,000 샘플/초에서 14,400 펄스를 기준으로 계산한 회전 속도는 다음과 같습니다:

\[ \text{회전 속도 (RPM)} = \frac{250,000 \, \text{펄스/초}}{14,400 \, \text{펄스/회전}} = 17.36 \, \text{회전/초} \]

이를 분당 회전수 (RPM)로 변환하면:

\[ \text{회전 속도 (RPM)} = 17.36 \, \text{회전/초} \times 60 = 1041.6 \, \text{RPM} \]

USB-6210 모델은 단방향 측정은 충분하지만, 양방향 측정에는 1000RPM까지만 권장됩니다.

이보다 빠른 속도로 측정하려면 400kS/s 샘플링 속도를 지원하는 USB-6212를 사용하면 됩니다.

 

C# 코드

아래 코드는 실무에서 사용 중인 단방향 측정 코드입니다.

public Task<bool> AIExtClkReadAsync(string channels, int samples_per_ch, double sample_rate, AITerminalConfiguration ait_cfg = AITerminalConfiguration.Differential, string DIStartTrigger = null, string DISampleClock = null)
{
    try
    {
        // Create a new task
        _ai_Task = new NationalInstruments.DAQmx.Task();

        string channel_name = $"{channels}";

        _ai_Task.AIChannels.CreateVoltageChannel(channel_name, "", ait_cfg, MinimumValue, MaximumValue, AIVoltageUnits.Volts); // Create a channel

        if (DIStartTrigger.IndexOf("/") != 0) DIStartTrigger = $"/{DIStartTrigger}";
        _ai_Task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(DIStartTrigger, DigitalEdgeStartTriggerEdge.Rising);

        if (DISampleClock.IndexOf("/") != 0) DISampleClock = $"/{DISampleClock}";
        _ai_Task.Timing.ConfigureSampleClock(DISampleClock, sample_rate, SampleClockActiveEdge.Rising, SampleQuantityMode.FiniteSamples, (int)samples_per_ch); // Configure timing specs

        _ai_Task.Control(TaskAction.Verify);    // Verify the task
        _ai_RunningTask = _ai_Task;
        channelCollection = _ai_Task.AIChannels;

        _ai_Reader = new AnalogMultiChannelReader(_ai_Task.Stream); // Read the data

        wave_data = _ai_Reader.ReadWaveform((int)samples_per_ch);

        // 여기까지 데이터 수집 완료
    }
    catch (DaqException ex)
    {
        LogUpdate(new LogEventArgs($"DAQ\n{ex.Message}", LogEventArgs.LogTypes.Error));
        Debug.WriteLine(ex.Message);
        _ai_RunningTask = null;
        return System.Threading.Tasks.Task.Factory.StartNew(() => { return false; });
    }
    finally
    {
        _ai_Task.Dispose();
        _ai_RunningTask = null;
    }

    return System.Threading.Tasks.Task.Factory.StartNew(() => { return true; });
}

 

아래 코드는 양방향 측정 코드 샘플입니다.

public class AbsoluteAngleReader
{
    private NationalInstruments.DAQmx.Task _task;
    private DigitalSingleChannelReader _readerA;
    private DigitalSingleChannelReader _readerB;
    private AnalogMultiChannelReader _analogReader;
    
    private int currentPosition = 0; // 현재 각도
    private int previousPosition = 0; // 이전 각도
    private Dictionary<int, double> angleVoltageMap = new Dictionary<int, double>(); // 각도-전압 매핑 저장

    private double voltageValue = 0.0;

    public async Task<bool> ReadQuadratureAndVoltageAsync(string channelA, string channelB, string voltageChannel, string ZTriggerChannel, int samplesPerCycle, double sampleRate)
    {
        try
        {
            // Task 생성
            _task = new NationalInstruments.DAQmx.Task();

            // A상, B상 디지털 입력 채널 생성
            _task.DIChannels.CreateChannel(channelA, "", ChannelLineGrouping.OneChannelForEachLine);
            _task.DIChannels.CreateChannel(channelB, "", ChannelLineGrouping.OneChannelForEachLine);

            // 아날로그 입력 전압 채널 생성
            _task.AIChannels.CreateVoltageChannel(voltageChannel, "", AITerminalConfiguration.Differential, -10, 10, AIVoltageUnits.Volts);

            // Z상 트리거 설정 (Z상에서 트리거 발생 시 각도 리셋)
            _task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(ZTriggerChannel, DigitalEdgeStartTriggerEdge.Rising);

            // 샘플 클럭 설정
            _task.Timing.ConfigureSampleClock("", sampleRate, SampleClockActiveEdge.Rising, SampleQuantityMode.FiniteSamples, samplesPerCycle);

            // Task 실행 준비
            _task.Control(TaskAction.Verify);

            // A상과 B상에 대한 리더 생성
            _readerA = new DigitalSingleChannelReader(_task.Stream);
            _readerB = new DigitalSingleChannelReader(_task.Stream);

            // 아날로그 전압 리더 생성
            _analogReader = new AnalogMultiChannelReader(_task.Stream);

            // 데이터 수집
            var pulseDataA = _readerA.ReadSingleSamplePort();
            var pulseDataB = _readerB.ReadSingleSamplePort();

            // 전압 읽기
            var voltageData = _analogReader.ReadSingleSample(); // 아날로그 데이터 읽기

            // 전압 값 저장
            voltageValue = voltageData[0]; // 첫 번째 채널 값 저장

            // Z상에서 각도를 리셋하고, 펄스를 읽어 위치를 업데이트
            if (pulseDataA == 1 && pulseDataB == 1)
            {
                // Z상 트리거가 발생하면 각도를 초기화
                ResetAngle();
            }
            else
            {
                // 회전 방향에 상관없이 절대 각도 업데이트
                UpdateAngle(pulseDataA, pulseDataB);
            }

            // 해당 각도의 전압 값 기록
            if (!angleVoltageMap.ContainsKey(currentPosition))
            {
                angleVoltageMap[currentPosition] = voltageValue;
            }

            // 수집 완료
            return true;
        }
        catch (DaqException ex)
        {
            Console.WriteLine($"DAQ Error: {ex.Message}");
            return false;
        }
        finally
        {
            // 자원 해제
            _task.Dispose();
        }
    }

    // 각도 리셋: Z상 신호 발생 시 각도를 0으로 리셋
    private void ResetAngle()
    {
        currentPosition = 0;  // Z상 트리거 발생 시 0으로 초기화
        Console.WriteLine($"Angle Reset to: {currentPosition}");
    }

    // 위치 업데이트: A상, B상 상태에 따라 회전 방향을 결정
    private void UpdateAngle(int pulseA, int pulseB)
    {
        if (pulseA != pulseB)
        {
            if (pulseA == 1)
            {
                // A상이 B상보다 먼저 상승 -> 시계방향
                currentPosition++;
            }
            else
            {
                // A상이 B상보다 먼저 상승 -> 반시계방향
                currentPosition--;
            }
        }

        // 현재 각도 출력
        Console.WriteLine($"Current Angle: {currentPosition} degrees");
        Console.WriteLine($"Current Voltage: {voltageValue}V");
    }
}

양방향 측정 코드는 단방향 측정 코드에 A, B상을 읽는 코드를 추가하고 배열의 절대 각도 위치에 측정 값을 저장하는 것입니다.

반응형

관련글