Autoware 定位之基于ARTag的landmark定位(六)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务,并且需要GPU资源,可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU,按时收费每卡2.6元,月卡只需要1.7元每小时,并附带200G的免费磁盘空间。通过链接注册并联系客服,可以获得20元代金券(相当于6-7H的免费GPU资源)。欢迎大家体验一下~

0. 简介

这一讲按照《Autoware 技术代码解读(三)》梳理的顺序,我们来说一说Autoware中基于地标的定位,这里的地标可以是:相机检测到的AR标签,还可以是通过LiDAR检测到的强度特征的板块。由于这些地标易于检测和估计姿态,如果地图上预先标注了地标的姿态,那么可以从检测到的地标的姿态计算出自我姿态。

在这里插入图片描述

1. 代码阅读

1.1 基于ar_tag的定位功能

该代码实现了一个基于ArUco标记的定位器节点,其中包含了构造函数和设置节点参数、日志记录、tf变换、图像传输、订阅者和发布者等功能。设置函数setup()用于初始化节点参数、日志记录、tf变换、图像传输、订阅者和发布者等。通过订阅地图数据和图像数据,进行ArUco标记的检测,并将检测到的标记位置信息发布出去。同时,还会根据相机信息进行相机参数的设置,并根据EKF姿态信息发布位置信息。

setup()函数中,通过声明节点参数、设置检测模式、初始化tf变换的缓冲区、监听器和广播器、初始化图像传输、设置订阅者和发布者等,完成了节点的初始化工作。在地图数据回调函数中,解析特定类型的地标信息并发布可视化消息;在图像数据回调函数中,进行ArUco标记的检测,并在图像中绘制检测到的标记,然后发布处理后的图像和诊断信息;相机信息回调函数用于设置相机参数;EKF姿态信息回调函数用于保存最新的姿态信息。最后,根据ArUco标记的位置信息,将位置信息发布为tf变换,并进行位置信息的计算和发布。这个流程主要依赖以下三个函数:

  1. map_bin_callback()函数:当接收到地图数据时,解析地图中的特定类型的地标信息,并将其转换为可视化消息后发布出去。
  2. image_callback()函数:当接收到图像数据时,进行ArUco标记的检测,并在图像中绘制检测到的标记,然后发布处理后的图像和诊断信息。
  3. publish_pose_as_base_link()函数:将相机到ArUco标记的位置信息发布为tf变换,并计算位置信息。

在这里插入图片描述

