use super::{
agent_core::{AgentCore, AgentCoreConfig, LaneChangeDirection},
global_route_planner::GlobalRoutePlanner,
local_planner::LocalPlannerConfig,
types::{Agent, RoadOption},
};
use crate::{
agents::tools::get_speed,
client::{ActorBase, Map, Vehicle, Waypoint},
error::{MapError, OperationError, Result},
geom::Location,
rpc::VehicleControl,
};
#[derive(Debug, Clone)]
pub struct BasicAgentConfig {
pub target_speed: f32,
pub core_config: AgentCoreConfig,
pub local_config: LocalPlannerConfig,
}
impl Default for BasicAgentConfig {
fn default() -> Self {
Self {
target_speed: 20.0,
core_config: AgentCoreConfig::default(),
local_config: LocalPlannerConfig {
target_speed: 20.0,
..Default::default()
},
}
}
}
pub struct BasicAgent {
core: AgentCore,
target_speed: f32,
}
impl BasicAgent {
pub fn new(
vehicle: Vehicle,
config: BasicAgentConfig,
map_inst: Option<Map>,
grp_inst: Option<GlobalRoutePlanner>,
) -> Result<Self> {
let target_speed = config.target_speed;
let core = AgentCore::new(
vehicle,
map_inst,
grp_inst,
config.core_config,
config.local_config,
)?;
Ok(Self { core, target_speed })
}
pub fn set_target_speed(&mut self, speed: f32) {
self.target_speed = speed;
self.core.local_planner.set_speed(speed);
}
pub fn follow_speed_limits(&mut self, value: bool) {
self.core.local_planner.follow_speed_limits(value);
}
pub fn set_destination(
&mut self,
end_location: Location,
start_location: Option<Location>,
clean_queue: bool,
) -> Result<()> {
let start_location = if let Some(loc) = start_location {
loc
} else if clean_queue {
if let Some((wp, _)) = self
.core
.local_planner
.get_incoming_waypoint_and_direction(0)
{
wp.transform().location
} else {
self.core.vehicle.transform()?.location
}
} else if let Some((wp, _)) = self.core.local_planner.get_plan().last() {
wp.transform().location
} else {
self.core.vehicle.transform()?.location
};
let route = self
.core
.global_planner
.trace_route(start_location, end_location)?;
self.core
.local_planner
.set_global_plan(route, true, clean_queue);
Ok(())
}
pub fn set_global_plan(
&mut self,
plan: Vec<(Waypoint, RoadOption)>,
stop_waypoint_creation: bool,
clean_queue: bool,
) {
self.core
.local_planner
.set_global_plan(plan, stop_waypoint_creation, clean_queue);
}
pub fn trace_route(
&self,
start_waypoint: &Waypoint,
end_waypoint: &Waypoint,
) -> Result<Vec<(Waypoint, RoadOption)>> {
let start_location = start_waypoint.transform().location;
let end_location = end_waypoint.transform().location;
self.core
.global_planner
.trace_route(start_location, end_location)
}
pub fn done(&self) -> bool {
self.core.local_planner.done()
}
pub fn run_step(&mut self) -> Result<VehicleControl> {
self.run_step_debug(false)
}
pub fn run_step_debug(&mut self, debug: bool) -> Result<VehicleControl> {
let mut hazard_detected = false;
let vehicle_speed = get_speed(&self.core.vehicle) / 3.6;
let max_vehicle_distance =
self.core.base_vehicle_threshold + self.core.speed_ratio * vehicle_speed;
let obstacle_result = self.core.vehicle_obstacle_detected(max_vehicle_distance)?;
if obstacle_result.obstacle_was_found {
hazard_detected = true;
if debug {
println!(
"BasicAgent: Vehicle obstacle detected at {:.1}m",
obstacle_result.distance
);
}
}
let max_tlight_distance =
self.core.base_tlight_threshold + self.core.speed_ratio * vehicle_speed;
let traffic_light_result = self.core.affected_by_traffic_light(max_tlight_distance)?;
if traffic_light_result.traffic_light_was_found {
hazard_detected = true;
if debug {
println!("BasicAgent: Red traffic light detected");
}
}
let mut control = self.core.local_planner.run_step(debug)?;
if hazard_detected {
control = self.add_emergency_stop(control);
if debug {
println!("BasicAgent: Emergency stop applied");
}
}
Ok(control)
}
pub fn add_emergency_stop(&self, mut control: VehicleControl) -> VehicleControl {
control.throttle = 0.0;
control.brake = self.core.max_brake;
control.hand_brake = false;
control
}
pub fn ignore_traffic_lights(&mut self, active: bool) {
self.core.ignore_traffic_lights(active);
}
pub fn ignore_stop_signs(&mut self, active: bool) {
self.core.ignore_stop_signs(active);
}
pub fn ignore_vehicles(&mut self, active: bool) {
self.core.ignore_vehicles(active);
}
pub fn local_planner(&self) -> &super::local_planner::LocalPlanner {
&self.core.local_planner
}
pub fn global_planner(&self) -> &GlobalRoutePlanner {
&self.core.global_planner
}
pub fn lane_change(
&mut self,
direction: LaneChangeDirection,
same_lane_time: f32,
other_lane_time: f32,
lane_change_time: f32,
) -> Result<()> {
let speed = get_speed(&self.core.vehicle) / 3.6; let speed = speed.max(1.0);
let distance_same_lane = speed * same_lane_time;
let distance_other_lane = speed * other_lane_time;
let lane_change_distance = speed * lane_change_time;
let current_waypoint = if let Some((wp, _)) = self
.core
.local_planner
.get_incoming_waypoint_and_direction(0)
{
wp
} else {
let vehicle_location = self.core.vehicle.transform()?.location;
self.core
.map
.waypoint_at(&vehicle_location)?
.ok_or_else(|| MapError::InvalidWaypoint {
location: format!("{:?}", vehicle_location),
reason: "Failed to get waypoint from vehicle location".to_string(),
})?
};
let step_distance = 2.0; let path = AgentCore::generate_lane_change_path(
¤t_waypoint,
direction,
distance_same_lane,
distance_other_lane,
lane_change_distance,
true, step_distance,
);
if path.is_empty() {
return Err(OperationError::InvalidTransform {
transform: format!("{:?}", current_waypoint.transform()),
reason: "Lane change not possible".to_string(),
}
.into());
}
self.set_global_plan(path, true, true);
Ok(())
}
}
impl Agent for BasicAgent {
fn run_step(&mut self) -> Result<VehicleControl> {
self.run_step()
}
fn done(&self) -> bool {
self.done()
}
fn set_destination(
&mut self,
end_location: Location,
start_location: Option<Location>,
clean_queue: bool,
) -> Result<()> {
self.set_destination(end_location, start_location, clean_queue)
}
fn set_target_speed(&mut self, speed: f32) {
self.set_target_speed(speed)
}
fn set_global_plan(
&mut self,
plan: Vec<(Waypoint, RoadOption)>,
stop_waypoint_creation: bool,
clean_queue: bool,
) {
self.set_global_plan(plan, stop_waypoint_creation, clean_queue)
}
fn trace_route(
&self,
start_waypoint: &Waypoint,
end_waypoint: &Waypoint,
) -> Result<Vec<(Waypoint, RoadOption)>> {
self.trace_route(start_waypoint, end_waypoint)
}
fn ignore_traffic_lights(&mut self, active: bool) {
self.ignore_traffic_lights(active)
}
fn ignore_stop_signs(&mut self, active: bool) {
self.ignore_stop_signs(active)
}
fn ignore_vehicles(&mut self, active: bool) {
self.ignore_vehicles(active)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_agent_config_default() {
let config = BasicAgentConfig::default();
assert_eq!(config.target_speed, 20.0);
}
#[test]
fn test_emergency_stop() {
let control = VehicleControl {
throttle: 0.5,
steer: 0.1,
brake: 0.0,
hand_brake: false,
reverse: false,
manual_gear_shift: false,
gear: 0,
};
let max_brake = 0.5;
let mut stopped_control = control;
stopped_control.throttle = 0.0;
stopped_control.brake = max_brake;
assert_eq!(stopped_control.throttle, 0.0);
assert_eq!(stopped_control.brake, 0.5);
}
}