Initial Commit (tested training, testing, and TRT conversion)

This commit is contained in:
Lu Junjie
2024-10-20 17:01:07 +08:00
parent 86d2f311f8
commit 5738088bae
221 changed files with 59249 additions and 6 deletions

View File

@@ -0,0 +1,481 @@
#include "flightlib/envs/quadrotor_env.hpp"
namespace flightlib {
QuadrotorEnv::QuadrotorEnv() : QuadrotorEnv(getenv("FLIGHTMARE_PATH") + std::string("/flightlib/configs/quadrotor_env.yaml")) {}
QuadrotorEnv::QuadrotorEnv(const std::string &cfg_path) : EnvBase() {
// load configuration file
YAML::Node cfg_ = YAML::LoadFile(cfg_path);
loadParam(cfg_);
quadrotor_ptr_ = std::make_shared<Quadrotor>();
// 1、define a bounding box (z is defined manually, different from spawn box)
// x_min, x_max, y_min, y_max, z_min, z_max
world_box_ << center_(0) - 0.5 * scale_(0), center_(0) + 0.5 * scale_(0), center_(1) - 0.5 * scale_(1), center_(1) + 0.5 * scale_(1), 0.0, 5.0;
if (!quadrotor_ptr_->setWorldBox(world_box_)) {
logger_.error("cannot set wolrd box");
};
// 2、define input and output dimension for the environment
obs_dim_ = kNObs;
act_dim_ = kNAct;
rew_dim_ = 1;
// 3、define planner
traj_opt_bridge = new TrajOptimizationBridge();
// 5、add camera
sgm_.reset(new sgm_gpu::SgmGpu(width_, height_));
if (!configCamera(cfg_)) {
logger_.error("Cannot config RGB Camera. Something wrong with the config file");
}
Vector<3> quad_size(0.01, 0.01, 0.01);
quadrotor_ptr_->setSize(quad_size);
is_collision_ = false;
steps_ = 0;
}
QuadrotorEnv::~QuadrotorEnv() { delete traj_opt_bridge; }
bool QuadrotorEnv::reset(Ref<Vector<>> obs, const bool random) {
quad_state_.setZero();
quad_act_.setZero();
is_collision_ = false;
steps_ = 0;
nearest_obstacle = 10;
// Dagger Training
if (random && !collect_data_) {
// 1.reset position.
do {
is_collision_ = false;
quad_state_.x(QS::POSX) = 0.40 * scale_(0) * uniform_dist_(random_gen_) + center_(0);
quad_state_.x(QS::POSY) = 0.40 * scale_(1) * uniform_dist_(random_gen_) + center_(1);
quad_state_.x(QS::POSZ) = 0.20 * scale_(2) * uniform_dist_(random_gen_) + center_(2);
collisionCheck(1.5);
} while (is_collision_);
// 2.reset orientation
float roll = 0.0;
float pitch = 0.0;
float yaw = 3.14159 * uniform_dist_(random_gen_);
Eigen::Quaternionf q_;
q_ = Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) *
Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX());
quad_state_.q(q_);
}
// Offline Data Collection
if (collect_data_) {
// 1.reset position.
do {
is_collision_ = false;
quad_state_.x(QS::POSX) = 0.5 * scale_(0) * uniform_dist_(random_gen_) + center_(0);
quad_state_.x(QS::POSY) = 0.5 * scale_(1) * uniform_dist_(random_gen_) + center_(1);
quad_state_.x(QS::POSZ) = 0.5 * scale_(2) * uniform_dist_(random_gen_) + center_(2);
collisionCheck(0.5);
} while (is_collision_);
// 2.reset orientation
float roll = (norm_dist_(random_gen_) * sqrt(roll_var_)) + 0.0;
float pitch = (norm_dist_(random_gen_) * sqrt(pitch_var_)) + 0.0;
float yaw = 3.14159 * uniform_dist_(random_gen_);
Eigen::Quaternionf q_;
q_ = Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) *
Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX());
quad_state_.q(q_);
}
// reset quadrotor with random states
quadrotor_ptr_->reset(quad_state_);
// Currently, since there is no controller, the desired state is equal to the actual state.
desired_p_ = Eigen::Vector3f(quad_state_.p(0), quad_state_.p(1), quad_state_.p(2));
desired_v_ = Eigen::Vector3f(quad_state_.v(0), quad_state_.v(1), quad_state_.v(2));
desired_a_ = Eigen::Vector3f(quad_state_.a(0), quad_state_.a(1), quad_state_.a(2));
// obtain observations
getObs(obs);
return true;
}
void QuadrotorEnv::setState(ConstRef<Vector<>> state) {
if (state.rows() != 13) {
std::cout << "ERROR: state must be 13 dim (P_xyz, V_xyz, A_xyz, Q_wxyz)" << std::endl;
return;
}
quad_state_.setZero();
quad_state_.x(QS::POSX) = state(0);
quad_state_.x(QS::POSY) = state(1);
quad_state_.x(QS::POSZ) = state(2);
quad_state_.x(QS::VELX) = state(3);
quad_state_.x(QS::VELY) = state(4);
quad_state_.x(QS::VELZ) = state(5);
quad_state_.x(QS::ACCX) = state(6);
quad_state_.x(QS::ACCY) = state(7);
quad_state_.x(QS::ACCZ) = state(8);
quad_state_.x(QS::ATTW) = state(9);
quad_state_.x(QS::ATTX) = state(10);
quad_state_.x(QS::ATTY) = state(11);
quad_state_.x(QS::ATTZ) = state(12);
quadrotor_ptr_->reset(quad_state_);
}
void QuadrotorEnv::setGoal(ConstRef<Vector<>> goal) {
if (goal.rows() != 3) {
std::cout << "ERROR: goal must be 3 dim (xyz)" << std::endl;
return;
}
goal_ = goal;
}
bool QuadrotorEnv::getObs(Ref<Vector<>> obs) {
// The actual position.
Eigen::Vector3f Pw(quad_state_.p(0), quad_state_.p(1), quad_state_.p(2));
Eigen::Quaternionf Qw = quad_state_.q();
// The desired state, same with the real flight.
Eigen::Matrix3f Rwb = quad_state_.R();
Eigen::Vector3f Vw(desired_v_(0), desired_v_(1), desired_v_(2));
Eigen::Vector3f Vb = Rwb.inverse() * Vw;
Eigen::Vector3f Aw(desired_a_(0), desired_a_(1), desired_a_(2));
Eigen::Vector3f Ab = Rwb.inverse() * Aw;
// Observation: p, q_wxyz in the world frame (for training); v, a in the body frame (for testing).
quad_obs_ << Pw(0), Pw(1), Pw(2), Vb(0), Vb(1), Vb(2), Ab(0), Ab(1), Ab(2), Qw.w(), Qw.x(), Qw.y(), Qw.z();
obs.segment<kNObs>(0) = quad_obs_;
return true;
}
bool QuadrotorEnv::getDepthImage(Ref<DepthImgVector<>> depth_img) {
if (!rgb_camera_left || !rgb_camera_left->getEnabledLayers()[1]) {
logger_.error("No RGB Camera or depth map is not enabled. Cannot retrieve depth images.");
return false;
}
rgb_camera_left->getDepthMap(depth_img_);
depth_img = Map<DepthImgVector<>>((float_t *)depth_img_.data, depth_img_.rows * depth_img_.cols);
return true;
}
bool QuadrotorEnv::getStereoImage(Ref<DepthImgVector<>> stereo_img) {
if (!rgb_camera_left || !rgb_camera_right) {
logger_.error("No Stereo Camera enabled. Cannot retrieve depth map.");
return false;
}
cv::Mat left_img, right_img;
rgb_camera_left->getRGBImage(left_img);
rgb_camera_right->getRGBImage(right_img);
// compute disparity image
cv::Mat stereo_(height_, width_, CV_32FC1);
computeDepthImage(left_img, right_img, &stereo_);
// fix the nan of stereo by gt depth (make it closer to RealSense 435, as the depth from 435 is better than from SGM directly)
if (rgb_camera_left->getEnabledLayers()[1]) {
if (rgb_camera_left->getDepthMap(depth_img_)) {
cv::Mat mask, mask1, mask2;
cv::compare(stereo_, 0, mask1, cv::CMP_EQ); // 将 A 中为 0 的位置置为 255其余位置置为 0
cv::compare(stereo_, 20, mask2, cv::CMP_GT); // 将 A 中大于 20 的位置置为 255其余位置置为 0
mask = mask1 | mask2; // 将两个掩码进行逻辑或操作
depth_img_.copyTo(stereo_, mask); // 将 B 中 mask 为 255 的位置的值复制到 A 中
}
}
stereo_img = Map<DepthImgVector<>>((float_t *)stereo_.data, stereo_.rows * stereo_.cols);
return true;
}
void QuadrotorEnv::setMapID(int id) {
// -1 represent using the latest map in imitation learning
if (id < 0)
map_idx_ = kdtrees.size() - 1;
else
map_idx_ = id;
}
void QuadrotorEnv::getCostAndGradient(ConstRef<Vector<>> endstate, int traj_id, float &cost, Ref<Vector<>> grad) {
if (endstate.rows() != 9) {
std::cout << "ERROR: endstate must be 9 dim (X_pva, Y_pva, Z_pva)" << std::endl;
return;
}
std::vector<double> endstate_, grad_;
double cost_;
for (size_t i = 0; i < endstate.rows(); i++) {
endstate_.push_back(static_cast<double>(endstate(i)));
}
traj_opt_bridge->setMap(esdf_maps[map_idx_], min_map_boundaries[map_idx_], max_map_boundaries[map_idx_]);
traj_opt_bridge->setState(quad_state_.p.cast<double>(), quad_state_.q().cast<double>(), quad_state_.v.cast<double>(),
quad_state_.a.cast<double>());
traj_opt_bridge->setGoal(goal_.cast<double>());
traj_opt_bridge->getCostAndGradient(endstate_, traj_id, cost_, grad_);
for (size_t i = 0; i < grad_.size(); i++) {
grad(i) = static_cast<float>(grad_[i]);
}
cost = static_cast<float>(cost_);
}
bool QuadrotorEnv::step(const Ref<Vector<>> act, Ref<Vector<>> obs, Ref<Vector<>> reward) {
// pythonsetGoal -> step
if (!act.allFinite() || act.rows() != 9 || reward.rows() != 1) {
std::cout << "ERROR: endstate must be 9 dim" << std::endl;
return false;
}
steps_++;
std::vector<double> endstate_;
for (size_t i = 0; i < act.rows(); i++) {
endstate_.push_back(static_cast<double>(act(i)));
}
traj_opt_bridge->setMap(esdf_maps[map_idx_], min_map_boundaries[map_idx_], max_map_boundaries[map_idx_]);
traj_opt_bridge->setState(quad_state_.p.cast<double>(), quad_state_.q().cast<double>(), quad_state_.v.cast<double>(),
quad_state_.a.cast<double>());
traj_opt_bridge->setGoal(goal_.cast<double>());
double cost_;
Eigen::Vector3d next_pos, next_vel, next_acc;
traj_opt_bridge->getNextStateAndCost(endstate_, cost_, next_pos, next_vel, next_acc, sim_dt_);
desired_p_ = next_pos.cast<float>();
desired_v_ = next_vel.cast<float>();
desired_a_ = next_acc.cast<float>();
reward(0) = static_cast<float>(cost_);
// calculate the state based on the desired state.
runControlAndUpdateState(desired_p_, desired_v_, desired_a_);
quadrotor_ptr_->getState(&quad_state_);
getObs(obs);
return true;
}
bool QuadrotorEnv::isTerminalState(Scalar &reward) {
// 1.if out of boundary
if (quad_state_.x(QS::POSX) <= (world_box_(0)) || quad_state_.x(QS::POSY) <= (world_box_(1)) || quad_state_.x(QS::POSZ) <= (world_box_(2)) ||
quad_state_.x(QS::POSX) >= (world_box_(3)) || quad_state_.x(QS::POSY) >= (world_box_(4)) || quad_state_.x(QS::POSZ) >= (world_box_(5))) {
reward = 0;
// std::cout<<"越界"<<std::endl;
return true;
}
// 2.if collision
if (is_collision_) {
reward = -1;
// std::cout<<"碰撞"<<std::endl;
return true;
}
// 3.prevent uav being trapped
if (steps_ > 10 && quad_state_.v.norm() < 0.6 && dagger_mode_) {
reward = 0;
return true;
}
// 4.if reach the goal
Eigen::Vector3f Pw(quad_state_.p(0), quad_state_.p(1), quad_state_.p(2));
Eigen::Vector3f Gw(goal_(0), goal_(1), goal_(2));
float dist = (Pw - Gw).norm();
if (dist < 4) {
reward = 0;
// std::cout<<"到达"<<std::endl;
return true;
}
reward = 0.0;
return false;
}
void QuadrotorEnv::collisionCheck(float dis) {
pcl::PointXYZ drone_;
drone_.x = quad_state_.x(QS::POSX);
drone_.y = quad_state_.x(QS::POSY);
drone_.z = quad_state_.x(QS::POSZ);
int K = 1;
std::vector<int> indices(K);
std::vector<float> distances(K); // the square of the distances to the neighboring points.
kdtrees[map_idx_]->nearestKSearch(drone_, K, indices, distances);
nearest_obstacle = distances[0];
if (distances[0] < dis * dis)
is_collision_ = true;
}
bool QuadrotorEnv::loadParam(const YAML::Node &cfg) {
// camera
width_ = cfg["rgb_camera_left"]["width"].as<int>();
height_ = cfg["rgb_camera_left"]["height"].as<int>();
fov_ = cfg["rgb_camera_left"]["fov"].as<Scalar>();
// train or test
collect_data_ = cfg["quadrotor_env"]["collect_data"].as<bool>();
sim_dt_ = cfg["quadrotor_env"]["sim_dt"].as<Scalar>();
// data_collection
roll_var_ = cfg["data_collection"]["roll_var"].as<Scalar>();
pitch_var_ = cfg["data_collection"]["pitch_var"].as<Scalar>();
// world box
for (int i = 0; i < 3; ++i) {
scale_(i) = cfg["quadrotor_env"]["bounding_box"][i].as<Scalar>();
center_(i) = cfg["quadrotor_env"]["bounding_box_origin"][i].as<Scalar>();
}
return true;
}
bool QuadrotorEnv::getAct(Ref<Vector<>> act) const {
if (quad_act_.allFinite()) {
act = quad_act_;
return true;
}
return false;
}
bool QuadrotorEnv::configCamera(const YAML::Node &cfg) {
if (!cfg["rgb_camera_left"]) {
logger_.error("Cannot config RGB Camera");
return false;
}
if (!cfg["rgb_camera_left"]["on"].as<bool>()) {
logger_.warn("Camera is off. Please turn it on.");
return false;
}
if (quadrotor_ptr_->getNumCamera() >= 2) {
logger_.warn("Camera has been added. Skipping the camera configuration.");
return false;
}
// create left camera --------------------------------------------
rgb_camera_left = std::make_shared<RGBCamera>();
// load camera settings
std::vector<Scalar> t_BC_vec = cfg["rgb_camera_left"]["t_BC"].as<std::vector<Scalar>>();
std::vector<Scalar> r_BC_vec = cfg["rgb_camera_left"]["r_BC"].as<std::vector<Scalar>>();
Vector<3> t_BC(t_BC_vec.data());
Matrix<3, 3> r_BC = (AngleAxis(r_BC_vec[2] * M_PI / 180.0, Vector<3>::UnitZ()) * AngleAxis(r_BC_vec[1] * M_PI / 180.0, Vector<3>::UnitY()) *
AngleAxis(r_BC_vec[0] * M_PI / 180.0, Vector<3>::UnitX()))
.toRotationMatrix();
// Flightmare's FOV is vertical, while usually is horizontal, so convert the input horizontal FOV to vertical FOV.
Scalar rgb_fov_deg_ = cfg["rgb_camera_left"]["fov"].as<Scalar>();
double hor_fov_radians = (M_PI * (rgb_fov_deg_ / 180.0));
Scalar img_rows_ = cfg["rgb_camera_left"]["height"].as<Scalar>();
Scalar img_cols_ = cfg["rgb_camera_left"]["width"].as<Scalar>();
double flightmare_fov = 2. * std::atan(std::tan(hor_fov_radians / 2) * img_rows_ / img_cols_);
flightmare_fov = (flightmare_fov / M_PI) * 180.0;
rgb_camera_left->setFOV(flightmare_fov);
rgb_camera_left->setWidth(cfg["rgb_camera_left"]["width"].as<int>());
rgb_camera_left->setHeight(cfg["rgb_camera_left"]["height"].as<int>());
rgb_camera_left->setRelPose(t_BC, r_BC);
rgb_camera_left->enableOpticalFlow(cfg["rgb_camera_left"]["enable_opticalflow"].as<bool>());
rgb_camera_left->enableSegmentation(cfg["rgb_camera_left"]["enable_segmentation"].as<bool>());
rgb_camera_left->enableDepth(cfg["rgb_camera_left"]["enable_depth"].as<bool>());
// add camera to the quadrotor
quadrotor_ptr_->addRGBCamera(rgb_camera_left);
// create right camera --------------------------------------------
bool have_right_camera = cfg["rgb_camera_right"]["on"].as<bool>();
if (have_right_camera) {
rgb_camera_right = std::make_shared<RGBCamera>();
// load camera settings
std::vector<Scalar> t_BC_vec_r = cfg["rgb_camera_right"]["t_BC"].as<std::vector<Scalar>>();
std::vector<Scalar> r_BC_vec_r = cfg["rgb_camera_right"]["r_BC"].as<std::vector<Scalar>>();
Vector<3> t_BC_r(t_BC_vec_r.data());
Matrix<3, 3> r_BC_r =
(AngleAxis(r_BC_vec_r[2] * M_PI / 180.0, Vector<3>::UnitZ()) * AngleAxis(r_BC_vec_r[1] * M_PI / 180.0, Vector<3>::UnitY()) *
AngleAxis(r_BC_vec_r[0] * M_PI / 180.0, Vector<3>::UnitX()))
.toRotationMatrix();
rgb_camera_right->setFOV(flightmare_fov);
rgb_camera_right->setWidth(cfg["rgb_camera_left"]["width"].as<int>());
rgb_camera_right->setHeight(cfg["rgb_camera_left"]["height"].as<int>());
rgb_camera_right->setRelPose(t_BC_r, r_BC_r);
rgb_camera_right->enableOpticalFlow(false);
rgb_camera_right->enableSegmentation(false);
rgb_camera_right->enableDepth(false);
// add camera to the quadrotor
quadrotor_ptr_->addRGBCamera(rgb_camera_right);
stereo_baseline_ = fabs(t_BC(1) - t_BC_r(1));
}
// adapt parameters
img_width_ = rgb_camera_left->getWidth();
img_height_ = rgb_camera_left->getHeight();
rgb_img_ = cv::Mat::zeros(img_height_, img_width_, CV_MAKETYPE(CV_8U, rgb_camera_left->getChannels()));
depth_img_ = cv::Mat::zeros(img_height_, img_width_, CV_32FC1);
return true;
}
void QuadrotorEnv::computeDepthImage(const cv::Mat &left_frame, const cv::Mat &right_frame, cv::Mat *const depth) {
cv::Mat disparity(height_, width_, CV_8UC1);
sgm_->computeDisparity(left_frame, right_frame, disparity);
disparity.convertTo(disparity, CV_32FC1);
// compute depth from disparity
float f = (width_ / 2.0) / std::tan((M_PI * (fov_ / 180.0)) / 2.0);
// depth = stereo_baseline_ * f / disparity
for (int r = 0; r < height_; ++r) {
for (int c = 0; c < width_; ++c) {
if (disparity.at<float>(r, c) == 0.0f) {
depth->at<float>(r, c) = 0.0f;
} else if (disparity.at<float>(r, c) == 255.0f) {
depth->at<float>(r, c) = 0.0f;
} else {
depth->at<float>(r, c) = static_cast<float>(stereo_baseline_) * f / disparity.at<float>(r, c);
}
}
}
}
bool QuadrotorEnv::getRGBImage(Ref<ImgVector<>> img, const bool rgb) {
if (!rgb_camera_left) {
logger_.error("No Camera! Cannot retrieve Images.");
return false;
}
rgb_camera_left->getRGBImage(rgb_img_);
if (rgb_img_.rows != height_ || rgb_img_.cols != width_) {
logger_.error("Image resolution mismatch. Aborting.. Image rows %d != %d, Image cols %d != %d", rgb_img_.rows, height_, rgb_img_.cols,
width_);
return false;
}
if (!rgb) {
// converting rgb image to gray image
cvtColor(rgb_img_, gray_img_, CV_RGB2GRAY);
// map cv::Mat data to Eiegn::Vector
img = Map<ImgVector<>>(gray_img_.data, gray_img_.rows * gray_img_.cols);
} else {
img = Map<ImgVector<>>(rgb_img_.data, rgb_img_.rows * rgb_img_.cols * rgb_camera_left->getChannels());
}
return true;
}
void QuadrotorEnv::runControlAndUpdateState(Eigen::Vector3f p_ref, Eigen::Vector3f v_ref, Eigen::Vector3f a_ref) {
Eigen::Vector3f p_cur;
p_cur = quad_state_.p;
Eigen::Vector3f dir_vel = v_ref;
Eigen::Vector3f dir_goal = goal_ - p_cur;
Eigen::Vector3f dir_des = dir_vel.normalized() + dir_goal.normalized();
float yaw_ref = atan2(dir_des(1), dir_des(0));
Vector<3> rpy_cur;
get_euler_from_R(rpy_cur, quad_state_.R());
yaw_ref = calculate_yaw(rpy_cur(2), yaw_ref, sim_dt_); // limit the yaw (required by controller)
Eigen::Quaternionf q_ref;
quadrotor_ptr_->runSimpleFlight(a_ref, yaw_ref, q_ref);
quadrotor_ptr_->setState(p_ref, v_ref, q_ref, a_ref, sim_dt_);
}
} // namespace flightlib