/// @brief 构造函数
/// @param options ros2节点选项
ArTagBasedLocalizer::ArTagBasedLocalizer(const rclcpp::NodeOptions & options)
: Node("ar_tag_based_localizer", options), cam_info_received_(false)
{
}/// @brief 用于设置节点参数、日志记录、tf变换、图像传输、订阅者和发布者
/// @return 
bool ArTagBasedLocalizer::setup()
{/*Declare node parameters*/marker_size_ = static_cast<float>(this->declare_parameter<double>("marker_size"));target_tag_ids_ = this->declare_parameter<std::vector<std::string>>("target_tag_ids");base_covariance_ = this->declare_parameter<std::vector<double>>("base_covariance");distance_threshold_squared_ = std::pow(this->declare_parameter<double>("distance_threshold"), 2);ekf_time_tolerance_ = this->declare_parameter<double>("ekf_time_tolerance");ekf_position_tolerance_ = this->declare_parameter<double>("ekf_position_tolerance");std::string detection_mode = this->declare_parameter<std::string>("detection_mode");//设置 ArUco 检测器的检测模式float min_marker_size = static_cast<float>(this->declare_parameter<double>("min_marker_size"));if (detection_mode == "DM_NORMAL") {detector_.setDetectionMode(aruco::DM_NORMAL, min_marker_size);} else if (detection_mode == "DM_FAST") {detector_.setDetectionMode(aruco::DM_FAST, min_marker_size);} else if (detection_mode == "DM_VIDEO_FAST") {detector_.setDetectionMode(aruco::DM_VIDEO_FAST, min_marker_size);} else {// ErrorRCLCPP_ERROR_STREAM(this->get_logger(), "Invalid detection_mode: " << detection_mode);return false;}/*Log parameter info*/RCLCPP_INFO_STREAM(this->get_logger(), "min_marker_size: " << min_marker_size);RCLCPP_INFO_STREAM(this->get_logger(), "detection_mode: " << detection_mode);RCLCPP_INFO_STREAM(this->get_logger(), "thresMethod: " << detector_.getParameters().thresMethod);RCLCPP_INFO_STREAM(this->get_logger(), "marker_size_: " << marker_size_);/*初始化 tf 变换的缓冲区、监听器和广播器*/tf_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());tf_listener_ = std::make_unique<tf2_ros::TransformListener>(*tf_buffer_);tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(this);/*Initialize image transport*/it_ = std::make_unique<image_transport::ImageTransport>(shared_from_this());/*Subscribers*/map_bin_sub_ = this->create_subscription<HADMapBin>("~/input/lanelet2_map", rclcpp::QoS(10).durability(rclcpp::DurabilityPolicy::TransientLocal),std::bind(&ArTagBasedLocalizer::map_bin_callback, this, std::placeholders::_1));//初始化图像传输rclcpp::QoS qos_sub(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));qos_sub.best_effort();image_sub_ = this->create_subscription<Image>("~/input/image", qos_sub,std::bind(&ArTagBasedLocalizer::image_callback, this, std::placeholders::_1));cam_info_sub_ = this->create_subscription<CameraInfo>("~/input/camera_info", qos_sub,std::bind(&ArTagBasedLocalizer::cam_info_callback, this, std::placeholders::_1));ekf_pose_sub_ = this->create_subscription<PoseWithCovarianceStamped>("~/input/ekf_pose", qos_sub,std::bind(&ArTagBasedLocalizer::ekf_pose_callback, this, std::placeholders::_1));/*Publishers*/rclcpp::QoS qos_marker = rclcpp::QoS(rclcpp::KeepLast(10));qos_marker.transient_local();qos_marker.reliable();marker_pub_ = this->create_publisher<MarkerArray>("~/debug/marker", qos_marker);rclcpp::QoS qos_pub(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));image_pub_ = it_->advertise("~/debug/result", 1);pose_pub_ =this->create_publisher<PoseWithCovarianceStamped>("~/output/pose_with_covariance", qos_pub);diag_pub_ = this->create_publisher<DiagnosticArray>("/diagnostics", qos_pub);RCLCPP_INFO(this->get_logger(), "Setup of ar_tag_based_localizer node is successful!");return true;
}
/// @brief 当接收到地图数据时,该回调函数会解析地图中的特定类型的地标信息,并将其转换为可视化消息后发布出去
/// @param msg 类型为 HADMapBin::ConstSharedPtr 的地图数据指针
void ArTagBasedLocalizer::map_bin_callback(const HADMapBin::ConstSharedPtr & msg)
{const std::vector<landmark_manager::Landmark> landmarks =landmark_manager::parse_landmarks(msg, "apriltag_16h5", this->get_logger());//提取特定类型的地标信息,并将其转换为 Landmark 结构体的向量for (const landmark_manager::Landmark & landmark : landmarks) {landmark_map_[landmark.id] = landmark.pose;//解析得到的地标信息存储到 landmark_map_ 中}const MarkerArray marker_msg = landmark_manager::convert_landmarks_to_marker_array_msg(landmarks);marker_pub_->publish(marker_msg);
}
/// @brief 当接收到图像数据时,该回调函数会进行 ArUco 标记的检测,并在图像中绘制检测到的标记,然后发布处理后的图像和诊断信息。
/// @param msg 类型为 Image::ConstSharedPtr 的图像数据指针
void ArTagBasedLocalizer::image_callback(const Image::ConstSharedPtr & msg)
{//检查是否有订阅者,如果没有则不进行 ArUco 标记的检测if ((image_pub_.getNumSubscribers() == 0) && (pose_pub_->get_subscription_count() == 0)) {RCLCPP_DEBUG(this->get_logger(), "No subscribers, not looking for ArUco markers");return;}//检查是否已接收到相机信息if (!cam_info_received_) {RCLCPP_DEBUG(this->get_logger(), "No cam_info has been received.");return;}//将图像数据转换为 OpenCV 格式,并进行 ArUco 标记的检测const builtin_interfaces::msg::Time curr_stamp = msg->header.stamp;cv_bridge::CvImagePtr cv_ptr;try {cv_ptr = cv_bridge::toCvCopy(*msg, sensor_msgs::image_encodings::RGB8);} catch (cv_bridge::Exception & e) {RCLCPP_ERROR(this->get_logger(), "cv_bridge exception: %s", e.what());return;}cv::Mat in_image = cv_ptr->image;// 检测结果将存储在 markers 中std::vector<aruco::Marker> markers;// ok, let's detectdetector_.detect(in_image, markers, cam_param_, marker_size_, false);// 每个检测到的标记都会发布位置信息for (const aruco::Marker & marker : markers) {const tf2::Transform tf_cam_to_marker = aruco_marker_to_tf2(marker);//位置信息发布为 tf 变换,并绘制在图像中TransformStamped tf_cam_to_marker_stamped;tf2::toMsg(tf_cam_to_marker, tf_cam_to_marker_stamped.transform);tf_cam_to_marker_stamped.header.stamp = curr_stamp;tf_cam_to_marker_stamped.header.frame_id = msg->header.frame_id;tf_cam_to_marker_stamped.child_frame_id = "detected_marker_" + std::to_string(marker.id);tf_broadcaster_->sendTransform(tf_cam_to_marker_stamped);//发布 tf 变换PoseStamped pose_cam_to_marker;tf2::toMsg(tf_cam_to_marker, pose_cam_to_marker.pose);//根据 tf 变换计算位置信息pose_cam_to_marker.header.stamp = curr_stamp;pose_cam_to_marker.header.frame_id = msg->header.frame_id;publish_pose_as_base_link(pose_cam_to_marker, std::to_string(marker.id));//发布位置信息// 画出检测到的标记marker.draw(in_image, cv::Scalar(0, 0, 255), 2);//在图像中绘制检测到的标记}// 画出每个标记的 3D 立方体if (cam_param_.isValid()) {for (aruco::Marker & marker : markers) {aruco::CvDrawingUtils::draw3dAxis(in_image, marker, cam_param_);}}if (image_pub_.getNumSubscribers() > 0) {// 将处理后的图像发布出去cv_bridge::CvImage out_msg;out_msg.header.stamp = curr_stamp;out_msg.encoding = sensor_msgs::image_encodings::RGB8;out_msg.image = in_image;image_pub_.publish(out_msg.toImageMsg());}const int detected_tags = static_cast<int>(markers.size());diagnostic_msgs::msg::DiagnosticStatus diag_status;// 根据检测到的标记数量发布诊断信息if (detected_tags > 0) {diag_status.level = diagnostic_msgs::msg::DiagnosticStatus::OK;diag_status.message = "AR tags detected. The number of tags: " + std::to_string(detected_tags);} else {diag_status.level = diagnostic_msgs::msg::DiagnosticStatus::WARN;diag_status.message = "No AR tags detected.";}diag_status.name = "localization: " + std::string(this->get_name());diag_status.hardware_id = this->get_name();diagnostic_msgs::msg::KeyValue key_value;key_value.key = "Number of Detected AR Tags";key_value.value = std::to_string(detected_tags);diag_status.values.push_back(key_value);DiagnosticArray diag_msg;//发布诊断信息diag_msg.header.stamp = this->now();diag_msg.status.push_back(diag_status);diag_pub_->publish(diag_msg);
}// wait for one camera info, then shut down that subscriber
/// @brief 当接收到相机信息后,将其转换为OpenCV矩阵并存储
/// @param msg 相机信息
void ArTagBasedLocalizer::cam_info_callback(const CameraInfo & msg)
{if (cam_info_received_) {return;}
//将输入的相机信息 msg 转换成 OpenCV 的矩阵格式cv::Mat camera_matrix(3, 4, CV_64FC1, 0.0);camera_matrix.at<double>(0, 0) = msg.p[0];camera_matrix.at<double>(0, 1) = msg.p[1];camera_matrix.at<double>(0, 2) = msg.p[2];camera_matrix.at<double>(0, 3) = msg.p[3];camera_matrix.at<double>(1, 0) = msg.p[4];camera_matrix.at<double>(1, 1) = msg.p[5];camera_matrix.at<double>(1, 2) = msg.p[6];camera_matrix.at<double>(1, 3) = msg.p[7];camera_matrix.at<double>(2, 0) = msg.p[8];camera_matrix.at<double>(2, 1) = msg.p[9];camera_matrix.at<double>(2, 2) = msg.p[10];camera_matrix.at<double>(2, 3) = msg.p[11];cv::Mat distortion_coeff(4, 1, CV_64FC1);for (int i = 0; i < 4; ++i) {distortion_coeff.at<double>(i, 0) = 0;}const cv::Size size(static_cast<int>(msg.width), static_cast<int>(msg.height));cam_param_ = aruco::CameraParameters(camera_matrix, distortion_coeff, size);cam_info_received_ = true;
}/// @brief 更新最新的扩展卡尔曼滤波器(EKF)姿态估计值
/// @param msg 带协方差的姿态消息
void ArTagBasedLocalizer::ekf_pose_callback(const PoseWithCovarianceStamped & msg)
{//直接将输入的姿态消息 msg 存储在 latest_ekf_pose_ 变量中latest_ekf_pose_ = msg;
}/// @brief 发布基于基准链接的位置信息
/// @param sensor_to_tag 传感器到标签的姿态信息
/// @param tag_id 标签ID
void ArTagBasedLocalizer::publish_pose_as_base_link(const PoseStamped & sensor_to_tag, const std::string & tag_id)
{// Check tag_idif (std::find(target_tag_ids_.begin(), target_tag_ids_.end(), tag_id) == target_tag_ids_.end()) {//检查是否在目标标签ID中RCLCPP_INFO_STREAM(this->get_logger(), "tag_id(" << tag_id << ") is not in target_tag_ids");return;}if (landmark_map_.count(tag_id) == 0) {//检查是否已经接收到地图信息RCLCPP_INFO_STREAM(this->get_logger(), "tag_id(" << tag_id << ") is not in landmark_map_");return;}// Range filterconst double distance_squared = sensor_to_tag.pose.position.x * sensor_to_tag.pose.position.x +sensor_to_tag.pose.position.y * sensor_to_tag.pose.position.y +sensor_to_tag.pose.position.z * sensor_to_tag.pose.position.z;if (distance_threshold_squared_ < distance_squared) {//检查是否超出距离阈值return;}// Transform to base_linkPoseStamped base_link_to_tag;try {const TransformStamped transform =tf_buffer_->lookupTransform("base_link", sensor_to_tag.header.frame_id, tf2::TimePointZero);tf2::doTransform(sensor_to_tag, base_link_to_tag, transform);//将传感器到标签的姿态信息转换为基准链接到标签的姿态信息base_link_to_tag.header.frame_id = "base_link";} catch (tf2::TransformException & ex) {RCLCPP_INFO(this->get_logger(), "Could not transform base_link to camera: %s", ex.what());return;}// (1) map_to_tagconst Pose & map_to_tag = landmark_map_.at(tag_id);const Eigen::Affine3d map_to_tag_affine = pose_to_affine3d(map_to_tag);// (2) tag_to_base_linkconst Eigen::Affine3d base_link_to_tag_affine = pose_to_affine3d(base_link_to_tag.pose);const Eigen::Affine3d tag_to_base_link_affine = base_link_to_tag_affine.inverse();// calculate map_to_base_link// 计算地图到基准链接的姿态变换const Eigen::Affine3d map_to_base_link_affine = map_to_tag_affine * tag_to_base_link_affine;const Pose map_to_base_link = tf2::toMsg(map_to_base_link_affine);// 如果当前姿态与最新的 EKF 姿态估计值的差异超过 ekf_time_tolerance_,则不会发布const rclcpp::Duration diff_time =rclcpp::Time(sensor_to_tag.header.stamp) - rclcpp::Time(latest_ekf_pose_.header.stamp);if (diff_time.seconds() > ekf_time_tolerance_) {//计算地图到基准链接的姿态变换,并进行时间戳和姿态差异的检查RCLCPP_INFO(this->get_logger(),"latest_ekf_pose_ is older than %f seconds compared to current frame. ""latest_ekf_pose_.header.stamp: %d.%d, sensor_to_tag.header.stamp: %d.%d",ekf_time_tolerance_, latest_ekf_pose_.header.stamp.sec, latest_ekf_pose_.header.stamp.nanosec,sensor_to_tag.header.stamp.sec, sensor_to_tag.header.stamp.nanosec);return;}// 如果当前姿态与最新的 EKF 姿态估计值的差异超过 ekf_position_tolerance_,则不会发布const Pose curr_pose = map_to_base_link;const Pose latest_ekf_pose = latest_ekf_pose_.pose.pose;const double diff_position = norm(curr_pose.position, latest_ekf_pose.position);if (diff_position > ekf_position_tolerance_) {RCLCPP_INFO(this->get_logger(),"curr_pose differs from latest_ekf_pose_ by more than %f m. ""curr_pose: (%f, %f, %f), latest_ekf_pose: (%f, %f, %f)",ekf_position_tolerance_, curr_pose.position.x, curr_pose.position.y, curr_pose.position.z,latest_ekf_pose.position.x, latest_ekf_pose.position.y, latest_ekf_pose.position.z);return;}// Construct output messagePoseWithCovarianceStamped pose_with_covariance_stamped;pose_with_covariance_stamped.header.stamp = sensor_to_tag.header.stamp;pose_with_covariance_stamped.header.frame_id = "map";pose_with_covariance_stamped.pose.pose = curr_pose;// ~5[m]: base_covariance// 5~[m]: scaling base_covariance by std::pow(distance/5, 3)//构造输出消息,并根据距离调整协方差const double distance = std::sqrt(distance_squared);const double scale = distance / 5;const double coeff = std::max(1.0, std::pow(scale, 3));for (int i = 0; i < 36; i++) {pose_with_covariance_stamped.pose.covariance[i] = coeff * base_covariance_[i];}pose_pub_->publish(pose_with_covariance_stamped);
}/// @brief 将 ArUco 标记转换为 tf 变换
/// @param marker ArUco 标记
/// @return 
tf2::Transform ArTagBasedLocalizer::aruco_marker_to_tf2(const aruco::Marker & marker)
{cv::Mat rot(3, 3, CV_64FC1);//存储标记的旋转矩阵cv::Mat r_vec64;marker.Rvec.convertTo(r_vec64, CV_64FC1);//将其转换为 CV_64FC1(即双精度浮点数)类型的 r_vec64cv::Rodrigues(r_vec64, rot);cv::Mat tran64;//存储标记的平移矩阵marker.Tvec.convertTo(tran64, CV_64FC1);tf2::Matrix3x3 tf_rot(rot.at<double>(0, 0), rot.at<double>(0, 1), rot.at<double>(0, 2), rot.at<double>(1, 0),rot.at<double>(1, 1), rot.at<double>(1, 2), rot.at<double>(2, 0), rot.at<double>(2, 1),rot.at<double>(2, 2));tf2::Vector3 tf_orig(tran64.at<double>(0, 0), tran64.at<double>(1, 0), tran64.at<double>(2, 0));return tf2::Transform(tf_rot, tf_orig);
}

