용도: 원형 물체를 측정할 때 사용됩니다.
트리거
회전체를 각도별로 측정하려면 트리거 신호 입력 기능이 있는지 확인해야 합니다.
데이터시트의 External Digital Triggers, General-Purpose Counters/Timers 항목에서 확인할 수 있습니다.
- 회전체의 기준이 되는 신호를 검출하려면 Start Trigger가 필요합니다. 인코더의 Z상을 읽습니다.
- 회전체의 각도와 동기화 되는 신호를 검출하려면 Sample Clock이 필요합니다. 인코더의 A 또는 B상을 읽습니다.
- 회전체의 절대 각도의 신호를 검출하려면 Quadrature가 필요합니다. 인코더의 A, B상을 모두 읽습니다.
모델명은 6X1X 시리즈가 해당됩니다. 예) USB-6211, USB-6421, PCIe-6321


측정 속도
회전체를 측정할 때에는 샘플링 속도가 중요합니다. 역기전력을 측정할 때에는 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상을 읽는 코드를 추가하고 배열의 절대 각도 위치에 측정 값을 저장하는 것입니다.