View File

@@ -0,0 +1,406 @@
#include "flightlib/envs/vec_env.hpp"
namespace flightlib {
template<typename EnvBase>
VecEnv<EnvBase>::VecEnv() : VecEnv(getenv("FLIGHTMARE_PATH") + std::string("/flightlib/configs/vec_env.yaml")) {}
template<typename EnvBase>
VecEnv<EnvBase>::VecEnv(const YAML::Node& cfg_node) : cfg_(cfg_node) {
init();
}
template<typename EnvBase>
VecEnv<EnvBase>::VecEnv(const std::string& cfgs, const bool from_file) {
// load environment configuration
if (from_file)
cfg_ = YAML::LoadFile(cfgs);
else
cfg_ = YAML::Load(cfgs);
init();
}
template<typename EnvBase>
void VecEnv<EnvBase>::init(void) {
// note that the cfg are input from python, and many have changed from vec_end.yaml
unity_render_ = cfg_["env"]["render"].as<bool>();
supervised_mode_ = cfg_["env"]["supervised"].as<bool>();
dagger_mode_ = cfg_["env"]["imitation"].as<bool>();
seed_ = cfg_["env"]["seed"].as<int>();
num_envs_ = cfg_["env"]["num_envs"].as<int>();
num_threads_ = cfg_["env"]["num_threads"].as<int>();
scene_id_ = cfg_["env"]["scene_id"].as<SceneID>();
ply_path_ = getenv("FLIGHTMARE_PATH") + cfg_["env"]["ply_path"].as<std::string>();
avg_tree_spacing_ = cfg_["unity"]["avg_tree_spacing"].as<Scalar>();
pointcloud_resolution_ = cfg_["unity"]["pointcloud_resolution"].as<Scalar>();
for (int i = 0; i < 3; ++i) {
bounding_box_(i) = cfg_["unity"]["bounding_box"][i].as<Scalar>();
bounding_box_origin_(i) = cfg_["unity"]["bounding_box_origin"][i].as<Scalar>();
}
// set threads
omp_set_num_threads(cfg_["env"]["num_threads"].as<int>());
// create & setup environments
const bool render = false;
for (int i = 0; i < num_envs_; i++) {
envs_.push_back(std::make_unique<EnvBase>());
}
// set Unity (init unity_bridge_ptr_ and add quadrotors to envs)
setUnity(unity_render_);
obs_dim_ = envs_[0]->getObsDim();
act_dim_ = envs_[0]->getActDim();
rew_dim_ = envs_[0]->getRewDim();
img_width_ = envs_[0]->getImgWidth();
img_height_ = envs_[0]->getImgHeight();
// if supervised_mode, then generate map from .ply
if (supervised_mode_)
generateMaps();
// set dagger_mode to each env
for (int i = 0; i < num_envs_; i++)
envs_[i]->setDAggerMode(dagger_mode_);
std::cout << "Vectorized Environment:\n"
<< "dagger mode = [" << dagger_mode_ << "]\n"
<< "supervised mode = [" << supervised_mode_ << "]\n"
<< "obs dim = [" << obs_dim_ << "]\n"
<< "act dim = [" << act_dim_ << "]\n"
<< "img width = [" << img_width_ << "]\n"
<< "img height = [" << img_height_ << "]\n"
<< "num_envs = [" << num_envs_ << "]\n"
<< "num_thread = [" << num_threads_ << "]\n"
<< "scene_id = [" << scene_id_ << "]" << std::endl;
}
template<typename EnvBase>
VecEnv<EnvBase>::~VecEnv() {}
// ====================== set functions ===================== //
template<typename EnvBase>
bool VecEnv<EnvBase>::reset(Ref<MatrixRowMajor<>> obs) {
if (obs.rows() != num_envs_ || obs.cols() != obs_dim_) {
logger_.error("Input matrix dimensions do not match with that of the environment.");
return false;
}
for (int i = 0; i < num_envs_; i++) {
envs_[i]->reset(obs.row(i));
}
if (unity_render_ && unity_ready_) {
frameID = 1;
FrameID frameID_rt;
unity_bridge_ptr_->getRender(frameID);
unity_bridge_ptr_->handleOutput(frameID_rt);
while (frameID != frameID_rt)
unity_bridge_ptr_->handleOutput(frameID_rt);
}
return true;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::setState(ConstRef<MatrixRowMajor<>> state) {
if (state.rows() != num_envs_ || state.cols() != 13) { // 13: pvaq
logger_.error("Input state dimensions do not match with state.");
return false;
}
for (int i = 0; i < num_envs_; i++) {
envs_[i]->setState(state.row(i));
}
return true;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::setGoal(ConstRef<MatrixRowMajor<>> goal) {
if (goal.rows() != num_envs_ || goal.cols() != 3) {
logger_.error("Input goal dimensions do not match with 3.");
return false;
}
for (int i = 0; i < num_envs_; i++) {
envs_[i]->setGoal(goal.row(i));
}
return true;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::step(Ref<MatrixRowMajor<>> act, Ref<MatrixRowMajor<>> obs, Ref<MatrixRowMajor<>> reward, Ref<BoolVector<>> done) {
if (act.rows() != num_envs_ || act.cols() != act_dim_ || obs.rows() != num_envs_ || obs.cols() != obs_dim_ || reward.rows() != num_envs_ ||
reward.cols() != rew_dim_ || done.rows() != num_envs_ || done.cols() != 1) {
logger_.error("Input matrix dimensions do not match with that of the environment.");
return false;
}
#pragma omp parallel for schedule(dynamic)
for (int i = 0; i < num_envs_; i++) {
perAgentStep(i, act, obs, reward, done);
}
if (unity_render_ && unity_ready_) {
frameID++;
FrameID frameID_rt;
unity_bridge_ptr_->getRender(frameID);
unity_bridge_ptr_->handleOutput(frameID_rt);
while (frameID != frameID_rt)
unity_bridge_ptr_->handleOutput(frameID_rt);
}
return true;
}
template<typename EnvBase>
void VecEnv<EnvBase>::perAgentStep(int agent_id, Ref<MatrixRowMajor<>> act, Ref<MatrixRowMajor<>> obs, Ref<MatrixRowMajor<>> reward,
Ref<BoolVector<>> done) {
envs_[agent_id]->step(act.row(agent_id), obs.row(agent_id), reward.row(agent_id));
// use larger collision threshold in training and lower in testing
if (dagger_mode_)
envs_[agent_id]->collisionCheck(0.3);
else
envs_[agent_id]->collisionCheck(0.1);
Scalar terminal_reward = 0;
done(agent_id) = envs_[agent_id]->isTerminalState(terminal_reward);
if (done[agent_id]) {
envs_[agent_id]->reset(obs.row(agent_id));
}
}
template<typename EnvBase>
void VecEnv<EnvBase>::setMapID(ConstRef<IntVector<>> id) {
if (id.rows() != num_envs_) {
logger_.error("Input matrix dimensions do not match with that of the environment.");
return;
}
for (int i = 0; i < num_envs_; i++) {
envs_[i]->setMapID(id(i));
}
}
template<typename EnvBase>
void VecEnv<EnvBase>::setSeed(const int seed) {
int seed_inc = seed;
for (int i = 0; i < num_envs_; i++)
envs_[i]->setSeed(seed_inc++);
}
// ====================== set functions ===================== //
template<typename EnvBase>
void VecEnv<EnvBase>::getObs(Ref<MatrixRowMajor<>> obs) {
for (int i = 0; i < num_envs_; i++)
envs_[i]->getObs(obs.row(i));
}
template<typename EnvBase>
bool VecEnv<EnvBase>::getRGBImage(Ref<ImgMatrixRowMajor<>> img, const bool rgb_image) {
bool valid_img = true;
for (int i = 0; i < num_envs_; i++) {
valid_img &= envs_[i]->getRGBImage(img.row(i), rgb_image);
}
return valid_img;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::getStereoImage(Ref<DepthImgMatrixRowMajor<>> stereo_img) {
bool valid_img = true;
for (int i = 0; i < num_envs_; i++) {
valid_img &= envs_[i]->getStereoImage(stereo_img.row(i));
}
return valid_img;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::getDepthImage(Ref<DepthImgMatrixRowMajor<>> depth_img) {
bool valid_img = true;
for (int i = 0; i < num_envs_; i++) {
valid_img &= envs_[i]->getDepthImage(depth_img.row(i));
}
return valid_img;
}
template<typename EnvBase>
void VecEnv<EnvBase>::getCostAndGradient(ConstRef<MatrixRowMajor<>> dp, ConstRef<IntVector<>> traj_id, Ref<Vector<>> cost,
Ref<MatrixRowMajor<>> grad) {
#pragma omp parallel for schedule(dynamic) num_threads(num_threads_)
for (int i = 0; i < num_envs_; i++) {
envs_[i]->getCostAndGradient(dp.row(i), traj_id(i), cost(i), grad.row(i));
}
}
// ====================== unity functions ===================== //
template<typename EnvBase>
bool VecEnv<EnvBase>::setUnity(bool render) {
unity_render_ = render;
if (unity_render_ && unity_bridge_ptr_ == nullptr) {
// create unity bridge
unity_bridge_ptr_ = UnityBridge::getInstance();
// add objects to Unity
for (int i = 0; i < num_envs_; i++) {
envs_[i]->addObjectsToUnity(unity_bridge_ptr_);
}
logger_.info("Flightmare Bridge is created.");
}
return true;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::spawnTrees() {
if (!unity_ready_ || unity_bridge_ptr_ == nullptr)
return false;
bool spawned = unity_bridge_ptr_->spawnTrees(bounding_box_, bounding_box_origin_, avg_tree_spacing_);
return spawned;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::savePointcloud(int ply_id) {
if (!unity_ready_ || unity_bridge_ptr_ == nullptr)
return false;
Vector<3> min_corner = bounding_box_origin_ - 0.5 * bounding_box_;
Vector<3> max_corner = bounding_box_origin_ + 0.5 * bounding_box_;
unity_bridge_ptr_->generatePointcloud(min_corner, max_corner, ply_id, ply_path_, scene_id_, pointcloud_resolution_);
return true;
}
template<typename EnvBase>
bool VecEnv<EnvBase>::spawnTreesAndSavePointcloud(int ply_id_in, float spacing) {
Scalar avg_tree_spacing = avg_tree_spacing_;
if (spacing > 0)
avg_tree_spacing = spacing;
int ply_id = envs_[0]->getMapNum();
if (ply_id_in >= 0)
ply_id = ply_id_in;
if (!unity_ready_ || unity_bridge_ptr_ == nullptr)
return false;
bool spawned = unity_bridge_ptr_->spawnTrees(bounding_box_, bounding_box_origin_, avg_tree_spacing);
Vector<3> min_corner = bounding_box_origin_ - 0.5 * bounding_box_;
Vector<3> max_corner = bounding_box_origin_ + 0.5 * bounding_box_;
unity_bridge_ptr_->generatePointcloud(min_corner, max_corner, ply_id, ply_path_, scene_id_, pointcloud_resolution_);
usleep(1 * 1e6); // waitting 1s for generating completely
// KDtree, for collision detection
pcl::search::KdTree<pcl::PointXYZ> kdtree;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
std::cout << "Map Path: " << ply_path_ + "pointcloud-" + std::to_string(ply_id) + ".ply" << std::endl;
pcl::io::loadPLYFile<pcl::PointXYZ>(ply_path_ + "pointcloud-" + std::to_string(ply_id) + ".ply", *cloud);
kdtree.setInputCloud(cloud); // 0.3s
// ESDF, for gradient calculation (map_id is required)
Eigen::Vector3d map_boundary_min, map_boundary_max;
std::shared_ptr<sdf_tools::SignedDistanceField> esdf_map = traj_opt::SdfConstruction(cloud, map_boundary_min, map_boundary_max);
for (int i = 0; i < num_envs_; i++) {
envs_[i]->addKdtree(std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>(kdtree));
envs_[i]->addESDFMap(esdf_map);
envs_[i]->addMapSize(map_boundary_min, map_boundary_max);
}
return true;
}
// For supervised learning
template<typename EnvBase>
void VecEnv<EnvBase>::generateMaps() {
std::vector<std::string> ply_files;
for (const auto& entry : std::filesystem::directory_iterator(ply_path_)) {
if (entry.is_regular_file() && entry.path().extension() == ".ply") {
ply_files.push_back(entry.path().string());
}
}
// Sort according to the number of the filename.
std::sort(ply_files.begin(), ply_files.end(), [this](const std::string& a, const std::string& b) {
return extract_number(std::filesystem::path(a).filename().string()) < extract_number(std::filesystem::path(b).filename().string());
});
for (auto ply_file : ply_files) {
std::cout << "load ply file: " << ply_file << std::endl;
pcl::search::KdTree<pcl::PointXYZ> kdtree;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPLYFile<pcl::PointXYZ>(ply_file, *cloud);
// KDtree, for collision detection
kdtree.setInputCloud(cloud); // 0.3s
// ESDF, for gradient calculation
Eigen::Vector3d map_boundary_min, map_boundary_max;
std::shared_ptr<sdf_tools::SignedDistanceField> esdf_map = traj_opt::SdfConstruction(cloud, map_boundary_min, map_boundary_max);
std::cout << "pc min:" << map_boundary_min.transpose() << " pc max:" << map_boundary_max.transpose() << std::endl;
for (int i = 0; i < num_envs_; i++) {
envs_[i]->addKdtree(std::make_shared<pcl::search::KdTree<pcl::PointXYZ>>(kdtree));
envs_[i]->addESDFMap(esdf_map);
envs_[i]->addMapSize(map_boundary_min, map_boundary_max);
}
}
}
template<typename EnvBase>
bool VecEnv<EnvBase>::connectUnity(void) {
if (unity_bridge_ptr_ == nullptr)
return false;
unity_ready_ = unity_bridge_ptr_->connectUnity(scene_id_);
return unity_ready_;
}
template<typename EnvBase>
void VecEnv<EnvBase>::render(void) {
if (unity_render_ && unity_ready_) {
frameID++;
FrameID frameID_rt;
unity_bridge_ptr_->getRender(frameID);
unity_bridge_ptr_->handleOutput(frameID_rt);
while (frameID != frameID_rt)
unity_bridge_ptr_->handleOutput(frameID_rt);
}
}
template<typename EnvBase>
void VecEnv<EnvBase>::disconnectUnity(void) {
if (unity_bridge_ptr_ != nullptr) {
unity_bridge_ptr_->disconnectUnity();
unity_ready_ = false;
} else {
logger_.warn("Flightmare Unity Bridge is not initialized.");
}
}
template<typename EnvBase>
void VecEnv<EnvBase>::close() {
for (int i = 0; i < num_envs_; i++) {
envs_[i]->close();
}
}
// ====================== other functions ===================== //
// Extract the number from the filename (e.g., pointcloud-1.ply)
template<typename EnvBase>
int VecEnv<EnvBase>::extract_number(const std::string& filename) {
std::regex number_regex("pointcloud-(\\d+)\\.ply");
std::smatch match;
if (std::regex_search(filename, match, number_regex)) {
return std::stoi(match[1]);
}
return -1; // If no number is found, return -1
}
// IMPORTANT. Otherwise:
// Segmentation fault (core dumped)
template class VecEnv<QuadrotorEnv>;
} // namespace flightlib