1.2 landmark管理


landmark_manager是一个实用程序包,用于从地图中加载地标。

  1. 转换:地标的中心是四个顶点的中心
  2. 旋转:让顶点编号为1、2、3、4,按逆时针方向如下一节所示。方向定义为从1到2的向量和从2到3的向量的叉积。
    用户可以将地标定义为Lanelet2的4个顶点多边形。在这种情况下,可能定义一种排列,其中四个顶点不能被认为在同一平面上。在这种情况下,地标的方向很难计算。因此,如果将4个顶点视为构成一个四面体,并且其体积超过volume_threshold参数,则地标将不会发布tf_static

这个文件中包含了两个函数:parse_landmarks和convert_landmarks_to_marker_array_msg。这两个函数用于处理地图数据中特定类型和子类型的地标信息,并将其转换为自定义的Landmark结构体,同时将Landmark结构体的地标信息转换为可视化消息。

在parse_landmarks函数中,首先使用日志记录器输出地图数据的基本信息,然后将地图数据转换为lanelet_map_ptr。接着遍历lanelet_map_ptr中的多边形图层,提取类型为"pose_marker"且子类型为目标子类型的地标信息。对于每个符合条件的多边形,计算其体积、中心点、坐标轴、旋转矩阵和四元数,并将这些信息存储在Landmark结构体中,最终返回包含Landmark结构体的向量。

