use super::{
agent_core::{AgentCore, AgentCoreConfig},
global_route_planner::GlobalRoutePlanner,
local_planner::LocalPlannerConfig,
types::{Agent, RoadOption},
};
use crate::{
agents::tools::get_speed,
client::{Actor, ActorBase, Map, Vehicle, Waypoint},
error::Result,
geom::Location,
rpc::VehicleControl,
};
#[derive(Debug, Clone)]
pub struct BehaviorParams {
pub max_speed: f32,
pub speed_lim_dist: f32,
pub speed_decrease: f32,
pub safety_time: f32,
pub min_proximity_threshold: f32,
pub braking_distance: f32,
pub tailgate_counter: i32,
}
#[derive(Debug, Clone)]
pub enum BehaviorType {
Cautious(BehaviorParams),
Normal(BehaviorParams),
Aggressive(BehaviorParams),
Custom(BehaviorParams),
}
impl BehaviorType {
pub fn cautious() -> Self {
BehaviorType::Cautious(BehaviorParams {
max_speed: 40.0,
speed_lim_dist: 6.0,
speed_decrease: 12.0,
safety_time: 3.0,
min_proximity_threshold: 12.0,
braking_distance: 6.0,
tailgate_counter: -1, })
}
pub fn normal() -> Self {
BehaviorType::Normal(BehaviorParams {
max_speed: 50.0,
speed_lim_dist: 3.0,
speed_decrease: 10.0,
safety_time: 2.0,
min_proximity_threshold: 10.0,
braking_distance: 5.0,
tailgate_counter: 0,
})
}
pub fn aggressive() -> Self {
BehaviorType::Aggressive(BehaviorParams {
max_speed: 70.0,
speed_lim_dist: 1.0,
speed_decrease: 8.0,
safety_time: 1.0,
min_proximity_threshold: 8.0,
braking_distance: 4.0,
tailgate_counter: -1, })
}
pub fn custom(params: BehaviorParams) -> Self {
BehaviorType::Custom(params)
}
pub fn params(&self) -> &BehaviorParams {
match self {
BehaviorType::Cautious(p)
| BehaviorType::Normal(p)
| BehaviorType::Aggressive(p)
| BehaviorType::Custom(p) => p,
}
}
pub fn params_mut(&mut self) -> &mut BehaviorParams {
match self {
BehaviorType::Cautious(p)
| BehaviorType::Normal(p)
| BehaviorType::Aggressive(p)
| BehaviorType::Custom(p) => p,
}
}
}
impl Default for BehaviorType {
fn default() -> Self {
Self::normal()
}
}
#[derive(Debug, Clone)]
pub struct BehaviorAgentConfig {
pub behavior: BehaviorType,
pub core_config: AgentCoreConfig,
pub local_config: LocalPlannerConfig,
}
impl Default for BehaviorAgentConfig {
fn default() -> Self {
let behavior = BehaviorType::normal();
let target_speed = behavior.params().max_speed;
Self {
behavior,
core_config: AgentCoreConfig::default(),
local_config: LocalPlannerConfig {
target_speed,
..Default::default()
},
}
}
}
pub struct BehaviorAgent {
core: AgentCore,
behavior: BehaviorType,
look_ahead_steps: usize,
speed: f32,
speed_limit: f32,
direction: Option<RoadOption>,
incoming_direction: Option<RoadOption>,
incoming_waypoint: Option<Waypoint>,
min_speed: f32,
#[allow(dead_code)]
sampling_resolution: f32,
}
impl BehaviorAgent {
pub fn new(
vehicle: Vehicle,
config: BehaviorAgentConfig,
map_inst: Option<Map>,
grp_inst: Option<GlobalRoutePlanner>,
) -> Result<Self> {
let behavior = config.behavior;
let sampling_resolution = config.core_config.sampling_resolution;
let core = AgentCore::new(
vehicle,
map_inst,
grp_inst,
config.core_config,
config.local_config,
)?;
Ok(Self {
core,
behavior,
look_ahead_steps: 5,
speed: 0.0,
speed_limit: 50.0,
direction: None,
incoming_direction: None,
incoming_waypoint: None,
min_speed: 5.0,
sampling_resolution,
})
}
fn update_information(&mut self) {
self.speed = get_speed(&self.core.vehicle);
if let Some((wp, dir)) = self
.core
.local_planner
.get_incoming_waypoint_and_direction(self.look_ahead_steps)
{
self.incoming_waypoint = Some(wp);
self.incoming_direction = Some(dir);
}
if let Some((_, dir)) = self
.core
.local_planner
.get_incoming_waypoint_and_direction(0)
{
self.direction = Some(dir);
}
self.speed_limit = self.behavior.params().max_speed;
}
fn traffic_light_manager(&mut self) -> bool {
let max_tlight_distance =
self.core.base_tlight_threshold + self.core.speed_ratio * (self.speed / 3.6);
match self.core.affected_by_traffic_light(max_tlight_distance) {
Ok(result) => result.traffic_light_was_found,
Err(_) => false,
}
}
fn collision_and_car_avoid_manager(&self) -> Result<(bool, Option<u32>, f32)> {
let params = self.behavior.params();
let max_distance = params.min_proximity_threshold + params.safety_time * (self.speed / 3.6);
let result = self.core.vehicle_obstacle_detected(max_distance)?;
Ok((
result.obstacle_was_found,
result.obstacle_id,
result.distance,
))
}
fn pedestrian_avoid_manager(&self) -> Result<(bool, Option<Actor>, f32)> {
let walker_list = self.core.world.actors()?.filter("*walker.pedestrian*")?;
let vehicle_location = self.core.vehicle.transform()?.location;
let params = self.behavior.params();
let max_distance = params.min_proximity_threshold + params.safety_time * (self.speed / 3.6);
for actor in walker_list.iter() {
let walker_location = actor.transform()?.location;
let dx = walker_location.x - vehicle_location.x;
let dy = walker_location.y - vehicle_location.y;
let distance = (dx * dx + dy * dy).sqrt();
if distance < max_distance {
return Ok((true, Some(actor), distance));
}
}
Ok((false, None, -1.0))
}
fn car_following_manager(&mut self, distance: f32, debug: bool) -> Result<VehicleControl> {
let params = self.behavior.params();
let _speed_decrease = params.speed_decrease;
let safety_distance = params.min_proximity_threshold;
let target_speed = if distance < safety_distance {
self.min_speed
} else {
let speed_factor = (distance - safety_distance) / safety_distance;
let target = self.speed_limit * speed_factor.min(1.0);
target.max(self.min_speed)
};
if debug {
println!(
"BehaviorAgent: Car following - distance={:.1}m, target_speed={:.1}km/h",
distance, target_speed
);
}
self.core.local_planner.set_speed(target_speed);
self.core.local_planner.run_step(debug)
}
fn emergency_stop(&self) -> VehicleControl {
VehicleControl {
throttle: 0.0,
steer: 0.0,
brake: self.core.max_brake,
hand_brake: false,
reverse: false,
manual_gear_shift: false,
gear: 0,
}
}
pub fn run_step_with_debug(&mut self, debug: bool) -> Result<VehicleControl> {
self.update_information();
if self.traffic_light_manager() {
if debug {
println!("BehaviorAgent: Red traffic light detected - emergency stop");
}
return Ok(self.emergency_stop());
}
let (walker_found, walker_actor, walker_distance) = self.pedestrian_avoid_manager()?;
if walker_found {
let vehicle_bbox = self.core.vehicle.bounding_box()?;
let vehicle_radius = vehicle_bbox.extent.x.max(vehicle_bbox.extent.y);
let walker_radius = {
let walker = walker_actor.unwrap();
let walker_bbox = walker.bounding_box();
walker_bbox.extent.x.max(walker_bbox.extent.y)
};
let actual_distance = walker_distance - walker_radius - vehicle_radius;
let params = self.behavior.params();
if actual_distance < params.braking_distance {
if debug {
println!(
"BehaviorAgent: Pedestrian too close ({:.1}m actual, {:.1}m center-to-center) - emergency stop",
actual_distance, walker_distance
);
}
return Ok(self.emergency_stop());
}
}
let (vehicle_found, vehicle_id, vehicle_distance) =
self.collision_and_car_avoid_manager()?;
if vehicle_found {
let ego_bbox = self.core.vehicle.bounding_box()?;
let vehicle_actor = self.core.world.actor(vehicle_id.unwrap())?.unwrap();
let obstacle_bbox = if let Ok(vehicle) = Vehicle::try_from(vehicle_actor) {
vehicle.bounding_box()?
} else {
ego_bbox.clone()
};
let vehicle_radius = obstacle_bbox.extent.x.max(obstacle_bbox.extent.y);
let ego_radius = ego_bbox.extent.x.max(ego_bbox.extent.y);
let actual_distance = vehicle_distance - vehicle_radius - ego_radius;
let params = self.behavior.params();
if actual_distance < params.braking_distance {
if debug {
println!(
"BehaviorAgent: Vehicle too close ({:.1}m actual, {:.1}m center-to-center) - emergency stop",
actual_distance, vehicle_distance
);
}
return Ok(self.emergency_stop());
} else {
return self.car_following_manager(actual_distance, debug);
}
}
let params = self.behavior.params();
let target_speed = self.speed_limit.min(params.max_speed);
if debug {
println!(
"BehaviorAgent: Normal driving - target_speed={:.1}km/h",
target_speed
);
}
self.core.local_planner.set_speed(target_speed);
self.core.local_planner.run_step(debug)
}
pub fn set_target_speed(&mut self, speed: f32) {
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_with_debug(false)
}
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);
}
}
impl Agent for BehaviorAgent {
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_behavior_params() {
let cautious = BehaviorType::cautious();
assert_eq!(cautious.params().max_speed, 40.0);
let normal = BehaviorType::normal();
assert_eq!(normal.params().max_speed, 50.0);
let aggressive = BehaviorType::aggressive();
assert_eq!(aggressive.params().max_speed, 70.0);
}
#[test]
fn test_behavior_agent_config_default() {
let config = BehaviorAgentConfig::default();
assert_eq!(config.behavior.params().max_speed, 50.0);
}
#[test]
fn test_emergency_stop() {
let max_brake = 0.5;
let stopped_control = VehicleControl {
throttle: 0.0,
steer: 0.0,
brake: max_brake,
hand_brake: false,
reverse: false,
manual_gear_shift: false,
gear: 0,
};
assert_eq!(stopped_control.throttle, 0.0);
assert_eq!(stopped_control.brake, 0.5);
}
}