RobotData.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. import enum
  2. import time
  3. import message_pb2 as message
  4. from mqtt_async import MqttAsync
  5. import google.protobuf.json_format as jtf
  6. from dijkstra.Map import DijikstraMap,SpaceNode,StreetNode
  7. import uuid
  8. import random
  9. import math
  10. import Count
  11. class ActType(enum.Enum):
  12. eReady=0
  13. eRotation=1
  14. eHorizon=2
  15. eVertical=3
  16. eMPC=4
  17. eClampOpen=5
  18. eClampClose=6
  19. class TimeStatu:
  20. def __init__(self, statu=None, timeout=3):
  21. self.statu = statu
  22. self.time = time.time()
  23. self.timeout_ms = timeout
  24. def timeout(self):
  25. tm = time.time()
  26. return tm - self.time > self.timeout_ms or self.statu == None
  27. class Robot(MqttAsync):
  28. def __init__(self, name=""):
  29. MqttAsync.__init__(self)
  30. self.timedRobotStatu_ = TimeStatu(message.RobotStatu, 0)
  31. self.timedNavStatu_ = TimeStatu(message.NavStatu, 0)
  32. self.dataTopic_ = {}
  33. self.messageArrivedSignal = None
  34. self.currentNavData_ = None
  35. self.navCmdTopic_ = None
  36. self.currentNavPathNodes_ = None
  37. self.currentNavPath_ = None
  38. self.pathColor_ = [1, 1, 0]
  39. self.robotColor_ = [0.7, 0.2, 0.3] # agv当前颜色
  40. self.name_ = name
  41. self.begId_="------"
  42. self.targetId_="------"
  43. self.autoTest_=False
  44. self.l_ = 0.8 # 轮长
  45. self.L_ = 1.3 # 轴距
  46. self.W_ = 2.5 # 宽
  47. def Color(self):
  48. if self.IsMainModeStatu():
  49. return [0, 0, 0]
  50. return self.robotColor_
  51. def IsMainAgv(self):
  52. if self.timedNavStatu_.timeout() == False:
  53. if self.timedNavStatu_.statu.main_agv == True:
  54. return True
  55. return False
  56. def IsMainModeStatu(self):
  57. if self.IsMainAgv():
  58. if self.timedNavStatu_.timeout() == False:
  59. if self.timedNavStatu_.statu.move_mode == 2:
  60. return True
  61. return False
  62. def SetSubDataTopic(self, match: dict, messageSignal=None):
  63. self.dataTopic_ = match
  64. self.messageArrivedSignal = messageSignal
  65. for item in match.items():
  66. [topic, _] = item
  67. self.subscribe(topic, self.on_message)
  68. def SetCmdTopic(self, topic):
  69. self.navCmdTopic_ = topic
  70. def SetColor(self, pathColor, robotColor):
  71. self.pathColor_ = pathColor
  72. self.robotColor_ = robotColor
  73. def ResetPose(self, agv: message.RobotStatu):
  74. self.timedRobotStatu_ = TimeStatu(agv, 2)
  75. def ResetNavStatu(self, statu: message.NavStatu):
  76. self.timedNavStatu_ = TimeStatu(statu, 2)
  77. def on_message(self, client, userdata, msg):
  78. topic = msg.topic
  79. if self.dataTopic_.get(topic) is not None:
  80. dtype = self.dataTopic_[topic]
  81. if dtype == message.RobotStatu.__name__:
  82. proto = message.RobotStatu()
  83. jtf.Parse(msg.payload.decode(), proto)
  84. self.ResetPose(proto)
  85. if dtype == message.NavStatu.__name__:
  86. self.last_running = self.IsNavigating()
  87. proto = message.NavStatu()
  88. jtf.Parse(msg.payload.decode(), proto)
  89. self.ResetNavStatu(proto)
  90. if self.last_running == True and self.IsNavigating() == False:
  91. self.currentNavPathNodes_ = None
  92. self.currentNavPath_ = None
  93. if self.messageArrivedSignal is not None:
  94. self.messageArrivedSignal()
  95. def MpcSelectTraj(self):
  96. traj = []
  97. if self.timedNavStatu_.timeout() == False:
  98. nav = self.timedNavStatu_.statu
  99. for pose2d in nav.selected_traj.poses:
  100. traj.append([pose2d.x, pose2d.y, pose2d.theta])
  101. return traj
  102. def MpcPredictTraj(self):
  103. traj = []
  104. if self.timedNavStatu_.timeout() == False:
  105. nav = self.timedNavStatu_.statu
  106. for pose2d in nav.predict_traj.poses:
  107. traj.append([pose2d.x, pose2d.y, pose2d.theta])
  108. return traj
  109. def Connected(self):
  110. return self.timedNavStatu_.timeout() == False and self.timedRobotStatu_.timeout() == False
  111. def IsNavigating(self):
  112. return not self.ActionType()==ActType.eReady
  113. '''if self.timedNavStatu_.timeout() == False:
  114. key = self.timedNavStatu_.statu.key
  115. if key == "" or key == None:
  116. return False
  117. return True'''
  118. def GeneratePath(self, begID, endID):
  119. self.currentNavPathNodes_ = DijikstraMap().GetShortestPath(begID, endID)
  120. self.currentNavPath_ = DijikstraMap().CreatePath(self.currentNavPathNodes_, 0.5)
  121. if self.currentNavPathNodes_ is not None and self.currentNavPath_ is not None:
  122. self.begId_=begID
  123. self.targetId_=endID
  124. return True
  125. return False
  126. def PositionId(self):
  127. if self.timedRobotStatu_.timeout():
  128. return None
  129. x=self.timedRobotStatu_.statu.x
  130. y=self.timedRobotStatu_.statu.y
  131. djks_map = DijikstraMap()
  132. [label, pt] = djks_map.findNeastNode([x, y])
  133. return [label,pt]
  134. '''
  135. 阻塞直到导航完成
  136. '''
  137. def Navigatting(self, begId, targetId,autoDirect):
  138. print("nav")
  139. self.GeneratePath(begId, targetId)
  140. #self.ExecPathNodes(autoDirect)
  141. self.ExecNavtask(autoDirect)
  142. while self.IsNavigating() == True:
  143. '''if self.Connected() == False:
  144. print("robot disconnected cancel task")
  145. self.CancelNavTask()
  146. return False'''
  147. time.sleep(0.5)
  148. print(" Nav completed!!!")
  149. if self.IsMainModeStatu():
  150. Count.TestCount().loadCountAdd()
  151. else:
  152. Count.TestCount().singleCountAdd()
  153. return True
  154. def AutoTestNavClamp(self,begId,targetId):
  155. beg=begId
  156. target=targetId
  157. while self.autoTest_:
  158. if self.TestNavClampOnce(beg,target)==False:
  159. print(" quit auto Test")
  160. break
  161. print("1111")
  162. posInfo=self.PositionId()
  163. if posInfo is not None:
  164. [label,pt]=posInfo
  165. beg=label
  166. print("beg",beg)
  167. node=DijikstraMap().GetVertex(beg)
  168. if node is not None:
  169. if isinstance(node,(SpaceNode)):
  170. target="O"
  171. if isinstance(node,(StreetNode)):
  172. end_nodes=["E","B","C","D"]
  173. id=random.randint(0,1000)%4
  174. target=end_nodes[id]
  175. print("3333",target)
  176. print(" Next nav clamp cmd,%s to %s"%(beg,target))
  177. def ActionType(self):
  178. if self.timedNavStatu_.timeout() == False:
  179. runningStatu=self.timedNavStatu_.statu
  180. if runningStatu.statu==0:
  181. return ActType.eReady
  182. if runningStatu.statu==1:
  183. return ActType.eRotation
  184. if runningStatu.statu==2:
  185. return ActType.eHorizon
  186. if runningStatu.statu==3:
  187. return ActType.eVertical
  188. if runningStatu.statu==4:
  189. return ActType.eMPC
  190. if runningStatu.statu==5:
  191. return ActType.eClampOpen
  192. if runningStatu.statu==6:
  193. return ActType.eClampClose
  194. else:
  195. return ActType.eReady
  196. @staticmethod
  197. def generateProtoNode(node,accuracy):
  198. [dx,dy,dyaw]=accuracy
  199. protoNode=message.PathNode()
  200. protoNode.pose.x=node.x_
  201. protoNode.pose.y=node.y_
  202. protoNode.accuracy.x=dx
  203. protoNode.accuracy.y=dx
  204. protoNode.accuracy.theta=dyaw
  205. return protoNode
  206. def ExecNavtask(self,autoDirect):
  207. cmd=message.NavCmd()
  208. key = str(uuid.uuid4())
  209. cmd.key = key
  210. cmd.action=0
  211. limitMPC_v=message.SpeedLimit()
  212. limitRotate=message.SpeedLimit()
  213. limAdjustV=message.SpeedLimit()
  214. limitAdjustH=message.SpeedLimit()
  215. limitInOutV=message.SpeedLimit()
  216. limitMPC_v.min=0.03
  217. limitMPC_v.max=1.2
  218. limitRotate.min=3*math.pi/180.0
  219. limitRotate.max=25*math.pi/180.0
  220. limAdjustV.min=0.03
  221. limAdjustV.max=0.5
  222. limitAdjustH.min=0.03
  223. limitAdjustH.max=0.3
  224. limitInOutV.min=0.03
  225. limitInOutV.max=0.5
  226. actions=self.SplitPath(self.currentNavPathNodes_)
  227. for action in actions:
  228. [type,nodes]=action
  229. if type=="input":
  230. [street,space]=nodes
  231. protoSpace=self.generateProtoNode(space,[0.01,0.01,0.3*math.pi/180.0])
  232. protoStreet=self.generateProtoNode(street,[0.02,0.02,1*math.pi/180.0])
  233. act=message.NewAction()
  234. act.type=1
  235. act.spaceNode.CopyFrom(protoSpace)
  236. act.streetNode.CopyFrom(protoStreet)
  237. act.NodeAngularLimit.CopyFrom(limitRotate)
  238. act.InOutVLimit.CopyFrom(limitInOutV)
  239. act.adjustVelocitylimit.CopyFrom(limAdjustV)
  240. act.adjustHorizonLimit.CopyFrom(limitAdjustH)
  241. cmd.newActions.add().CopyFrom(act)
  242. if type=="output":
  243. [space,street]=nodes
  244. protoSpace=self.generateProtoNode(space,[0.02,0.2,0.5*math.pi/180.0])
  245. protoStreet=self.generateProtoNode(street,[0.05,0.05,1*math.pi/180.0])
  246. act=message.NewAction()
  247. act.type=2
  248. act.spaceNode.CopyFrom(protoSpace)
  249. act.streetNode.CopyFrom(protoStreet)
  250. act.InOutVLimit.CopyFrom(limitInOutV)
  251. act.NodeAngularLimit.CopyFrom(limitRotate)
  252. act.adjustVelocitylimit.CopyFrom(limAdjustV)
  253. act.adjustHorizonLimit.CopyFrom(limitAdjustH)
  254. cmd.newActions.add().CopyFrom(act)
  255. if type=="nav":
  256. action =self.CreateNavPathNodesAction(nodes,autoDirect)
  257. if action is None:
  258. print("Nav cmd is None")
  259. return False
  260. else:
  261. cmd.newActions.add().CopyFrom(action)
  262. if cmd is None:
  263. print("Nav cmd is None")
  264. return False
  265. count=3
  266. while self.ActionType() == ActType.eReady and count>0:
  267. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  268. count-=1
  269. time.sleep(1)
  270. print("send nav cmd completed!!!")
  271. return True
  272. def CreateNavPathNodesAction(self,path,autoDirect):
  273. if path is not None and self.timedRobotStatu_.timeout() == False:
  274. for node in path:
  275. if isinstance(node,(SpaceNode)):
  276. print(" nodes must all street node")
  277. return None
  278. statu = self.timedRobotStatu_.statu
  279. if statu is not None:
  280. newAction=message.NewAction()
  281. limitMPC_v=message.SpeedLimit()
  282. limitRotate=message.SpeedLimit()
  283. limAdjustV=message.SpeedLimit()
  284. limitAdjustH=message.SpeedLimit()
  285. limitMPC_v.min=0.03
  286. limitMPC_v.max=1.5
  287. limitRotate.min=3*math.pi/180.0
  288. limitRotate.max=25*math.pi/180.0
  289. limAdjustV.min=0.03
  290. limAdjustV.max=0.3
  291. limitAdjustH.min=0.03
  292. limitAdjustH.max=0.3
  293. count=0
  294. size=len(path)
  295. for i in range(size):
  296. node=path[i]
  297. pose=message.Pose2d()
  298. accuracy=message.Pose2d()
  299. pose.x=node.x_
  300. pose.y=node.y_
  301. if i+1<size:
  302. next=path[i+1]
  303. vector = [next.x_ - node.x_, next.y_ - node.y_]
  304. dx = vector[0]
  305. dy = vector[1]
  306. yaw = math.asin(dy / math.sqrt(dx * dx + dy * dy))
  307. if yaw >= 0:
  308. if dx < 0:
  309. yaw = math.pi - yaw
  310. if yaw < 0:
  311. if dx < 0:
  312. yaw = -math.pi - yaw
  313. pose.theta=yaw
  314. else:
  315. pose.theta=0
  316. if self.IsMainModeStatu() and count==0:
  317. accuracy.x=0.05
  318. accuracy.y=0.1
  319. accuracy.theta=0.5*math.pi/(180.0)
  320. if count==size-1:
  321. accuracy.x=0.02
  322. accuracy.y=0.02
  323. accuracy.theta=3*math.pi/(180.0)
  324. else:
  325. accuracy.x=0.05
  326. accuracy.y=0.1
  327. accuracy.theta=5*math.pi/180.0
  328. count+=1
  329. pathNode=message.PathNode()
  330. pathNode.pose.CopyFrom(pose)
  331. pathNode.accuracy.CopyFrom(accuracy)
  332. newAction.type=4
  333. if autoDirect:
  334. newAction.type=3
  335. newAction.pathNodes.add().CopyFrom(pathNode)
  336. newAction.NodeVelocityLimit.CopyFrom(limitMPC_v)
  337. newAction.NodeAngularLimit.CopyFrom(limitRotate)
  338. newAction.adjustVelocitylimit.CopyFrom(limAdjustV)
  339. newAction.adjustHorizonLimit.CopyFrom(limitAdjustH)
  340. return newAction
  341. else:
  342. print("statu is None")
  343. else:
  344. print("current path is none")
  345. return None
  346. def ExecPathNodes(self,autoDirect):
  347. if self.navCmdTopic_ == None:
  348. print("Robot has not set nav cmd topic")
  349. return False
  350. if not self.ActionType()==ActType.eReady:
  351. print(" robot is navigating ,key:%s" % (self.timedNavStatu_.statu.key))
  352. return False
  353. cmd = message.NavCmd()
  354. key = str(uuid.uuid4())
  355. cmd.key = key
  356. cmd.action=0
  357. action =self.CreateNavPathNodesAction(self.currentNavPathNodes_,autoDirect)
  358. if action is None:
  359. print("Nav cmd is None")
  360. return False
  361. cmd.newActions.add().CopyFrom(action)
  362. print(cmd)
  363. published=False
  364. while self.IsNavigating() == False:
  365. if not self.ActionType() == ActType.eReady:
  366. published=True
  367. if published==False:
  368. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  369. time.sleep(1)
  370. print("send nav cmd completed!!!")
  371. return True
  372. '''
  373. 拆分路径,生成动作集,input:入库动作,nav:导航动作,output:出库动作
  374. '''
  375. def SplitPath(self,path):
  376. space_count=0
  377. for i in range(len(path)):
  378. if isinstance(path[i],(SpaceNode)):
  379. space_count+=1
  380. if i==0 or i==len(path)-1:
  381. pass
  382. else:
  383. print("Error: space node is not first/last of path")
  384. return None
  385. actions=[]
  386. lastNode=None
  387. navIndex=-1
  388. for node in path:
  389. if lastNode==None:
  390. lastNode=node
  391. continue
  392. if isinstance(node,(SpaceNode)): #当前点是车位点
  393. if isinstance(lastNode,(SpaceNode)): #上一点为车位点
  394. print("Error: last node and current node is space node ")
  395. lastNode=None
  396. continue
  397. elif isinstance(lastNode,(StreetNode)): #上一点为马路点,则动作为入库
  398. actions.append(["input",[lastNode,node]])
  399. lastNode=None
  400. continue
  401. if isinstance(node,(StreetNode)):
  402. if isinstance(lastNode,(SpaceNode)): #上一点为车位点, 出库
  403. actions.append(["output",[lastNode,node]])
  404. elif isinstance(lastNode,(StreetNode)):
  405. if navIndex<0:
  406. actions.append(["nav",[lastNode]])
  407. navIndex=len(actions)-1
  408. actions[navIndex][1].append(node)
  409. lastNode=node
  410. return actions
  411. def SwitchMoveMode(self, mode, wheelbase):
  412. if self.IsMainAgv() == False:
  413. print(" this agv is single can not switch mode")
  414. return False
  415. cmd = message.NavCmd()
  416. key = str(uuid.uuid4())
  417. cmd.key = key
  418. cmd.action=0
  419. action=message.NewAction()
  420. action.type=8
  421. action.changedMode = mode
  422. if mode == 2:
  423. action.wheelbase = wheelbase
  424. cmd.newActions.add().CopyFrom(action)
  425. loop=3
  426. while loop>0:
  427. if mode == 2:
  428. if self.IsMainModeStatu():
  429. return True
  430. if mode == 1:
  431. if self.IsMainModeStatu() == False:
  432. return True
  433. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  434. loop-=1
  435. time.sleep(0.5)
  436. return False
  437. def IsClampClosed(self):
  438. if self.timedRobotStatu_.timeout() == False:
  439. if self.IsMainModeStatu():
  440. clamp = self.timedRobotStatu_.statu.agvStatu.clamp
  441. clamp_other = self.timedRobotStatu_.statu.agvStatu.clamp_other
  442. return (clamp == 1 and clamp_other == 1)
  443. else:
  444. return self.timedRobotStatu_.statu.agvStatu.clamp == 1
  445. return False
  446. def IsClampRunning(self):
  447. if self.timedRobotStatu_.timeout() == False:
  448. if self.IsMainModeStatu():
  449. clamp = self.timedRobotStatu_.statu.agvStatu.clamp
  450. clamp_other = self.timedRobotStatu_.statu.agvStatu.clamp_other
  451. return (clamp == 0 or clamp_other == 0)
  452. else:
  453. return self.timedRobotStatu_.statu.agvStatu.clamp == 0
  454. return False
  455. def IsClampOpened(self):
  456. if self.timedRobotStatu_.timeout() == False:
  457. if self.IsMainModeStatu():
  458. clamp = self.timedRobotStatu_.statu.agvStatu.clamp
  459. clamp_other = self.timedRobotStatu_.statu.agvStatu.clamp_other
  460. return (clamp == 2 and clamp_other == 2)
  461. else:
  462. return self.timedRobotStatu_.statu.agvStatu.clamp == 2
  463. return False
  464. def ClampClose(self):
  465. if self.IsClampClosed() == True:
  466. print("clamp closed")
  467. return True
  468. cmd = message.NavCmd()
  469. cmd.action=0
  470. key = str(uuid.uuid4())
  471. cmd.key = (key)
  472. act = message.NewAction()
  473. act.type = 6
  474. cmd.newActions.add().CopyFrom(act)
  475. published=False
  476. while self.IsClampClosed()==False:
  477. if self.ActionType()==ActType.eClampClose:
  478. published=True
  479. if published==False:
  480. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  481. time.sleep(0.5)
  482. return True
  483. def ClampOpen(self):
  484. if self.IsClampOpened() == True:
  485. print("clamp opended")
  486. return True
  487. cmd = message.NavCmd()
  488. cmd.action=0
  489. key = str(uuid.uuid4())
  490. cmd.key = (key)
  491. act = message.NewAction()
  492. act.type = 7
  493. cmd.newActions.add().CopyFrom(act)
  494. published=False
  495. while self.IsClampOpened()==False:
  496. if self.ActionType()==ActType.eClampOpen:
  497. published=True
  498. if published==False:
  499. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  500. time.sleep(0.5)
  501. return True
  502. def CancelNavTask(self):
  503. cmd = message.NavCmd()
  504. cmd.action=3
  505. while self.IsNavigating() == True:
  506. self.publish(self.navCmdTopic_, jtf.MessageToJson(cmd))
  507. time.sleep(1)
  508. print(" Cancel task completed!!!")
  509. return True