在convert_landmarks_to_marker_array_msg函数中,将Landmark结构体的地标信息转换为可视化消息。根据每个地标信息创建立方体标记和文本标记,并将它们添加到标记数组中,最终返回包含标记数组的可视化消息。

…详情请参照古月居

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/45801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS相对定位和绝对定位的区别

CSS相对定位和绝对定位的区别 区别1&#xff1a;相对的对象不同 相对定位是相对于自己绝对定位是相对于离自己最近的有定位的祖先 区别2:是否会脱离文档流 相对定位不会脱离文档流&#xff0c;不会影响其他元素的位置绝对定位会脱离文档流&#xff0c;会影响其他元素的布局 代…

玩转springboot之SpringBoot打成jar包的结构

SpringBoot打成jar包的结构 springboot通常会打成jar包&#xff0c;然后使用java -jar来进行执行&#xff0c;那么这个jar包里的结构是什么样的呢 其中 BOOT-INF 中包含的classes是我们程序中所有的代码编译后的class文件&#xff0c;lib是程序所引用的外部依赖 META-INF 这个…

AM243-Timer

目录 简介初始化代码测试API补充 简介 定时中断。 初始化 开启定时器&#xff0c;最多支持8个硬件定时器 定时周期1ms 增加一个GPIO输出口PRG0_PRU1_GPO15/M4 &#xff0c;我们会在定时中断中每隔1ms翻转该引脚&#xff0c;理想情况下应该在该引脚上测得2ms周期500Hz的矩形…

