class LateralPlanner(): def __init__(self, CP, use_lanelines=True, wide_camera=False): self.use_lanelines = use_lanelines self.LP = LanePlanner(wide_camera) self.last_cloudlog_t = 0 self.steer_rate_cost = CP.steerRateCost self.setup_mpc() self.solution_invalid_cnt = 0 self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none self.lane_change_timer = 0.0 self.lane_change_ll_prob = 1.0 self.prev_one_blinker = False self.desire = log.LateralPlan.Desire.none self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE, )) self.t_idxs = np.arange(TRAJECTORY_SIZE) self.y_pts = np.zeros(TRAJECTORY_SIZE) # dp self.dp_lc_auto_allowed = False self.dp_lc_auto_timer = None self.dp_lc_auto_delay = 2. self.dp_lc_auto_cont = False self.dp_lc_auto_completed = False def setup_mpc(self): self.libmpc = libmpc_py.libmpc self.libmpc.init() self.mpc_solution = libmpc_py.ffi.new("log_t *") self.cur_state = libmpc_py.ffi.new("state_t *") self.cur_state[0].x = 0.0 self.cur_state[0].y = 0.0 self.cur_state[0].psi = 0.0 self.cur_state[0].curvature = 0.0 self.desired_curvature = 0.0 self.safe_desired_curvature = 0.0 self.desired_curvature_rate = 0.0 self.safe_desired_curvature_rate = 0.0 def update(self, sm, CP): v_ego = sm['carState'].vEgo active = sm['controlsState'].active measured_curvature = sm['controlsState'].curvature self.LP.update_dp_set_offsets(sm['dragonConf'].dpCameraOffset, sm['dragonConf'].dpPathOffset) md = sm['modelV2'] self.LP.parse_model(sm['modelV2']) if len(md.position.x) == TRAJECTORY_SIZE and len( md.orientation.x) == TRAJECTORY_SIZE: self.path_xyz = np.column_stack( [md.position.x, md.position.y, md.position.z]) self.t_idxs = np.array(md.position.t) self.plan_yaw = list(md.orientation.z) if len(md.orientation.xStd) == TRAJECTORY_SIZE: self.path_xyz_stds = np.column_stack( [md.position.xStd, md.position.yStd, md.position.zStd]) # Lane change logic one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker below_lane_change_speed = v_ego < (sm['dragonConf'].dpLcMinMph * CV.MPH_TO_MS) if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX): self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none else: # LaneChangeState.off if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed: self.lane_change_state = LaneChangeState.preLaneChange self.lane_change_ll_prob = 1.0 # dp alc cur_time = sec_since_boot() if not below_lane_change_speed and sm[ 'dragonConf'].dpLateralMode == 2 and v_ego >= ( sm['dragonConf'].dpLcAutoMinMph * CV.MPH_TO_MS): # we allow auto lc when speed reached dragon_auto_lc_min_mph self.dp_lc_auto_allowed = True else: # if too slow, we reset all the variables self.dp_lc_auto_allowed = False self.dp_lc_auto_timer = None # disable auto lc when continuous is off and already did auto lc once if self.dp_lc_auto_allowed and not sm[ 'dragonConf'].dpLcAutoCont and self.dp_lc_auto_completed: self.dp_lc_auto_allowed = False # LaneChangeState.preLaneChange elif self.lane_change_state == LaneChangeState.preLaneChange: # Set lane change direction if sm['carState'].leftBlinker: self.lane_change_direction = LaneChangeDirection.left elif sm['carState'].rightBlinker: self.lane_change_direction = LaneChangeDirection.right else: # If there are no blinkers we will go back to LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none torque_applied = sm['carState'].steeringPressed and \ ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right)) blindspot_detected = ( (sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or (sm['carState'].rightBlindspot and self.lane_change_direction == LaneChangeDirection.right)) # dp alc if self.dp_lc_auto_allowed: if self.dp_lc_auto_timer is None: self.dp_lc_auto_timer = cur_time + sm[ 'dragonConf'].dpLcAutoDelay elif cur_time >= self.dp_lc_auto_timer: # if timer is up, we set torque_applied to True to fake user input torque_applied = True self.dp_lc_auto_completed = True # we reset the timers when torque is applied regardless if torque_applied and not blindspot_detected: self.dp_lc_auto_timer = None if not one_blinker or below_lane_change_speed: self.lane_change_state = LaneChangeState.off elif torque_applied and not blindspot_detected: self.lane_change_state = LaneChangeState.laneChangeStarting # LaneChangeState.laneChangeStarting elif self.lane_change_state == LaneChangeState.laneChangeStarting: # fade out over .5s self.lane_change_ll_prob = max( self.lane_change_ll_prob - 2 * DT_MDL, 0.0) # 98% certainty lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01: self.lane_change_state = LaneChangeState.laneChangeFinishing # LaneChangeState.laneChangeFinishing elif self.lane_change_state == LaneChangeState.laneChangeFinishing: # fade in laneline over 1s self.lane_change_ll_prob = min( self.lane_change_ll_prob + DT_MDL, 1.0) if one_blinker and self.lane_change_ll_prob > 0.99: self.lane_change_state = LaneChangeState.preLaneChange elif self.lane_change_ll_prob > 0.99: self.lane_change_state = LaneChangeState.off # dp when finishing, we reset timer to none. self.dp_lc_auto_timer = None if self.lane_change_state in [ LaneChangeState.off, LaneChangeState.preLaneChange ]: self.lane_change_timer = 0.0 else: self.lane_change_timer += DT_MDL if self.prev_one_blinker and not one_blinker: self.dp_lc_auto_completed = False self.prev_one_blinker = one_blinker self.desire = DESIRES[self.lane_change_direction][ self.lane_change_state] # Turn off lanes during lane change if self.desire == log.LateralPlan.Desire.laneChangeRight or self.desire == log.LateralPlan.Desire.laneChangeLeft: self.LP.lll_prob *= self.lane_change_ll_prob self.LP.rll_prob *= self.lane_change_ll_prob if self.use_lanelines: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) self.libmpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) else: d_path_xyz = self.path_xyz path_cost = np.clip( abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 5.0) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) self.libmpc.set_weights(path_cost, heading_cost, CP.steerRateCost) y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) self.y_pts = y_pts assert len(y_pts) == LAT_MPC_N + 1 assert len(heading_pts) == LAT_MPC_N + 1 # for now CAR_ROTATION_RADIUS is disabled # to use it, enable it in the MPC assert abs(CAR_ROTATION_RADIUS) < 1e-3 self.libmpc.run_mpc(self.cur_state, self.mpc_solution, float(v_ego), CAR_ROTATION_RADIUS, list(y_pts), list(heading_pts)) # init state for next self.cur_state.x = 0.0 self.cur_state.y = 0.0 self.cur_state.psi = 0.0 self.cur_state.curvature = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.mpc_solution.curvature) # Check for infeasable MPC solution mpc_nans = any(math.isnan(x) for x in self.mpc_solution.curvature) t = sec_since_boot() if mpc_nans: self.libmpc.init() self.cur_state.curvature = measured_curvature if t > self.last_cloudlog_t + 5.0: self.last_cloudlog_t = t cloudlog.warning("Lateral mpc - nan: True") if self.mpc_solution[ 0].cost > 20000. or mpc_nans: # TODO: find a better way to detect when MPC did not converge self.solution_invalid_cnt += 1 else: self.solution_invalid_cnt = 0 def publish(self, sm, pm): plan_solution_valid = self.solution_invalid_cnt < 2 plan_send = messaging.new_message('lateralPlan') plan_send.valid = sm.all_alive_and_valid(service_list=[ 'carState', 'controlsState', 'modelV2', 'dragonConf' ]) plan_send.lateralPlan.laneWidth = float(self.LP.lane_width) plan_send.lateralPlan.dPathPoints = [float(x) for x in self.y_pts] plan_send.lateralPlan.psis = [ float(x) for x in self.mpc_solution.psi[0:CONTROL_N] ] plan_send.lateralPlan.curvatures = [ float(x) for x in self.mpc_solution.curvature[0:CONTROL_N] ] plan_send.lateralPlan.curvatureRates = [ float(x) for x in self.mpc_solution.curvature_rate[0:CONTROL_N - 1] ] + [0.0] plan_send.lateralPlan.lProb = float(self.LP.lll_prob) plan_send.lateralPlan.rProb = float(self.LP.rll_prob) plan_send.lateralPlan.dProb = float(self.LP.d_prob) plan_send.lateralPlan.mpcSolutionValid = bool(plan_solution_valid) plan_send.lateralPlan.desire = self.desire plan_send.lateralPlan.laneChangeState = self.lane_change_state plan_send.lateralPlan.laneChangeDirection = self.lane_change_direction plan_send.lateralPlan.dpALCAllowed = self.dp_lc_auto_allowed pm.send('lateralPlan', plan_send) if LOG_MPC: dat = messaging.new_message('liveMpc') dat.liveMpc.x = list(self.mpc_solution.x) dat.liveMpc.y = list(self.mpc_solution.y) dat.liveMpc.psi = list(self.mpc_solution.psi) dat.liveMpc.curvature = list(self.mpc_solution.curvature) dat.liveMpc.cost = self.mpc_solution.cost pm.send('liveMpc', dat)
class LateralPlanner(): def __init__(self, CP, use_lanelines=True, wide_camera=False): self.use_lanelines = use_lanelines self.LP = LanePlanner(wide_camera) self.last_cloudlog_t = 0 self.steer_rate_cost = CP.steerRateCost self.solution_invalid_cnt = 0 self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none self.lane_change_timer = 0.0 self.lane_change_ll_prob = 1.0 self.keep_pulse_timer = 0.0 self.prev_one_blinker = False self.desire = log.LateralPlan.Desire.none self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE, )) self.t_idxs = np.arange(TRAJECTORY_SIZE) self.y_pts = np.zeros(TRAJECTORY_SIZE) self.d_path_w_lines_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.lat_mpc = LateralMpc() self.reset_mpc(np.zeros(6)) self.d_path_w_lines_xyz = np.zeros((TRAJECTORY_SIZE, 3)) # dp self.dp_torque_apply_length = 1.5 # secs of torque we apply for self.dp_lc_auto_start = 0. # time to start alc self.dp_lc_auto_start_in = 0. # remaining time to start alc self.dp_lc_auto_torque_end = 0. # time to end applying torque self.dp_torque_apply = False # should we apply torque? self.laneless_mode = 2 # AUTO self.laneless_mode_status = False self.laneless_mode_status_buffer = False def reset_mpc(self, x0=np.zeros(6)): self.x0 = x0 self.lat_mpc.reset(x0=self.x0) self.desired_curvature = 0.0 self.safe_desired_curvature = 0.0 self.desired_curvature_rate = 0.0 self.safe_desired_curvature_rate = 0.0 def update(self, sm, CP): self.use_lanelines = not sm['dragonConf'].dpLaneLessModeCtrl self.laneless_mode = sm['dragonConf'].dpLaneLessMode v_ego = sm['carState'].vEgo active = sm['controlsState'].active measured_curvature = sm['controlsState'].curvature self.LP.update_dp_set_offsets(sm['dragonConf'].dpCameraOffset, sm['dragonConf'].dpPathOffset) md = sm['modelV2'] self.LP.parse_model(sm['modelV2']) if len(md.position.x) == TRAJECTORY_SIZE and len( md.orientation.x) == TRAJECTORY_SIZE: self.path_xyz = np.column_stack( [md.position.x, md.position.y, md.position.z]) self.t_idxs = np.array(md.position.t) self.plan_yaw = list(md.orientation.z) if len(md.position.xStd) == TRAJECTORY_SIZE: self.path_xyz_stds = np.column_stack( [md.position.xStd, md.position.yStd, md.position.zStd]) # Lane change logic one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker below_lane_change_speed = v_ego < (sm['dragonConf'].dpLcMinMph * CV.MPH_TO_MS) if (not active) or (self.lane_change_timer > LANE_CHANGE_TIME_MAX): self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none else: reset = False if one_blinker: cur_time = sec_since_boot() # reach auto lc condition if not below_lane_change_speed and sm[ 'dragonConf'].dpLateralMode == 2 and v_ego >= ( sm['dragonConf'].dpLcAutoMinMph * CV.MPH_TO_MS): # work out alc start time and torque apply end time if self.dp_lc_auto_start == 0.: self.dp_lc_auto_start = cur_time + sm[ 'dragonConf'].dpLcAutoDelay self.dp_lc_auto_torque_end = self.dp_lc_auto_start + self.dp_torque_apply_length else: # work out how long til alc start # for display only self.dp_lc_auto_start_in = self.dp_lc_auto_start - cur_time self.dp_torque_apply = True if self.dp_lc_auto_start < cur_time <= self.dp_lc_auto_torque_end else False else: reset = True # reset all vals if not active or reset: self.dp_lc_auto_start = 0. self.dp_lc_auto_start_in = 0. self.dp_lc_auto_torque_end = 0. self.dp_torque_apply = False # LaneChangeState.off if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed: self.lane_change_state = LaneChangeState.preLaneChange self.lane_change_ll_prob = 1.0 # LaneChangeState.preLaneChange elif self.lane_change_state == LaneChangeState.preLaneChange: # Set lane change direction if sm['carState'].leftBlinker: self.lane_change_direction = LaneChangeDirection.left elif sm['carState'].rightBlinker: self.lane_change_direction = LaneChangeDirection.right else: # If there are no blinkers we will go back to LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none torque_applied = sm['carState'].steeringPressed and \ ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right)) blindspot_detected = ( (sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or (sm['carState'].rightBlindspot and self.lane_change_direction == LaneChangeDirection.right)) # if human made lane change prior alca, we should stop alca until new blinker (off -> on) self.dp_lc_auto_start = self.dp_lc_auto_torque_end if torque_applied else self.dp_lc_auto_start torque_applied = self.dp_torque_apply if self.dp_torque_apply else torque_applied if not one_blinker or below_lane_change_speed: self.lane_change_state = LaneChangeState.off elif torque_applied and not blindspot_detected: self.lane_change_state = LaneChangeState.laneChangeStarting # LaneChangeState.laneChangeStarting elif self.lane_change_state == LaneChangeState.laneChangeStarting: # fade out over .5s self.lane_change_ll_prob = max( self.lane_change_ll_prob - 2 * DT_MDL, 0.0) # 98% certainty lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01: self.lane_change_state = LaneChangeState.laneChangeFinishing # LaneChangeState.laneChangeFinishing elif self.lane_change_state == LaneChangeState.laneChangeFinishing: # fade in laneline over 1s self.lane_change_ll_prob = min( self.lane_change_ll_prob + DT_MDL, 1.0) if one_blinker and self.lane_change_ll_prob > 0.99: self.lane_change_state = LaneChangeState.preLaneChange elif self.lane_change_ll_prob > 0.99: self.lane_change_state = LaneChangeState.off if self.lane_change_state in [ LaneChangeState.off, LaneChangeState.preLaneChange ]: self.lane_change_timer = 0.0 else: self.lane_change_timer += DT_MDL self.prev_one_blinker = one_blinker self.desire = DESIRES[self.lane_change_direction][ self.lane_change_state] # Send keep pulse once per second during LaneChangeStart.preLaneChange if self.lane_change_state in [ LaneChangeState.off, LaneChangeState.laneChangeStarting ]: self.keep_pulse_timer = 0.0 elif self.lane_change_state == LaneChangeState.preLaneChange: self.keep_pulse_timer += DT_MDL if self.keep_pulse_timer > 1.0: self.keep_pulse_timer = 0.0 elif self.desire in [ log.LateralPlan.Desire.keepLeft, log.LateralPlan.Desire.keepRight ]: self.desire = log.LateralPlan.Desire.none # Turn off lanes during lane change if self.desire == log.LateralPlan.Desire.laneChangeRight or self.desire == log.LateralPlan.Desire.laneChangeLeft: self.LP.lll_prob *= self.lane_change_ll_prob self.LP.rll_prob *= self.lane_change_ll_prob self.d_path_w_lines_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) if self.use_lanelines: d_path_xyz = self.d_path_w_lines_xyz self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) self.laneless_mode_status = False elif self.laneless_mode == 0: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) self.laneless_mode_status = False elif self.laneless_mode == 1: d_path_xyz = self.path_xyz path_cost = np.clip( abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 5.0) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) self.lat_mpc.set_weights(path_cost, heading_cost, CP.steerRateCost) self.laneless_mode_status = True elif self.laneless_mode == 2 and ( (self.LP.lll_prob + self.LP.rll_prob) / 2 < 0.3) and self.lane_change_state == LaneChangeState.off: d_path_xyz = self.path_xyz path_cost = np.clip( abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 5.0) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) self.lat_mpc.set_weights(path_cost, heading_cost, CP.steerRateCost) self.laneless_mode_status = True self.laneless_mode_status_buffer = True elif self.laneless_mode == 2 and ((self.LP.lll_prob + self.LP.rll_prob)/2 > 0.5) and \ self.laneless_mode_status_buffer and self.lane_change_state == LaneChangeState.off: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) self.laneless_mode_status = False self.laneless_mode_status_buffer = False elif self.laneless_mode == 2 and self.laneless_mode_status_buffer == True and self.lane_change_state == LaneChangeState.off: d_path_xyz = self.path_xyz path_cost = np.clip( abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 5.0) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) self.lat_mpc.set_weights(path_cost, heading_cost, CP.steerRateCost) self.laneless_mode_status = True else: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) self.laneless_mode_status = False self.laneless_mode_status_buffer = False y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) self.y_pts = y_pts assert len(y_pts) == LAT_MPC_N + 1 assert len(heading_pts) == LAT_MPC_N + 1 self.x0[4] = v_ego self.lat_mpc.run(self.x0, v_ego, CAR_ROTATION_RADIUS, y_pts, heading_pts) # init state for next self.x0[3] = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.lat_mpc.x_sol[:, 3]) # Check for infeasable MPC solution mpc_nans = any(math.isnan(x) for x in self.lat_mpc.x_sol[:, 3]) t = sec_since_boot() if mpc_nans or self.lat_mpc.solution_status != 0: self.reset_mpc() self.x0[3] = measured_curvature if t > self.last_cloudlog_t + 5.0: self.last_cloudlog_t = t cloudlog.warning("Lateral mpc - nan: True") if self.lat_mpc.cost > 20000. or mpc_nans: self.solution_invalid_cnt += 1 else: self.solution_invalid_cnt = 0 def publish(self, sm, pm): plan_solution_valid = self.solution_invalid_cnt < 2 plan_send = messaging.new_message('lateralPlan') plan_send.valid = sm.all_alive_and_valid(service_list=[ 'carState', 'controlsState', 'modelV2', 'dragonConf' ]) plan_send.lateralPlan.laneWidth = float(self.LP.lane_width) plan_send.lateralPlan.dPathPoints = [float(x) for x in self.y_pts] plan_send.lateralPlan.psis = [ float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 2] ] plan_send.lateralPlan.curvatures = [ float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 3] ] plan_send.lateralPlan.curvatureRates = [ float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N - 1] ] + [0.0] plan_send.lateralPlan.lProb = float(self.LP.lll_prob) plan_send.lateralPlan.rProb = float(self.LP.rll_prob) plan_send.lateralPlan.dProb = float(self.LP.d_prob) plan_send.lateralPlan.mpcSolutionValid = bool(plan_solution_valid) plan_send.lateralPlan.desire = self.desire plan_send.lateralPlan.useLaneLines = self.use_lanelines plan_send.lateralPlan.laneChangeState = self.lane_change_state plan_send.lateralPlan.laneChangeDirection = self.lane_change_direction # dp plan_send.lateralPlan.dpLaneLessModeStatus = bool( self.laneless_mode_status) plan_send.lateralPlan.dpALCAStartIn = self.dp_lc_auto_start_in plan_send.lateralPlan.dPathWLinesX = [ float(x) for x in self.d_path_w_lines_xyz[:, 0] ] plan_send.lateralPlan.dPathWLinesY = [ float(y) for y in self.d_path_w_lines_xyz[:, 1] ] pm.send('lateralPlan', plan_send)