epics-modules / motor

APS BCDA synApps module: motor
https://epics-modules.github.io/motor/
20 stars 47 forks source link

How to support seperate resolutions for position, velocity and acceleration with motorRecord? #215

Open ps3017 opened 4 months ago

ps3017 commented 4 months ago

Hi, I'm working with a Thorlabs Kinesis motor controller KDC101 and a Thorlabs motor Z825BV. Its device units to EGU (mm) conversion is different for distance, velocity and acceleration:

From motorRecord documentation I assumed one resolution (set in the field MRES) is used for position, velocity and acceleration - or is there already a way to specify different resolutions for these 3 parameters using motorRecord?

I've added 4 records to the .db file of my IOC in order to take into account the different resolutions for velocity and acceleration. 2 records _EGU are used to enter the desired value in engineering units and 2 record _v/accRes are used to calculate the difference between position resolution and velocity/acceleration resolution and sending calculated values to the motorRecord VELO and ACCL fields via the OUT field.

When I enter values to the PV: $(P)$(M)_VELO_EGU, the velocity is correctly calculated and both the VELO and S fields are updated (same goes for the PV: $(P)$(M)_ACCL_EGU and fields ACCL and JAR). However, when I try to move the motor relative or absolute the motor state is "Moving" (MSTA = 0x401), but the motor doesn't actually move, instead it stays stuck in the "Moving" state.

Is the processing of the 4 added records (2 x ai ,2 x calcout) with FLNK and OUT fields interfering with the processing of the main motor record? If so how can I change the database in order to include the resolution calculation and be able to move the motor normally?

This is the content of the .db file with the 4 added records:

record(motor, "$(P)$(M)") {
  field(DESC, "$(DESC)")
  field(DTYP, "$(DTYP)")
  field(DIR, "$(DIR)")
  field(VBAS, "$(VBAS)")
  field(ACCU, "$(ACCU=0)")
  field(BDST, "$(BDST)")
  field(BVEL, "$(BVEL)")
  field(BACC, "$(BACC)")
  field(OUT, "@asyn($(PORT),$(ADDR))")
  field(MRES, "$(MRES)")
  field(PREC, "$(PREC)")
  field(EGU, "$(EGU)")
  field(DHLM, "$(DHLM)")
  field(DLLM, "$(DLLM)")
  field(RTRY, "$(RTRY=10)")
  field(INIT, "$(INIT)")
  field(RSTM, "$(RSTM=NearZero)")
  field(TWV, "$(TWV)")
  field(SDIS, "$(P)$(M)_able.VAL")
}

# ***********************************************************

record(ai, "$(P)$(M)_VELO_EGU") {
    field(DESC, "velocity in EGU/s")
    field(VAL, "$(VELO_EGU)")
    field(EGU, "mm/sec")
    field(PREC, "4")
    field(FLNK, "$(P)$(M)_vRes")
}
 record(ai, "$(P)$(M)_ACCL_EGU") {
     field(DESC, "seconds to velocity in EGU")
     field(VAL, "$(ACCL_EGU)")
     field(EGU, "sec")
     field(PREC, "4")
     field(FLNK, "$(P)$(M)_accRes")
 }

record(calcout, "$(P)$(M)_vRes") {
  field(DESC, "calculate velocity resolution")
  field(CALC, "(772970/34555)*a")
  field(INPA, "$(P)$(M)_VELO_EGU")
  field(OUT, "$(P)$(M).VELO")
}

record(calcout, "$(P)$(M)_accRes") {
  field(DESC, "calculate acceletarion resolution")
  field(CALC, "(264/34555)*a")
  field(INPA, "$(P)$(M)_ACCL_EGU")
  field(OUT, "$(P)$(M).ACCL")
}

# ***********************************************************

record(bo, "$(P)$(M)_able") {
  field(DESC, "motor enable")
  field(PINI, "YES")
  field(OUT, "$(P)$(M).DISP")
  field(ZNAM, "Enable")
  field(ONAM, "Disable")
}

record(calcout, "$(P)$(M)_vCh") {
  field(DESC, "change velocity")
  field(CALC, "min(max(a*b,c),d)")
  field(INPB, "$(P)$(M).S")
  field(INPC, "$(P)$(M).SBAS")
  field(INPD, "$(P)$(M).SMAX")
  field(OUT, "$(P)$(M).S")
}

record(calcout, "$(P)$(M)_twCh") {
  field(DESC, "change TWV")
  field(CALC, "min(max(a*b,c),d-e)")
  field(INPB, "$(P)$(M).TWV")
  field(INPC, "$(P)$(M).MRES")
  field(INPD, "$(P)$(M).HLM")
  field(INPE, "$(P)$(M).LLM")
  field(OUT, "$(P)$(M).TWV")
}

# These records make the motor resolution, offset and direction available to the driver
# which is needed for profile moves and other applications

# Motor direction for this axis
record(longout,"$(P)$(M)Direction") {
    field(DESC, "$(M) direction")
    field(DOL,  "$(P)$(M).DIR CP MS")
    field(OMSL, "closed_loop")
    field(DTYP, "asynInt32")
    field(OUT,  "@asyn($(PORT),$(ADDR))MOTOR_REC_DIRECTION")
}

# Motor offset for this axis
record(ao,"$(P)$(M)Offset") {
    field(DESC, "$(M) offset")
    field(DOL,  "$(P)$(M).OFF CP MS")
    field(OMSL, "closed_loop")
    field(DTYP, "asynFloat64")
    field(OUT,  "@asyn($(PORT),$(ADDR))MOTOR_REC_OFFSET")
    field(PREC, "$(PREC)")
}

# Motor resolution for this axis
record(ao,"$(P)$(M)Resolution") {
    field(DESC, "$(M) resolution")
    field(DOL,  "$(P)$(M).MRES CP MS")
    field(OMSL, "closed_loop")
    field(DTYP, "asynFloat64")
    field(OUT,  "@asyn($(PORT),$(ADDR))MOTOR_REC_RESOLUTION")
    field(PREC, "$(PREC)")
}