• Python的电机控制模拟程序


    一个带有EPICS支持的虚拟电机控制器

    1)Status类:其实例化对象代表一个电机轴的状态。 

    1. #!/usr/bin/env python
    2. '''
    3. Status类代表一个电机处于的状态:
    4. 1、DIRECTION状态位:设置运动方向
    5. 2、DONE_MOVING状态字:置位表示结束运动
    6. 3、MOVING状态字:置位表示正在运动
    7. 4、HIGH_LIMIT状态字:置位表示触发高限位
    8. 5、LOW_LIMIT状态字:置位表示触发低限位
    9. 6、HOMING:置位表示正在寻HOME位
    10. 7、HOMING_LIMIT:置位表示触发HOME开关
    11. 8、HOMED:置位表示寻HOME结尾
    12. 9、ERROR:置位表示出错
    13. '''
    14. class Status:
    15. '''
    16. 代表轴状态的类:初始化这个对象
    17. '''
    18. def __init__(self):
    19. # 初始化方向,方向1表示正方向
    20. self.direction = 1
    21. # 初始化运动状态为静止
    22. self.doneMoving = 1
    23. self.moving = 0
    24. # 初始化高低限位信号为无效
    25. self.highLimitActive = 0
    26. self.lowLimitActive = 0
    27. # 初始化归位:未在归位中,归位无效
    28. self.homing = 0
    29. self.homed = 0
    30. self.homeSwitchActive = 0
    31. # 初始化错误:没有
    32. self.error = False
    33. self.errorMessage = None
    34. # 状态位定义:每种状态所在的位
    35. #方向:第0BIT、结束运行:第1BIT、移动中:第2BIT
    36. # 高限位:第3BIT、低限位:第4BIT、归位中:第5BI
    37. # 归位开关:第6BIT,已经归位:第7BIT,错误:第8BIT
    38. self.DIRECTION = 1 << 0
    39. self.DONE_MOVING = 1 << 1
    40. self.MOVING = 1 << 2
    41. self.HIGH_LIMIT = 1 << 3
    42. self.LOW_LIMIT = 1 << 4
    43. self.HOMING = 1 >> 5
    44. self.HOME_LIMIT = 1 << 6
    45. self.HOMED = 1 << 7
    46. self.ERROR = 1 << 8
    47. # 初始状态为0,通过以上状态定义计算状态
    48. self.status = 0
    49. self.calcStatus()
    50. # 设置错误状态码和错误消息
    51. def setError(self, flag, message):
    52. self.error = flag # 设置错误标记
    53. self.errorMessage = message
    54. # 错误标记非0,状态中置位ERROR对应的BIT位状态
    55. # 错误标记为0, 状态中复位ERROR对应的BIT位状态
    56. if self.error:
    57. self.status |= self.ERROR
    58. else:
    59. self.status &= ~self.ERROR
    60. # 设置为运动,结束移动的标志置0,运动的标志置1,状态中对结束移动标的志置0,对运动标志置1
    61. def setMoving(self):
    62. self.doneMoving = 0
    63. self.moving = 1
    64. self.status |= self.MOVING
    65. self.status &= ~self.DONE_MOVING
    66. # 设置为结束,结束移动的标志置1,运动的标志置0,状态中对结束移动标的志置1,对运动标志置0
    67. def setDoneMoving(self):
    68. self.doneMoving = 1
    69. self.moving = 0
    70. self.status |= self.DONE_MOVING
    71. self.status &= ~self.MOVING
    72. # 设置正方向,方向标志为1
    73. def setDirPositive(self):
    74. self.direction = 1
    75. self.status |= self.DIRECTION
    76. # 设置负方向,方向标志为0
    77. def setDirNegative(self):
    78. self.direction = 0
    79. self.status &= ~self.DIRECTION
    80. # 设置高限位为1
    81. def setHighLimit(self):
    82. self.highLimitActive = 1
    83. self.status |= self.HIGH_LIMIT
    84. # 重置高限位
    85. def resetHighLimit(self):
    86. self.highLimitActive = 0
    87. self.status &= ~self.HIGH_LIMIT
    88. # 设置低限位
    89. def setLowLimit(self):
    90. self.lowLimitActive = 1
    91. self.status |= self.LOW_LIMIT
    92. # 重置低限位
    93. def resetLowLimit(self):
    94. self.lowLimitActive = 0
    95. self.status &= ~self.LOW_LIMIT
    96. # 返回当前状态
    97. def getStatus(self):
    98. return self.status
    99. # 根据方向标志、结束运行的标志、移动的标志,高限位的标志,低限位的标志,归位的标志、归位开关标志、归位结束的标志,以及
    100. # 错误标志,构造状态
    101. # 根据Status对象的diretion, doneMoving, moving, highLimitActive, lowLimitActive, homing, homed, homeSwitchActive
    102. # error成员的状态计算成员status
    103. def calcStatus(self):
    104. status = 0
    105. if self.direction:
    106. status |= self.DIRECTION
    107. if self.doneMoving:
    108. status |= self.DONE_MOVING
    109. if self.moving:
    110. status |= self.MOVING
    111. if self.highLimitActive:
    112. status |= self.HIGH_LIMIT
    113. if self.lowLimitActive:
    114. status |= self.LOW_LIMIT
    115. if self.homing:
    116. status |= self.HOMING
    117. if self.homeSwitchActive:
    118. status |= self.HOME_LIMIT
    119. if self.homed:
    120. status |= self.HOMED
    121. if self.error:
    122. status |= self.ERROR
    123. self.status = status
    124. return

    2) Axis类,其实例化代表电机的一个运动轴:

    1. #!/usr/bin/env python
    2. import status
    3. import datetime
    4. import math
    5. import time
    6. class Axis:
    7. """
    8. 代表电机一个运动轴的类
    9. """
    10. def __init__(self, index):
    11. self.index = index
    12. # 基速度和匀速速度
    13. self.baseVelocity = 0
    14. self.velocity = 400
    15. # 加速度和减速度
    16. self.acceleration = 400
    17. self.deceleration = 400
    18. # 上下限位
    19. self.highLimit = 40000
    20. self.lowLimit = -40000
    21. # 单位
    22. self.units = "counts"
    23. # 分辨率
    24. self.resolution = 1.0
    25. # 启动开始时刻,移动取消时刻
    26. self.moveStartTime = None
    27. self.abortTime = None
    28. # 轴的上次位置,当前位置,当前偏移量,目标位置,方向,移动速度
    29. self.lastPosition = 0
    30. self.currentPosition = 0
    31. self.currentDisplacement = 0
    32. self.targetPosition = 0
    33. self.direction = 1
    34. self.moveVelocity = self.velocity
    35. # 加速持续时间,加速阶段的距离,减速持续时间,减速阶段的距离
    36. self.accelDistance = 0.0
    37. self.accelDuration = 0.0
    38. self.decelDistance = 0.0
    39. self.decelDuration = 0.0
    40. # 移动距离,匀速持续时间,减速启动时间,移动持续时间
    41. self.moveDistance = 0
    42. self.constVelDuration = 0.0
    43. self.decelStartTime = 0.0
    44. self.moveDuration = 0.0
    45. # 默认执行限位检查
    46. self.enforceLimits = True
    47. # 实例化一个默认的Status对象,代表电机轴的状态
    48. self.status = status.Status()
    49. def move(self, targetPosition):
    50. # 已经移动,则忽略,检查启动时刻是否已经存在
    51. if self.moveStartTime != None:
    52. return "Busy"
    53. self.targetPosition = targetPosition
    54. self.lastPosition = self.currentPosition
    55. # 比较当前位置和目标位置,来设置方向
    56. # 此处的direction用于计算位置
    57. if self.targetPosition < self.lastPosition:
    58. self.direction = -1
    59. self.status.setDirNegative()
    60. else:
    61. self.direction = 1
    62. self.status.setDirPositive()
    63. print("move:direction:" , self.direction)
    64. # 检查上下限位情况
    65. if (self.enforceLimits == True) and (self.direction == 1) and (self.status.highLimitActive == 1):
    66. self.status.setError(True, "Can't move in positive direction when high limit is active")
    67. print("hh")
    68. elif (self.enforceLimits == True) and (self.direction == -1) and (self.status.lowLimitActive == 1):
    69. self.status.setError(True, "Can't move in negative direction when low limit is active")
    70. print("ll")
    71. else:
    72. # 设置启动时刻,表示电机轴开始移动了
    73. self.moveStartTime = datetime.datetime.now()
    74. # 计算移动距离
    75. self.moveDistance = abs(self.targetPosition - self.lastPosition)
    76. print("moveDistance , ", self.moveDistance)
    77. # 加速时间
    78. self.accelDuration = (self.velocity - self.baseVelocity) / self.acceleration
    79. # 加速过程中,移动的距离:vb*t+1/2*a*t^2,a=(v-vb)/t==>vb*t+0.5*(v-vb)*t
    80. self.accelDistance = self.baseVelocity * self.accelDuration + self.accelDuration * 0.5 * (self.velocity - self.baseVelocity)
    81. # 减速时间,减速过程的距离
    82. self.decelDuration = (self.velocity - self.baseVelocity) / self.deceleration
    83. #! debug: print("decelDuration = ", self.decelDuration)
    84. self.decelDistance = self.baseVelocity * self.decelDuration + self.decelDuration * 0.5 * (self.velocity - self.baseVelocity)
    85. if self.moveDistance < (self.accelDistance + self.decelDistance):
    86. # 这点距离不能使得轴达到运行速度,加速到一个峰值速度后,就进行减速
    87. peakVelocity = math.sqrt(2 * self.acceleration * self.deceleration * self.moveDistance / (self.acceleration + self.deceleration))
    88. print("---------+-------------")
    89. print("peakVelocity = ", peakVelocity)
    90. self.moveVelocity = peakVelocity
    91. # 重新计算:加速所用时间,加速的距离,减速所用时间,减速的距离
    92. self.accelDuration = (peakVelocity - self.baseVelocity) / self.acceleration
    93. self.accelDistance = self.baseVelocity * self.accelDuration + self.accelDuration * 0.5 * (peakVelocity - self.baseVelocity)
    94. self.decelDuration = (peakVelocity - self.baseVelocity) / self.deceleration
    95. self.decelDistance = self.baseVelocity * self.decelDuration + self.decelDuration * 0.5 * (peakVelocity - self.baseVelocity)
    96. self.constVelDuration = 0.0
    97. else:
    98. self.moveVelocity = self.velocity
    99. # 匀速移动距离,匀速移动时间
    100. self.constVelDuration = (self.moveDistance - self.accelDistance - self.decelDistance) / self.moveVelocity
    101. # 开始减速的时刻
    102. self.decelStartTime = self.accelDuration + self.constVelDuration
    103. # 整个移动时间
    104. self.moveDuration = self.decelStartTime + self.decelDuration
    105. # 打印轴移动的信息:
    106. # 轴编码,起始位置,终止位置,移动距离,移动持续时间,加速距离,匀速时间
    107. # 减速时刻,减速持续时间,减速距离
    108. print("+-----------motor %d :" % (self.index + 1))
    109. print("Start Position: ", self.lastPosition, self.units)
    110. print("End Position: ", self.targetPosition, self.units)
    111. print()
    112. print("Move Duration : ", self.moveDuration, "seconds")
    113. print("Move Distance : ", self.moveDistance, self.units)
    114. print()
    115. print("Accel Duration: ", self.accelDuration, "seconds")
    116. print("Accel Distance: ", self.accelDistance, self.units)
    117. print()
    118. print("Constant Vel Duration: ", self.constVelDuration, "seconds")
    119. print("Decel Start Time : ", self.decelStartTime, "seconds")
    120. print()
    121. print("Decel Duration : ", self.decelDuration, "seconds")
    122. print("Decel Distance : ", self.decelDistance, self.units)
    123. print()
    124. return "OK"
    125. def moveRelative(self, displacement):
    126. if self.moveStartTime != None:
    127. return "Busy"
    128. self.lastPosition = self.currentPosition
    129. targetPosition = self.lastPosition + displacement
    130. print("current %s to target %s" % (self.lastPosition, targetPosition))
    131. retval = self.move(targetPosition)
    132. return retval
    133. def jog(self, velocity):
    134. print("Velocity: ", velocity)
    135. displacement = velocity * 3600.0
    136. retval = self.moveRelative(displacement)
    137. return retval
    138. def stop(self):
    139. if self.moveStartTime == None: #未开始移动
    140. self.abortTime = None
    141. else:
    142. if self.abortTime != None: # 已经发送了取消命令
    143. pass
    144. else:
    145. # 记录当前取消时刻
    146. self.abortTime = datetime.datetime.now()
    147. # 到取消时刻已经运行了多少时间
    148. abortTimeDelta = self.abortTime - self.moveStartTime
    149. abortTimeSeconds = abortTimeDelta.total_seconds()
    150. # 重新计算移动的距离
    151. if abortTimeSeconds < self.accelDuration: # 在加速阶段就取消了
    152. self.accelDuration = abortTimeSeconds
    153. self.accelDistance = self.baseVelocity * abortTimeSeconds + 0.5 * self.acceleration * abortTimeSeconds * abortTimeSeconds
    154. peakVelocity = self.acceleration * abortTimeSconds
    155. self.constVelDuration = 0.0
    156. self.decelDuration = (peakVelocity - baseVelocity)/ self.deceleraction
    157. self.decelDistance = self.baseVelocity * self.decelDuration + 0.5 * self.deceleration * self.decelDuration * self.decelDuration
    158. # 实际移动距离=加速的距离+减速的距离
    159. self.moveDistance = self.accelDistance + self.decelDistance
    160. # 实际移动时间=加速所用时间+减速所用时间
    161. self.moveDuration = self.accelDuration + self.decelDuration
    162. # 移动峰值速度
    163. self.moveVelocity = peakVelocity
    164. elif abortTimeSeconds < self.decelStartTime: # 在匀速阶段取消
    165. self.decelStartTime = abortTimeSeconds
    166. # 匀速运动所用时间
    167. self.constVelDuration = abortTimeSeconds - self.accelDuration
    168. # 移动距离
    169. self.moveDistance = self.accelDistance + self.moveVelocity * self.constVelDuration + self.decelDistance
    170. # 移动的时间
    171. self.moveDuration = self.accelDuration + self.constVelDuration + self.decelDuration
    172. elif abortTimeSeconds <= self.moveDuration: # 在减速阶段取消
    173. pass
    174. else:
    175. print("Error: Stop received after a move shoud have been complete.")
    176. self.status.setError(True, "Error: Stop received after a move should been complete.")
    177. return "OK"
    178. # 读取当前位置
    179. def readPosition(self):
    180. if self.moveStartTime == None: # 电机轴未移动
    181. pass
    182. else:
    183. # 设置移动标志
    184. moveFlag = True
    185. currentTime = datetime.datetime.now()
    186. # 计算移动时间:当前时间 - 启动时间
    187. movingTimeDelta = currentTime - self.moveStartTime
    188. movingTimeSeconds = movingTimeDelta.total_seconds()
    189. print("readPosition timedelta: ", movingTimeSeconds)
    190. self.currentDisplacement = 0
    191. if movingTimeSeconds < self.accelDuration: # 加速阶段中,偏移量
    192. self.currentDisplacement = self.baseVelocity * movingTimeSeconds + 0.5 * self.acceleration * movingTimeSeconds * movingTimeSeconds
    193. elif movingTimeSeconds < self.decelStartTime: # 匀速阶段中,偏移量
    194. self.currentDisplacement = self.baseVelocity * self.accelDuration + 0.5 * self.acceleration * self.accelDuration * self.accelDuration + (movingTimeSeconds - self.accelDuration) * self.moveVelocity
    195. elif movingTimeSeconds < self.moveDuration: # 减速阶段中,偏移量
    196. self.currentDisplacement = self.baseVelocity * self.accelDuration + 0.5 * self.acceleration * self.accelDuration * self.accelDuration + self.constVelDuration * self.moveVelocity + self.baseVelocity * (movingTimeSeconds - self.decelStartTime) + 0.5 * self.deceleration * (movingTimeSeconds - self.decelStartTime) * (movingTimeSeconds - self.decelStartTime)
    197. else: # 已经超出了运行时间, 设置移动标志
    198. moveFlag = False
    199. #print("In readPosition--> currentDisplacement", self.currentDisplacement)
    200. print("in readPosition: direction : ", self.direction)
    201. if moveFlag == True:# 还在移动,计算当前位置
    202. self.currentPosition = self.lastPosition + self.direction * self.currentDisplacement
    203. else:
    204. if self.abortTime == None: # 运动自然结束
    205. self.currentPosition = self.targetPosition
    206. else:# 运动被取消
    207. self.currentPostion = self.lastPosition + self.direction * self.moveDistance
    208. self.abortTime = None
    209. self.latPosition = self.currentPosition
    210. self.moveStartTime = None
    211. return self.currentPosition
    212. def setPosition(self, newPostion):
    213. if self.moveStartTime == None: # 电机轴未移动
    214. self.currentPosition = newPosition
    215. self.lastPosition = self.currentPosition
    216. else:
    217. pass
    218. return "OK"
    219. def readStatus(self):
    220. if self.moveStartTime == None:
    221. self.status.setDoneMoving()
    222. else:
    223. currentTime = datetime.datetime.now()
    224. movingTimeDelta = currentTime - self.moveStartTime
    225. movingTimeSeconds = movingTimeDelta.total_seconds()
    226. if movingTimeSeconds < self.moveDuration:
    227. self.status.setMoving()
    228. else:
    229. self.status.setDoneMoving()
    230. if self.enforceLimits == True:
    231. if self.currentPosition > self.highLimit:
    232. self.status.setHighLimit()
    233. if (self.status.doneMoving == 0) and (self.direction == 1):
    234. self.stop()
    235. else:
    236. self.status.resetHighLimit()
    237. if self.currentPosition < self.lowLimit:
    238. self.status.setLowLimit()
    239. if (self.status.doneMoving == 0) and (self.direction == -1):
    240. self.stop()
    241. else:
    242. self.status.resetLowLimit()
    243. else:
    244. self.status.resetLowLimit()
    245. self.status.resetHighLimit()
    246. return self.status.getStatus()
    247. def setVelocity(self, velocity):
    248. self.velocity = abs(velocity)
    249. return "OK"
    250. def getVelocity(self):
    251. return self.velocity
    252. def setBaseVelocity(self, velocity):
    253. self.baseVelocity = abs(velocity)
    254. return "OK"
    255. def getBaseVelocity(self):
    256. return self.baseVelocity
    257. def setAcceleration(self, acceleration):
    258. self.acceleration = acceleration
    259. return "OK"
    260. def setDeceleration(self, deceleration):
    261. self.deceleration = accceleration
    262. return "OK"
    263. def getAcceleration(self):
    264. return self.acceleration
    265. def getDeceleration(self):
    266. return self.deceleration
    267. def setHighLimit(self, highLimit):
    268. self.highLimit = highLimit
    269. return "OK"
    270. def getHighLimit(self):
    271. return self.highLimit
    272. def setLowLimit(self, lowLimit):
    273. self.lowLimit = lowLimit
    274. return "OK"
    275. def getLowLimit(self):
    276. return self.lowLimit
    277. # 以下是测试一个Axis实例的代码
    278. if __name__ == "__main__":
    279. print("working")
    280. axis = Axis(1)
    281. print("Velocity:", axis.getVelocity())
    282. print("BaseVelocity:",axis.getBaseVelocity())
    283. print("Acceleratoin:", axis.getAcceleration())
    284. print("High Limit:", axis.getHighLimit())
    285. print("Low Limit", axis.getLowLimit())
    286. print()
    287. print("Start Move 1", axis.move(1000))
    288. print()
    289. for i in range(10):
    290. pos = axis.readPosition()
    291. status = axis.readStatus()
    292. print("pos: %s, status: %s" % (pos, status))
    293. time.sleep(0.5)
    294. print("move stop\n")
    295. print("lastPosition: " , axis.lastPosition)
    296. print("currentPosition: ", axis.currentPosition)
    297. print("Start Move 2", axis.moveRelative(-1000))
    298. for i in range(10):
    299. pos = axis.readPosition()
    300. status = axis.readStatus()
    301. print("pos: %s, status: %s" % (pos, status))
    302. time.sleep(0.5)
    303. print("move stop\n")
    304. print("lastPosition: " , axis.lastPosition)
    305. print("currentPosition: ", axis.currentPosition)

    3) 控制器的类:一个电机控制器

    1. #!/usr/bin/evn python3
    2. import axis
    3. import time
    4. class Controller:
    5. """
    6. 代表电机控制器的类
    7. """
    8. def __init__(self):
    9. # 控制器中有8个轴
    10. self.numAxes = 8
    11. # 轴名称的列表
    12. self.axisNameList = ['X', 'Y', 'Z','T', 'U', 'V', 'R','S']
    13. # 轴数值编号的列表
    14. self.axisNumberList = [str(x) for x in range(1, self.numAxes + 1)]
    15. # 命令字典
    16. self.commandDict = {3:{'MV': self.moveAxis,
    17. 'MR':self.moveRelative,
    18. 'JOG':self.jog,
    19. 'POS':self.setPosition,
    20. 'ACC':self.setAcceleration,
    21. 'VEL':self.setVelocity,
    22. 'BAS':self.setBaseVelocity,
    23. 'LL':self.setLowLimit,
    24. 'HL':self.setHighLimit},
    25. 2:{'POS?':self.queryPosition,
    26. 'ST?':self.queryStatus,
    27. 'ACC?':self.queryAcceleration,
    28. 'VEL?':self.queryVelocity,
    29. 'LL?':self.queryLowLimit,
    30. 'HL?':self.queryHighLimit,
    31. 'AB':self.stopAxis}
    32. }
    33. # 轴对象的字典
    34. self.axisDict = {}
    35. # 轴对象的列表
    36. self.axisList = []
    37. # 默认执行限位检查
    38. self.enforceLimits = True
    39. for i in range(self.numAxes): #实例子化八个Axis对象,
    40. # 追加到一个列表末尾
    41. self.axisList.append(axis.Axis(i))
    42. # 键值对:轴名称----axis对象索引号; 轴编号----Axis对象索引号
    43. self.axisDict[self.axisNameList[i]] = i
    44. self.axisDict[self.axisNumberList[i]] =i
    45. print(self.axisDict) # 打印字典
    46. print(self.axisDict.keys()) # 打印字典的键
    47. def refinePos(self, inputPos):
    48. # 把来自Axis对象的raw位置转换成一个合适输出的东西
    49. # 返回一个int,由于控制器使用单位为计数
    50. return round(inputPos)
    51. def handleCommand(self, command):
    52. # 命令字符串格式: 轴名称/轴编号 命令名称 <命令参数> 或者 轴名称/轴编号 命令名称
    53. print("In Controller ", command)
    54. if command == '':
    55. retVal = None
    56. else:
    57. args = command.split(' ')
    58. numArgs = len(args) # 获取命令串中分隔出的参数数目
    59. print("split params:", args, "numArgs:", numArgs)
    60. print("commandDict.keys()", self.commandDict.keys())
    61. print("axisDict.keys()", self.axisDict.keys())
    62. print("2 parameters command", self.commandDict[2].keys())
    63. print("3 parameters command", self.commandDict[3].keys())
    64. if numArgs not in self.commandDict.keys(): # 参数数目不为2或3, 不对
    65. retVal = "Argument error"
    66. elif args[0] not in self.axisDict.keys():
    67. retVal = "Axis name error" # 给的轴名称/轴编号错误
    68. else: #如果是3个字符串的参数,则格式如 X MV 400
    69. if args[1] in self.commandDict[numArgs].keys(): # 命令名称出错
    70. if numArgs == 2: # 轴 + 命令
    71. retVal = self.commandDict[numArgs][args[1]](args[0])
    72. elif numArgs == 3: # 轴 + 命令 + 命令参数
    73. print("command: %s %s %s" % (args[1], args[0], args[2]))
    74. retVal = self.commandDict[numArgs][args[1]](args[0], args[2])
    75. else:
    76. retVal = "Strange error"
    77. print("retVal:", retVal)
    78. return retVal
    79. def queryPosition(self, axis):
    80. # 由于控制器单位是计数,取整结果
    81. return self.refinePos(self.axisList[self.axisDict[axis]].readPosition())
    82. def setPosition(self, axis, pos):
    83. return self.axisList[self.axisDict[axis]].setPosition(int(pos))
    84. def queryStatus(self, axis):
    85. return self.axisList[self.axisDict[axis]].readStatus()
    86. def moveAxis(self, axis, pos):
    87. return self.axisList[self.axisDict[axis]].move(int(pos))
    88. def moveRelative(self, axis, pos):
    89. return self.axisList[self.axisDict[axis]].moveRelative(int(pos))
    90. def jog(self, axis, velocity):
    91. return self.axisList[self.axisDict[axis]].jog(float(velocity))
    92. def stopAxis(self, axis):
    93. return self.axisList[self.axisDict[axis]].stop()
    94. def setVelocity(self, axis, velocity):
    95. return self.axisList[self.axisDict[axis]].setVelocity(float(velocity))
    96. def queryVelocity(self, axis):
    97. return self.axisList[self.axisDict[axis]].readVelocity()
    98. def setBaseVelocity(self, axis, velocity):
    99. return self.axisList[self.axisDict[axis]].setBaseVelocity(float(velocity))
    100. def queryBaseVelocity(self, axis):
    101. return self.axisList[self.axisDict[axis]].readBaseVelocity()
    102. def setAcceleration(self, axis, acceleration):
    103. return self.axisList[self.axisDict[axis]].setAcceleration(float(acceleration))
    104. def queryAcceleration(self, axis):
    105. return self.axisList[self.axisDict[axis]].readAcceleration()
    106. def queryHighLimit(self, axis):
    107. return self.axisList[self.axisDict[axis]].readHighLimit()
    108. def setHighLimit(self, axis, highLimit):
    109. return self.axisList[self.axisDict[axis]].setHighLimit(int(highLimit))
    110. def queryLowLimit(self, axis):
    111. return self.axisList[self.axisDict[axis]].readLowLimit()
    112. def setLowLimit(self, axis, lowLimit):
    113. return self.axisList[self.axisDict[axis]].setLowLimit(int(lowLimit))
    114. # 此处是测试一个控制器实例的代码
    115. if __name__ == "__main__":
    116. controller = Controller()
    117. print("Test X axis:")
    118. print(controller.queryStatus("X"))
    119. print()
    120. print(controller.moveAxis("X",1000))
    121. for i in range(10):
    122. time.sleep(0.5)
    123. pos = controller.queryPosition("X")
    124. status = controller.queryStatus("X")
    125. print("pos: %s, status: %s" % (pos, status))
    126. print()
    127. print(controller.moveAxis("X",0))
    128. for i in range(10):
    129. time.sleep(0.5)
    130. pos = controller.queryPosition("X")
    131. status = controller.queryStatus("X")
    132. print("pos: %s, status: %s" % (pos, status))

    4) 服务器程序:为电机控制器提供网路服务

    服务器表现类似一个8轴控制器。
    默认轴值保持与半步模式的步进电机一致(每个分辨400步)。
    可以用名称(X, Y, Z, T, U, V, R, S)或者数值(1, 2, 3, 4, 5, 6, 7, 8)访问轴。
    控制器接受以计数单位的值。为了响应非查询命令,服务器返回一个"OK"。

    启动服务器
    $ python3 server.py
    这将启动这个服务器,它默认在31337端口上监听。
    可以通过修改server.py中的DEFAULT_PORT更高这个端口号。

    命令参考:
    输入终止符: \r\n
    输出终止符: \r

    命令语法: [argument]
    命令:

    • axis MV                         # 绝对移动(计数)
    • axis MR                # 相对移动(计数)
    • axis  JOG                       # Jog (计数/s, 符号)
    • axis POS                      # 设置位置 (计数)
    • axis ACC               # 设置加速度(计数/s/s)
    • axis VEL                       # 设置速度 (计数/s)
    • axis BAS             # 设置基速度(计数/s)
    • axis AB                                        # 取消移动
    • axis POS?                                   # 查询位置(返回:计数)
    • axis ST?                                      # 查询状态(返回:整数)
    1.      状态位
    2.      方向:        0x1
    3.      结束移动:    0x2
    4.      移动中:    0x4
    5.      高限制:    0x8
    6.      低限位:    0x10
    7.      寻home:    0x20
    8.      home限位:    0x40
    9.      已经找到home:        0x80
    10.      错误:        0x100
    • axis ACC?                    # 查询加速度(返回:计数/s/s)
    • axis VEL?                    # 查询速度(返回:计数/s)
    • axis LL        # 设置低限位(计数)
    • axis HL       # 设置高限位(计数)
    • axis LL?                     # 查询低限位返回:计数)
    • axis HL?                    # 查询高限位返回:计数)
    1. #!/usr/bin/env python3
    2. import getopt
    3. import os
    4. import sys
    5. import asyncore
    6. import asynchat
    7. import socket
    8. import controller
    9. DEFAULT_PORT = 6666
    10. class ConnectionDispatcher(asyncore.dispatcher):
    11. def __init__(self, port):
    12. asyncore.dispatcher.__init__(self)
    13. self.port = port
    14. self.device = controller.Controller()
    15. self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    16. self.set_reuse_addr()
    17. self.bind(("", port))
    18. self.listen(5)
    19. def handle_accept(self):
    20. # client_info is a tuple with socket as the 1st element
    21. client_info = self.accept()
    22. ConnectionHandler(client_info[0], self.device)
    23. class ConnectionHandler(asynchat.async_chat):
    24. ## regular expressions, if necessary, can go here
    25. def __init__(self, sock, device):
    26. asynchat.async_chat.__init__(self, sock)
    27. self.set_terminator(b"\r")
    28. #
    29. self.outputTerminator = "\r\n"
    30. self.device = device
    31. self.buffer = ""
    32. def collect_incoming_data(self, data):
    33. self.buffer = self.buffer + data.decode()
    34. def found_terminator(self):
    35. data = self.buffer
    36. self.buffer = ""
    37. self.handleClientRequest(data)
    38. def handleClientRequest(self, request):
    39. request = request.strip()
    40. # 打印接收到的命令
    41. print("command from client:",request)
    42. response = self.device.handleCommand(request)
    43. if response != None:
    44. self.sendClientResponse("{}".format(response))
    45. # 打印发送给客户端的命令:
    46. print("comand sent to client:", response)
    47. print("send finished!")
    48. return
    49. def sendClientResponse(self, response=""):
    50. data = response + self.outputTerminator
    51. self.push(data.encode())
    52. # 获取本程序名
    53. def getProgramName(args=None):
    54. # 获取命令行参数列表, args[0]即是程序名
    55. if args == None:
    56. args = sys.argv
    57. if len(args) == 0 or args[0] == "-c":
    58. return "PROGRAM_NAME"
    59. print(args)
    60. return os.path.basename(args[0])
    61. # 打印这个程序的使用方法
    62. def printUsage():
    63. print("""\
    64. Usage: {} [-ph]
    65. -p, --port=NUMBER Listen on the specified port NUMBER for incoming
    66. connections (default:{})
    67. -h, --help Print usage message and exit""".format(getProgramName(), DEFAULT_PORT)
    68. )
    69. # 解析命令行参数,并且返回一个端口号
    70. def parseCommandLineArgs(args):
    71. # 指定长参数和短参数格式中的选项名称:-p -help; --port= --help
    72. # 解析后选项被放入一个元组列表 [('-p', port), ('--port', 'port'), ...]
    73. # 参数后面带:或者=的选项,必须有选项参数
    74. (options, extra) = getopt.getopt(args[1:], "p:h", ["port=", "help"])
    75. port = DEFAULT_PORT
    76. # 用于调试
    77. #!print(options)
    78. #!print(extra)
    79. # 除选项及对应的选项参数外,还有其它参数
    80. if len(extra) > 0:
    81. print("Error: unexpected command-line argument \"{}\"".format(extra[0]))
    82. printUsage()
    83. sys.exit(1)
    84. for eachOptName, eachOptValue in options:
    85. if eachOptName in ("-p", "--port"):
    86. port = int(eachOptValue)
    87. elif eachOptName in ("-h", "--help"):
    88. printUsage()
    89. sys.exit(0)
    90. return port
    91. def main(args):
    92. port = parseCommandLineArgs(args)
    93. server = ConnectionDispatcher(port)
    94. print("Use Port: ", port)
    95. try:
    96. asyncore.loop()
    97. except KeyboardInterrupt:
    98. print()
    99. print("Shutting down the server...")
    100. sys.exit(0)
    101. if __name__ == "__main__":
    102. # 检测python版本
    103. if sys.version_info < (3,0,0) and sys.version_info < (3,12,0):
    104. sys.stderr.write("You need Python 3.0 or later (but less than 3.12) to run this script\n")
    105. input("Press enter to quit... ")
    106. sys.exit(1)
    107. # Try to run the server
    108. try:
    109. main(sys.argv)
    110. except Exception as e:
    111. if isinstance(e, SystemExit):
    112. raise e
    113. else:
    114. print("Error: {}".format(e))
    115. sys.exit(1)

    4) 运行以上服务器代码,并且用客户端测试:

    客户端测试了MV,POS?,ST?三个命令:

    • MV:移动电机轴到指定位置。
    • POS?:查询电机轴当前处于的位置。
    • ST?:查询电机轴的状态。

    Python电机仿真程序用于练习EPICS电机控制器驱动程序(EPICS motor驱动程序实例_EPICS Technical的博客-CSDN博客)的编写。 

  • 相关阅读:
    猿人学第一题
    只用二十行代码,用Python实现获取网抑云榜单文件保存本地,非常简单...
    编程基础都要懂的计算机组成
    零距离揭秘绝地求生:玩家最关心的吃鸡要领和细节全攻略!
    软考76-上午题-【面向对象技术3-设计模式】-创建型设计模式01
    机器学习之增强学习DQN(Deep Q Network)
    【场景化解决方案】慧穗云开票,让钉钉与业务数据流转更灵活
    MySQL 索引
    Python每日一练-DAY01
    【LIN总线测试】——LIN主节点调度表测试
  • 原文地址:https://blog.csdn.net/yuyuyuliang00/article/details/132483050