手把手教你打数学建模国赛!!!第一天软件准备篇

第一天软件准备 MATLAB MATLAB&#xff08;Matrix Laboratory&#xff09;是一种强大的数值计算和科学编程软件。它提供了丰富的数学函数和工具&#xff0c;用于数据分析、算法开发、信号处理、图像处理、控制系统设计、仿真等应用领域。 MATLAB具有直观的语法&#xff0c;使…

Postman接口模拟请求工具使用技巧

Postman是一款非常强大的接口模拟请求工具&#xff0c;可以帮助开发者快速测试、调试API接口。下面集合实际使用过程中的经验&#xff0c;分享大家一些基础使用技巧&#xff1a; 1. 安装与启动&#xff1a;首先在官网&#xff08;Download Postman | Get Started for Free&…

【Linux信号】阻塞信号、信号在内核中的表示、信号集操作函数、sigprocmask、sigpending

我们先来了解一下关于信号的一些常见概念&#xff1a; 实际执行 信号的处理动作 称为信号递达。 信号从产生到递达的之间的状态称为信号未决。 进程可以选择阻塞(Block)某个信号。 被阻塞的信号产生时是处于未决状态的&#xff0c;知道进程解除对该信号的阻塞&#xff0c;该…

零信任作为解决方案,Hvv还能打进去么?

