08-26-2024 10:50 AM
Hello,
I am trying to read the position of a quadrature encoder using the DAQmx python library. Here are my setup details:
cDAQ-9174
NI-9401 module
SICK DGS80B-TSHC08192 encoder:
https://cdn.sick.com/media/pdf/5/95/295/dataSheet_DGS80B-TSHC08192_1118689_en.pdf
Python 3.12.4 running on PyCharm in a miniconda3 environment with NI DAQmx API, windows 10
I seem to be reading the counter and seeing a change as I rotate the encoder, but the readings don't make sense. I'll rotate the encoder approx. 80 degrees and my code output will show something between 2.1 and 2.6. I have been able to correctly read encoder position in NI DAQexpress where the displayed plot matches up with actual encoder position so I'm confident the hardware setup is working correctly. Here is the code I am using:
from nidaqmx.constants import AcquisitionType, EncoderType, AngleUnits, READ_ALL_AVAILABLE, Edge
def read_encoder_data(counter_name, encoder_type, position_units, encoder_resolution, start_angle):
# Create a task
with nidaq.Task() as task:
# Add an encoder counter task
sensor_position = task.ci_channels.add_ci_ang_encoder_chan(counter=counter_name, #passed in through main
decoding_type=encoder_type, #passed in through main
zidx_enable=False, #Explicitly set to false just to be sure
units=position_units, #passed in through main
pulses_per_rev=encoder_resolution, #passed in through main
initial_angle=start_angle) #passed in through main
# Configure clock timing. This does seem to change the behavior but still not the correct behavior
#task.timing.cfg_samp_clk_timing(1000.0,'/cDAQ1Mod1/PFI1',active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=1000)
#Define which lines to read encoder channels A and B from.
# These lines do not seem to affect behavior. ie. we get the same values even if these are commented out
sensor_position.ci_encoder_a_input_term = 'PFI1'
sensor_position.ci_encoder_b_input_term = 'PFI3'
# Configuration for Z channel.
# We are not using the Z channel in DAQexpress as it was causing a constant reset error so
# we've opted to not use it here either. We may need it though, not sure.
# sensor_position.ci_encoder_z_input_term_cfg
# sensor_position.ci_encoder_z_input_term = 'PFI5'
# Start task
task.start()
# Read data
data = task.read() #Using the task.read function as we couldn't find a counter specific read function
#The C# example I found uses a "DAQmxReadCounterF64" function. From what I've read
#this is wrapped up in the task.read function in python
return data
def main():
# Define encoder variables
counter_name = 'cDAQ1Mod1/ctr0' #Our D I/O module is in slot 1 of our cDAQ-9174
encoder_type = EncoderType.X_2 #We are currently using X2 decoding. Better resolution than X1 but less sensitive to vibrations than X4
position_units = AngleUnits.DEGREES #We want the result in degrees
encoder_resolution = 8192 #Our encoder (SICK DGS80B-TSHC08192) has 8192 pulses per revolution
start_angle = 0.0 #Define the starting angle to be 0.0 degrees
current_angle = start_angle #Set current angle to start angle before current angle calculations happen in while loop
try:
while True: #While loop that is always running once main is called
# Run the read encoder data function
position_data = read_encoder_data(counter_name, encoder_type, position_units, encoder_resolution, start_angle) #pass argument values to the read_encoder_data function
current_angle = current_angle + position_data #The output of the task.read function seemed to be giving the change in angle. This line adds the change to the current angle.
print(current_angle)
# Error handling
except nidaqmx.DaqError as e:
print('An error occurred: ', e)
except KeyboardInterrupt:
print("\nStopped by user")
finally:
print('Task complete')
Here is a photo of the DAQexpress setup that is giving expected results:
Any help would be greatly appreciated!
Solved! Go to Solution.
08-26-2024 07:25 PM
You are looping the entire DAQmx task, which resets the task every iteration.
You should only call add_channel and task.start once, then put the task.read in a loop.
08-27-2024 09:19 AM
This was the problem! Thank you for catching that. Here is the updated code for anyone who may have this problem in the future:
from nidaqmx.constants import AcquisitionType, EncoderType, AngleUnits, READ_ALL_AVAILABLE, Edge
def encoder():
# Define encoder variables
counter_name = 'cDAQ1Mod1/ctr0' #Our D I/O module is in slot 1 of our cDAQ-9174
encoder_type = EncoderType.X_2 #We are currently using X2 decoding. Better resolution than X1 but less sensitive to vibrations than X4
position_units = AngleUnits.DEGREES #We want the result in degrees
encoder_resolution = 8192 #Our encoder (SICK DGS80B-TSHC08192) has 8192 pulses per revolution
start_angle = 0.0 #Define the starting angle to be 0.0 degrees
current_angle = start_angle #Set current angle to start angle before current angle calculations happen in while loop
with nidaq.Task() as task:
task.ci_channels.add_ci_ang_encoder_chan(counter=counter_name, #passed in through main
decoding_type=encoder_type, #passed in through main
zidx_enable=False, #Explicitly set to false just to be sure
units=position_units, #passed in through main
pulses_per_rev=encoder_resolution, #passed in through main
initial_angle=start_angle) #passed in through main
task.start()
try:
while True: # While loop that is always running once main is called
data = task.read() # Read the task data
print(data)
# Error handling
except nidaqmx.DaqError as e:
print('An error occurred: ', e)
except KeyboardInterrupt:
print("\nStopped by user")
finally:
print('Task complete')
if __name__ == "__main__": # Run the main function
encoder()