Program Listing for File agents_class.cpp

Return to documentation for file (agent_path_prediction/src/agents_class.cpp)

#include <agent_path_prediction/agents_class.h>

#include <numeric>

// Configuarable parameters
#define AGENTS_SUB_TOPIC "/tracked_agents"
#define WINDOW_MOVING_AVG 5
#define HUM_RADIUS 0.3
#define ROBOT_RADIUS 0.35
#define PLANNING_RADIUS 10.0
#define BASE_LINK_FRAME "base_link"
#define MAP_FRAME "map"
#define ODOM_FRAME "odom"

namespace agents {

Agents::Agents() : initialized_(false) {
  // Throw error on wrong initialization
  ROS_ERROR("The Agents class needs tf2_ros::Buffer* and costmap_2d::Costmap2DROS *, and are not passed!");
}

Agents::Agents(tf2_ros::Buffer *tf, costmap_2d::Costmap2DROS *costmap_ros) : initialized_(false) {
  // call the initialize class to start things
  if (!initialized_) {
    ros::NodeHandle private_nh("~");

    // Get params
    loadRosParamFromNodeHandle(private_nh);

    // Initialize the publisher
    agents_info_pub_ = private_nh.advertise<agent_path_prediction::AgentsInfo>("agentsInfo", 10);

    // Need to remap subscriber properly
    if (!ns_.empty()) {
      tracked_agents_sub_topic_ = "/" + ns_ + tracked_agents_sub_topic_;
    }

    // Subscribers
    tracked_agents_sub_ = private_nh.subscribe(tracked_agents_sub_topic_, 1, &Agents::trackedAgentsCB, this);

    // Initialize variables
    tf_ = tf;
    costmap_ros_ = costmap_ros;
    costmap_ = costmap_ros_->getCostmap();
    stuck_ = false;
    stuck_agent_id_ = -1;
    initialized_ = true;
  }
}

void Agents::trackedAgentsCB(const cohan_msgs::TrackedAgents &tracked_agents) {
  tracked_agents_ = tracked_agents;

  // Msg for publishing agents info
  agent_path_prediction::AgentsInfo agents_info;

  geometry_msgs::TransformStamped transform_stamped;
  auto base_link = base_link_frame_;
  if (!ns_.empty()) {
    base_link = ns_ + "/" + base_link_frame_;
  }

  // Get the robot pose
  transform_stamped = tf_->lookupTransform(map_frame_, base_link, ros::Time(0), ros::Duration(0.5));
  auto xpos = transform_stamped.transform.translation.x;
  auto ypos = transform_stamped.transform.translation.y;
  auto ryaw = tf2::getYaw(transform_stamped.transform.rotation);
  Eigen::Vector2d robot_vec(std::cos(ryaw), std::sin(ryaw));
  geometry_msgs::Pose2D robot_pose;
  robot_pose.x = xpos;
  robot_pose.y = ypos;
  robot_pose.theta = ryaw;

  // Add robot pose to the info message
  agents_info.robot_pose = robot_pose;

  agent_vels_.clear();
  visible_agent_ids_.clear();
  agent_still_.clear();

  std::map<double, int> agent_dist_id_map;
  std::map<int, double> agents_radii;
  std::map<int, agent_path_prediction::HumanInfo> humans_info;

  for (auto &agent : tracked_agents_.agents) {
    agent_path_prediction::HumanInfo human_info;
    auto h_id = agent.track_id;
    human_info.id = h_id;
    human_info.name = agent.name;
    if (agent.type == 1) {
      agents_radii[h_id] = human_radius_;
    } else {
      agents_radii[h_id] = robot_radius_;
    }

    if (agents_states_.size() < tracked_agents_.agents.size()) {
      // Add the agent id and state
      human_info.state = agents::AgentState::NO_STATE;

      // Update the class variable
      if (agents_states_.find(h_id) == agents_states_.end()) {
        agents_states_[h_id] = agents::AgentState::NO_STATE;
      }

      std::vector<double> h_vels;
      agent_vels_[h_id] = h_vels;
      agent_nominal_vels_[h_id] = 0.0;
      geometry_msgs::Pose h_pose;
      agents_[h_id] = h_pose;
    }
    // double h_xpos, h_ypos;
    for (auto &segment : agent.segments) {
      if (segment.type == DEFAULT_AGENT_SEGMENT) {
        agents_[h_id] = segment.pose.pose;
        double h_xpos = segment.pose.pose.position.x;
        double h_ypos = segment.pose.pose.position.y;

        Eigen::Vector2d rh_vec(h_xpos - xpos, h_ypos - ypos);
        auto hr_dist = rh_vec.norm();
        // Add the agent distance to robot
        human_info.dist = hr_dist;
        if (hr_dist < planning_radius_ && rh_vec.dot(robot_vec) >= 0) {
          // Update the dist map
          agent_dist_id_map[hr_dist] = h_id;
        }

        agent_vels_[agent.track_id].push_back(std::hypot(segment.twist.twist.linear.x, segment.twist.twist.linear.y));

        // Human state update --> MOVING
        if ((abs(segment.twist.twist.linear.x) + abs(segment.twist.twist.linear.y) + abs(segment.twist.twist.angular.z)) > CALC_EPS) {
          if (agents_states_.find(h_id) != agents_states_.end() && agents_states_[h_id] != agents::AgentState::BLOCKED) {
            agents_states_[h_id] = agents::AgentState::MOVING;
            // Update the state info message
            human_info.state = agents::AgentState::MOVING;
          }
        }

        auto n = agent_vels_[h_id].size();
        float average_vel = 0.0F;
        if (n != 0) {
          average_vel = accumulate(agent_vels_[h_id].begin(), agent_vels_[h_id].end(), 0.0) / n;
        }
        agent_nominal_vels_[h_id] = average_vel;

        if (n == window_moving_avg_) {
          agent_vels_[h_id].erase(agent_vels_[h_id].begin());
        }

        // Check if the human is still or halted
        if (prev_agents_.find(h_id) != prev_agents_.end()) {
          double human_move_dist = std::hypot(h_xpos - prev_agents_[h_id].position.x, h_ypos - prev_agents_[h_id].position.y);
          if (human_move_dist < CALC_EPS && agents_states_.find(h_id) != agents_states_.end()) {
            agent_still_[h_id] = true;

            // Human state update --> STOPPED
            if (agents_states_[h_id] == agents::AgentState::STOPPED || agents_states_[h_id] == agents::AgentState::MOVING) {
              // Update the state info message
              agents_states_[h_id] = agents::AgentState::STOPPED;
              human_info.state = agents::AgentState::STOPPED;
            }
          } else {
            agent_still_[h_id] = false;
            agents_states_[h_id] = agents::AgentState::MOVING;
            human_info.state = agents::AgentState::MOVING;
          }
        }

        // Check if the human is stuck and update the angle for backoff recovery
        if (h_id == stuck_agent_id_) {
          stuck_ = hr_dist <= planning_radius_ && rh_vec.dot(robot_vec) >= 0;
        }

        // agents_info.humans.push_back(human_info);
        humans_info[h_id] = human_info;
      }
    }
  }
  prev_agents_ = agents_;

  ROS_INFO_ONCE("Number of agents_agents_info, %d ", (int)agents_.size());

  // Get the distance sorted list of visible ids
  visible_agent_ids_.clear();

  for (auto &dist_id_map : agent_dist_id_map) {
    visible_agent_ids_.push_back(dist_id_map.second);
  }

  std::vector<int> sorted_ids;
  if (use_simulated_fov_) {
    /**************** for a centralised perception ***************/
    sorted_ids = filterVisibleAgents(agents_, visible_agent_ids_, agents_radii, robot_pose);
  } else {
    sorted_ids = visible_agent_ids_;
  }

  agents_info.visible = sorted_ids;

  for (auto &f_id : sorted_ids) {
    if (agents_states_[f_id] == agents::AgentState::NO_STATE || agents_states_[f_id] == agents::AgentState::STATIC) {
      agents_states_[f_id] = agents::AgentState::STATIC;
      humans_info[f_id].state = agents::AgentState::STATIC;
    }

    if (agent_still_.find(f_id) != agent_still_.end()) {
      if (agent_still_[f_id]) {
        agents_info.still.push_back(f_id);
      } else {
        agents_info.moving.push_back(f_id);
      }
    }

    agents_info.humans.push_back(humans_info[f_id]);
  }

  // Safety step for agents if agent_layers is not added in local costmap
  // Adds a temporary costmap around the agents to let planner plan safe
  // trajectories

  if (planning_mode_ > 0) {
    for (int i = 0; i < sorted_ids.size() && i < agents_.size(); i++) {
      geometry_msgs::Point v1;
      geometry_msgs::Point v2;
      geometry_msgs::Point v3;
      geometry_msgs::Point v4;
      auto idx = sorted_ids[i];
      auto agent_radius = agents_radii[idx];
      v1.x = agents_[idx].position.x - agent_radius, v1.y = agents_[idx].position.y - agent_radius, v1.z = 0.0;
      v2.x = agents_[idx].position.x - agent_radius, v2.y = agents_[idx].position.y + agent_radius, v2.z = 0.0;
      v3.x = agents_[idx].position.x + agent_radius, v3.y = agents_[idx].position.y + agent_radius, v3.z = 0.0;
      v4.x = agents_[idx].position.x + agent_radius, v4.y = agents_[idx].position.y - agent_radius, v4.z = 0.0;

      std::vector<geometry_msgs::Point> agent_pos_costmap;

      transform_stamped = tf_->lookupTransform(odom_frame_, map_frame_, ros::Time(0), ros::Duration(0.5));
      tf2::doTransform(v1, v1, transform_stamped);
      tf2::doTransform(v2, v2, transform_stamped);
      tf2::doTransform(v3, v3, transform_stamped);
      tf2::doTransform(v4, v4, transform_stamped);

      agent_pos_costmap.push_back(v1);
      agent_pos_costmap.push_back(v2);
      agent_pos_costmap.push_back(v3);
      agent_pos_costmap.push_back(v4);

      bool set_success = false;
      set_success = costmap_->setConvexPolygonCost(agent_pos_costmap, COST_OBS);
    }
  }

  agents_info_pub_.publish(agents_info);
};  // namespace agents

std::vector<int> Agents::filterVisibleAgents(std::map<int, geometry_msgs::Pose> tr_agents, std::vector<int> sorted_ids, std::map<int, double> agents_radii, geometry_msgs::Pose2D robot_pose) {
  std::vector<int> filtered_ids;
  auto xpos = robot_pose.x;
  auto ypos = robot_pose.y;

  if (!stuck_) {
    int n = MAX_PTS;
    if (sorted_ids.size() >= AGENT_NUM_TH) {
      n = MIN_PTS;
    }
    for (auto &it : sorted_ids) {
      // Ray Tracing
      double tm_x = tr_agents[it].position.x;
      double tm_y = tr_agents[it].position.y;
      // Get the difference between poses along x and y
      auto dx = (tm_x - xpos);
      auto dy = (tm_y - ypos);
      // Define step size in each direction
      dx = dx / n;
      dy = dy / n;

      // Checking using raytracing
      bool cell_collision = false;
      double x = xpos;
      double y = ypos;

      for (int j = 0; j < n; j++) {
        unsigned int mx;
        unsigned int my;

        double check_rad = agents_radii[it] + 0.1 + inflation_radius_;

        if (sqrt(((x - tm_x) * (x - tm_x)) + ((y - tm_y) * (y - tm_y))) <= check_rad) {
          break;
        }
        if (costmap_->worldToMap(x, y, mx, my)) {
          auto cellcost = costmap_->getCost(mx, my);
          if ((int)(cellcost) > COST_MIN && (int)(cellcost) < COST_OBS) {
            cell_collision = true;
            break;
          }
          x += dx;
          y += dy;
        }
      }

      if (!cell_collision) {
        filtered_ids.push_back(it);
      }
    }
    return filtered_ids;

  } else {
    for (int it = 0; it < 2 && it < sorted_ids.size(); it++) {
      if (sorted_ids[it] == stuck_agent_id_) {
        filtered_ids.push_back(sorted_ids[it]);
        break;
      }
    }
  }
  return filtered_ids;
}

void Agents::resetAgents() {
  // Reset the variables
  agents_states_.clear();
  agent_nominal_vels_.clear();
  stuck_agent_id_ = -1;
  stuck_ = false;
}

void Agents::loadRosParamFromNodeHandle(const ros::NodeHandle &private_nh) {
  private_nh.param("ns", ns_, std::string(""));
  private_nh.param("local_costmap/inflater/inflation_radius", inflation_radius_, 0.0);
  private_nh.param("planning_mode", planning_mode_, 0);
  private_nh.param("use_simulated_fov", use_simulated_fov_, false);
  private_nh.param("window_moving_avg", window_moving_avg_, WINDOW_MOVING_AVG);
  private_nh.param("HATebLocalPlannerROS/agent_radius", human_radius_, HUM_RADIUS);
  private_nh.param("HATebLocalPlannerROS/robot_radius", robot_radius_, ROBOT_RADIUS);
  private_nh.param("tracked_agents_sub_topic", tracked_agents_sub_topic_, std::string(AGENTS_SUB_TOPIC));
  private_nh.param("base_link_frame", base_link_frame_, std::string(BASE_LINK_FRAME));
  private_nh.param("map_frame", map_frame_, std::string(MAP_FRAME));
  private_nh.param("odom_frame", odom_frame_, std::string(ODOM_FRAME));
  private_nh.param("planning_radius", planning_radius_, PLANNING_RADIUS);
}

}  // namespace agents