PageRenderTime 71ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/selfdrive/controls/controlsd.py

https://gitlab.com/jerdog/openpilot
Python | 375 lines | 221 code | 96 blank | 58 comment | 70 complexity | f7638565f240293d17ea44c84adfc887 MD5 | raw file
  1. #!/usr/bin/env python
  2. import os
  3. import zmq
  4. import numpy as np
  5. import selfdrive.messaging as messaging
  6. from cereal import car
  7. from selfdrive.swaglog import cloudlog
  8. from common.numpy_fast import clip
  9. from common.fingerprints import fingerprint
  10. from selfdrive.config import Conversions as CV
  11. from common.services import service_list
  12. from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
  13. from common.profiler import Profiler
  14. from common.params import Params
  15. from selfdrive.controls.lib.drive_helpers import learn_angle_offset
  16. from selfdrive.controls.lib.longcontrol import LongControl
  17. from selfdrive.controls.lib.latcontrol import LatControl
  18. from selfdrive.controls.lib.pathplanner import PathPlanner
  19. from selfdrive.controls.lib.adaptivecruise import AdaptiveCruise
  20. from selfdrive.controls.lib.alertmanager import AlertManager
  21. V_CRUISE_MAX = 144
  22. V_CRUISE_MIN = 8
  23. V_CRUISE_DELTA = 8
  24. V_CRUISE_ENABLE_MIN = 40
  25. def controlsd_thread(gctx, rate=100): #rate in Hz
  26. # *** log ***
  27. context = zmq.Context()
  28. live100 = messaging.pub_sock(context, service_list['live100'].port)
  29. carstate = messaging.pub_sock(context, service_list['carState'].port)
  30. carcontrol = messaging.pub_sock(context, service_list['carControl'].port)
  31. thermal = messaging.sub_sock(context, service_list['thermal'].port)
  32. live20 = messaging.sub_sock(context, service_list['live20'].port)
  33. model = messaging.sub_sock(context, service_list['model'].port)
  34. health = messaging.sub_sock(context, service_list['health'].port)
  35. logcan = messaging.sub_sock(context, service_list['can'].port)
  36. # connects to can
  37. CP = fingerprint(logcan)
  38. # import the car from the fingerprint
  39. cloudlog.info("controlsd is importing %s", CP.carName)
  40. exec('from selfdrive.car.'+CP.carName+'.interface import CarInterface')
  41. sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
  42. CI = CarInterface(CP, logcan, sendcan)
  43. # write CarParams
  44. Params().put("CarParams", CP.to_bytes())
  45. PP = PathPlanner(model)
  46. AC = AdaptiveCruise(live20)
  47. AM = AlertManager()
  48. LoC = LongControl()
  49. LaC = LatControl()
  50. # controls enabled state
  51. enabled = False
  52. last_enable_request = 0
  53. # learned angle offset
  54. angle_offset = 0
  55. # rear view camera state
  56. rear_view_toggle = False
  57. v_cruise_kph = 255
  58. # 0.0 - 1.0
  59. awareness_status = 0.0
  60. soft_disable_timer = None
  61. # Is cpu temp too high to enable?
  62. overtemp = False
  63. free_space = 1.0
  64. # start the loop
  65. set_realtime_priority(2)
  66. rk = Ratekeeper(rate, print_delay_threshold=2./1000)
  67. while 1:
  68. prof = Profiler()
  69. cur_time = sec_since_boot()
  70. # read CAN
  71. CS = CI.update()
  72. # broadcast carState
  73. cs_send = messaging.new_message()
  74. cs_send.init('carState')
  75. cs_send.carState = CS # copy?
  76. carstate.send(cs_send.to_bytes())
  77. prof.checkpoint("CarInterface")
  78. # did it request to enable?
  79. enable_request, enable_condition = False, False
  80. if enabled:
  81. # gives the user 6 minutes
  82. awareness_status -= 1.0/(100*60*6)
  83. if awareness_status <= 0.:
  84. AM.add("driverDistracted", enabled)
  85. # reset awareness status on steering
  86. if CS.steeringPressed:
  87. awareness_status = 1.0
  88. # handle button presses
  89. for b in CS.buttonEvents:
  90. print b
  91. # reset awareness on any user action
  92. awareness_status = 1.0
  93. # button presses for rear view
  94. if b.type == "leftBlinker" or b.type == "rightBlinker":
  95. if b.pressed:
  96. rear_view_toggle = True
  97. else:
  98. rear_view_toggle = False
  99. if b.type == "altButton1" and b.pressed:
  100. rear_view_toggle = not rear_view_toggle
  101. if not CP.enableCruise and enabled and not b.pressed:
  102. if b.type == "accelCruise":
  103. v_cruise_kph = v_cruise_kph - (v_cruise_kph % V_CRUISE_DELTA) + V_CRUISE_DELTA
  104. elif b.type == "decelCruise":
  105. v_cruise_kph = v_cruise_kph - (v_cruise_kph % V_CRUISE_DELTA) - V_CRUISE_DELTA
  106. v_cruise_kph = clip(v_cruise_kph, V_CRUISE_MIN, V_CRUISE_MAX)
  107. if not enabled and b.type in ["accelCruise", "decelCruise"] and not b.pressed:
  108. enable_request = True
  109. # do disable on button down
  110. if b.type == "cancel" and b.pressed:
  111. AM.add("disable", enabled)
  112. prof.checkpoint("Buttons")
  113. # *** health checking logic ***
  114. hh = messaging.recv_sock(health)
  115. if hh is not None:
  116. # if the board isn't allowing controls but somehow we are enabled!
  117. if not hh.health.controlsAllowed and enabled:
  118. AM.add("controlsMismatch", enabled)
  119. # *** thermal checking logic ***
  120. # thermal data, checked every second
  121. td = messaging.recv_sock(thermal)
  122. if td is not None:
  123. # Check temperature.
  124. overtemp = any(
  125. t > 950
  126. for t in (td.thermal.cpu0, td.thermal.cpu1, td.thermal.cpu2,
  127. td.thermal.cpu3, td.thermal.mem, td.thermal.gpu))
  128. # under 15% of space free
  129. free_space = td.thermal.freeSpace
  130. prof.checkpoint("Health")
  131. # *** getting model logic ***
  132. PP.update(cur_time, CS.vEgo)
  133. if rk.frame % 5 == 2:
  134. # *** run this at 20hz again ***
  135. angle_offset = learn_angle_offset(enabled, CS.vEgo, angle_offset, np.asarray(PP.d_poly), LaC.y_des, CS.steeringPressed)
  136. # disable if the pedals are pressed while engaged, this is a user disable
  137. if enabled:
  138. if CS.gasPressed or CS.brakePressed:
  139. AM.add("disable", enabled)
  140. if enable_request:
  141. # check for pressed pedals
  142. if CS.gasPressed or CS.brakePressed:
  143. AM.add("pedalPressed", enabled)
  144. enable_request = False
  145. else:
  146. print "enabled pressed at", cur_time
  147. last_enable_request = cur_time
  148. # don't engage with less than 15% free
  149. if free_space < 0.15:
  150. AM.add("outOfSpace", enabled)
  151. enable_request = False
  152. if CP.enableCruise:
  153. enable_condition = ((cur_time - last_enable_request) < 0.2) and CS.cruiseState.enabled
  154. else:
  155. enable_condition = enable_request
  156. if enable_request or enable_condition or enabled:
  157. # add all alerts from car
  158. for alert in CS.errors:
  159. AM.add(alert, enabled)
  160. if AC.dead:
  161. AM.add("radarCommIssue", enabled)
  162. if PP.dead:
  163. AM.add("modelCommIssue", enabled)
  164. if overtemp:
  165. AM.add("overheat", enabled)
  166. prof.checkpoint("Model")
  167. if enable_condition and not enabled and not AM.alertPresent():
  168. print "*** enabling controls"
  169. # beep for enabling
  170. AM.add("enable", enabled)
  171. # enable both lateral and longitudinal controls
  172. enabled = True
  173. # on activation, let's always set v_cruise from where we are, even if PCM ACC is active
  174. v_cruise_kph = int(round(max(CS.vEgo * CV.MS_TO_KPH * CP.uiSpeedFudge, V_CRUISE_ENABLE_MIN)))
  175. # 6 minutes driver you're on
  176. awareness_status = 1.0
  177. # reset the PID loops
  178. LaC.reset()
  179. # start long control at actual speed
  180. LoC.reset(v_pid = CS.vEgo)
  181. if CP.enableCruise and CS.cruiseState.enabled:
  182. v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
  183. # *** put the adaptive in adaptive cruise control ***
  184. AC.update(cur_time, CS.vEgo, CS.steeringAngle, LoC.v_pid, awareness_status, CP)
  185. prof.checkpoint("AdaptiveCruise")
  186. # *** gas/brake PID loop ***
  187. final_gas, final_brake = LoC.update(enabled, CS.vEgo, v_cruise_kph, AC.v_target_lead, AC.a_target, AC.jerk_factor, CP)
  188. # *** steering PID loop ***
  189. final_steer, sat_flag = LaC.update(enabled, CS.vEgo, CS.steeringAngle, CS.steeringPressed, PP.d_poly, angle_offset, CP)
  190. prof.checkpoint("PID")
  191. # ***** handle alerts ****
  192. # send a "steering required alert" if saturation count has reached the limit
  193. if sat_flag:
  194. AM.add("steerSaturated", enabled)
  195. if enabled and AM.alertShouldDisable():
  196. print "DISABLING IMMEDIATELY ON ALERT"
  197. enabled = False
  198. if enabled and AM.alertShouldSoftDisable():
  199. if soft_disable_timer is None:
  200. soft_disable_timer = 3 * rate
  201. elif soft_disable_timer == 0:
  202. print "SOFT DISABLING ON ALERT"
  203. enabled = False
  204. else:
  205. soft_disable_timer -= 1
  206. else:
  207. soft_disable_timer = None
  208. # *** push the alerts to current ***
  209. alert_text_1, alert_text_2, visual_alert, audible_alert = AM.process_alerts(cur_time)
  210. # ***** control the car *****
  211. CC = car.CarControl.new_message()
  212. CC.enabled = enabled
  213. CC.gas = float(final_gas)
  214. CC.brake = float(final_brake)
  215. CC.steeringTorque = float(final_steer)
  216. CC.cruiseControl.override = True
  217. CC.cruiseControl.cancel = bool((not CP.enableCruise) or (not enabled and CS.cruiseState.enabled)) # always cancel if we have an interceptor
  218. CC.cruiseControl.speedOverride = float((LoC.v_pid - .3) if (CP.enableCruise and final_brake == 0.) else 0.0)
  219. CC.cruiseControl.accelOverride = float(AC.a_pcm)
  220. CC.hudControl.setSpeed = float(v_cruise_kph * CV.KPH_TO_MS)
  221. CC.hudControl.speedVisible = enabled
  222. CC.hudControl.lanesVisible = enabled
  223. CC.hudControl.leadVisible = bool(AC.has_lead)
  224. CC.hudControl.visualAlert = visual_alert
  225. CC.hudControl.audibleAlert = audible_alert
  226. # this alert will apply next controls cycle
  227. if not CI.apply(CC):
  228. AM.add("controlsFailed", enabled)
  229. # broadcast carControl
  230. cc_send = messaging.new_message()
  231. cc_send.init('carControl')
  232. cc_send.carControl = CC # copy?
  233. carcontrol.send(cc_send.to_bytes())
  234. prof.checkpoint("CarControl")
  235. # ***** publish state to logger *****
  236. # publish controls state at 100Hz
  237. dat = messaging.new_message()
  238. dat.init('live100')
  239. # show rear view camera on phone if in reverse gear or when button is pressed
  240. dat.live100.rearViewCam = ('reverseGear' in CS.errors) or rear_view_toggle
  241. dat.live100.alertText1 = alert_text_1
  242. dat.live100.alertText2 = alert_text_2
  243. dat.live100.awarenessStatus = max(awareness_status, 0.0) if enabled else 0.0
  244. # what packets were used to process
  245. dat.live100.canMonoTimes = list(CS.canMonoTimes)
  246. dat.live100.mdMonoTime = PP.logMonoTime
  247. dat.live100.l20MonoTime = AC.logMonoTime
  248. # if controls is enabled
  249. dat.live100.enabled = enabled
  250. # car state
  251. dat.live100.vEgo = CS.vEgo
  252. dat.live100.angleSteers = CS.steeringAngle
  253. dat.live100.steerOverride = CS.steeringPressed
  254. # longitudinal control state
  255. dat.live100.vPid = float(LoC.v_pid)
  256. dat.live100.vCruise = float(v_cruise_kph)
  257. dat.live100.upAccelCmd = float(LoC.Up_accel_cmd)
  258. dat.live100.uiAccelCmd = float(LoC.Ui_accel_cmd)
  259. # lateral control state
  260. dat.live100.yActual = float(LaC.y_actual)
  261. dat.live100.yDes = float(LaC.y_des)
  262. dat.live100.upSteer = float(LaC.Up_steer)
  263. dat.live100.uiSteer = float(LaC.Ui_steer)
  264. # processed radar state, should add a_pcm?
  265. dat.live100.vTargetLead = float(AC.v_target_lead)
  266. dat.live100.aTargetMin = float(AC.a_target[0])
  267. dat.live100.aTargetMax = float(AC.a_target[1])
  268. dat.live100.jerkFactor = float(AC.jerk_factor)
  269. # lag
  270. dat.live100.cumLagMs = -rk.remaining*1000.
  271. live100.send(dat.to_bytes())
  272. prof.checkpoint("Live100")
  273. # *** run loop at fixed rate ***
  274. if rk.keep_time():
  275. prof.display()
  276. def main(gctx=None):
  277. controlsd_thread(gctx, 100)
  278. if __name__ == "__main__":
  279. main()