零信任平台由“中心组件服务”三大部分构成&#xff0c;以平台形式充分融合软件定义边界&#xff08;SDP&#xff09;、身份与访问管理&#xff08;IAM&#xff09;、微隔离 &#xff08;MSG&#xff09;的技术方案优势&#xff0c;通过关键技术的创新&#xff0c;实现最佳可信…

手机数据恢复篇:如何从 Android 手机恢复消失的照片

丢失 Android 手机中的照片现在已成为您可能遇到的最糟糕的情况之一。随着手机在相机方面越来越好&#xff0c;即使是那些不热衷于拍照的人也成为了摄影师。 如今&#xff0c;人们可以随时随地拍摄照片&#xff0c;每一张照片都保存着回忆和数据&#xff0c;因此&#xff0c;丢…

变得越来越优秀的方法

反省后看到问题很正常&#xff0c;接纳-行动-改变-能量-帮助-成长变优秀&#xff1b;温和后需要【中庸智慧】灵活处世&#xff0c;不做老好人&#xff0c;须有原则有框架&#xff01; —— 只有深刻地反省&#xff0c;我们才能真正地认识自己&#xff0c;我们反省后会看到自己…

昇思25天学习打卡营第19天|应用实践之基于MobileNetv2的垃圾分类

基本介绍 今天的应用实践是垃圾分类代码开发&#xff0c;整体流程是读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。采用的是MobileNetv2模型&#xff0c;使用官方提供的数据集&#xff0c;数据集分为4大类&…

【MySQL】8.复合查询

复合查询 一.基本查询回顾(新增子查询)二.多表查询三.自连接四.子查询1.单列单行子查询2.单列多行子查询——三个关键字3.多列子查询4.在 from 子句中使用子查询 五.合并查询六.总结 一.基本查询回顾(新增子查询) //1.查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还…

Avalonia创建导航菜单

1. 简介 已开源&#xff0c;后续还会继续更新学习到的内容&#xff0c;欢迎Star&#xff0c;GitHub地址 开发Avalonia需要的一些资料&#xff0c;我已经分享到另一篇文章 示意图 涉及到内容&#xff1a; MVVM路由模板 开发&#xff1a; 开发工具&#xff1a;Rider&#x…

【linux】进程间通信(IPC)——匿名管道,命名管道与System V内核方案的共享内存,以及消息队列和信号量的原理概述

目录 ✈必备知识 进程间通信概述 &#x1f525;概述 &#x1f525;必要性 &#x1f525;原理 管道概述 &#x1f525;管道的本质 &#x1f525;管道的相关特性 &#x1f525;管道的同步与互斥机制 匿名管道 &#x1f525;系统调用接口介绍 &#x1f525;内核原理 …

C++ QT开发 学习笔记(1)

C QT开发 学习笔记(1) 考试系统 创建项目 新建Qt桌面应用程序&#xff0c;项目名&#xff1a;ExamSys。 类信息&#xff1a;类名LoginDialog继承自QDialog &#xff08;1&#xff09; ExamSys.pro 工程文件&#xff0c;包含当前工程的相关信息。 QDialog 是 Qt 框架中用…

二、计划任务

1.什么是计划任务 对于一些特定的任务&#xff0c;可以设定任务&#xff0c;让服务在规定时间去执行 2.windows中的计划任务 打开控制面板》管理工具》任务计划程序》创建基本任务 3.linux中的计划任务 周期性的计划crontab crontab -l :显示当前的计划惹怒我 -e&#…

大健康产业运营模式|大健康行业商业模式|健康管理盈利模式

大家好&#xff01;今天我们来聊聊如何在大健康行业中选择一个适合自己的商业模式&#xff0c;从保健、医疗、健身、美容、养老等方面快速发展并取得成功。 首先&#xff0c;大健康行业涵盖了很多领域&#xff0c;但最核心的是保健和医疗&#xff0c;这两者是保障大家健康的基础…

通过maven基于springboot项目构建脚手架archetype

1、引入脚手架构建的插件依赖 <!--构建脚手架archetype--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-archetype-plugin</artifactId><version>3.2.1</version></plugin><plugin><…

【Visual Studio】Visual Studio使用技巧及报错解决合集

目录 目录 一.概述 二.Visual Studio报错问题及解决方法 三.Visual Studio操作过程中遇到的问题及解决方法 四.Visual Studio编译优化选项 五.Visual Studio快捷键 一.概述 持续更新Visual Studio报错及解决方法&#xff0c;包括Visual Studio报错问题及解决方法、Visua…

土壤品质检测仪:守护大地之母的科技卫士

土壤&#xff0c;作为地球生命之源&#xff0c;承载着万物的生长与繁衍。然而&#xff0c;随着现代农业的快速发展&#xff0c;土壤品质问题日益凸显&#xff0c;对农作物的生长和人们的健康构成了潜在威胁。 随着环保意识的增强和农业可持续发展的需求&#xff0c;土壤品质检测…

实验8 视图创建与管理实验

一、实验目的 理解视图的概念。掌握创建、更改、删除视图的方法。掌握使用视图来访问数据的方法。 二、实验内容 在job数据库中&#xff0c;有聘任人员信息表&#xff1a;Work_lnfo表&#xff0c;其表结构如下表所示&#xff1a; 其中表中练习数据如下&#xff1a; 1.‘张明…