Jetson Orin——RT-DETR目标实时检测
本文旨在将自己微调的模型部署到jetson-Orin中并利用官方的isaac-ROS库isaac-ros-rtdetr库运行该模型做实时检测一、创建环境1.训练环境a.创建conda基础环境conda create -n rtdetr python3.10 -yconda activate rtdetrb.安装pytorchpip install torch torchvision --index-url https://download.pytorch.org/whl/cu118c.验证是否安装成功python -c import torch; print(torch.__version__); print(torch.cuda.is_available())2.源代码a.克隆仓库git clone https://github.com/lyuwenyu/RT-DETR.gitb.进入对应目录cd RT-DETRcd rtdetrv2_pytorchc.安装仓库依赖pip install -r requirements.txt二、准备数据集准备COCO格式数据集目录结构官方如下或者很多标注工具如roboflow输出的coco格式如下注要确保json文件中类别必须连续从0开始roboflow输出的数据集会多一个奇怪的父类如下所示正确如下三、训练模型1.写数据配置文件configs/dataset/your_detection.ymltask: detection evaluator: type: CocoEvaluator iou_types: [bbox, ] num_classes: 3 remap_mscoco_category: False train_dataloader: type: DataLoader dataset: type: CocoDetection img_folder: ./dataset/bolt_washer_spacer/train/ ann_file: ./dataset/bolt_washer_spacer/train/_annotations.coco.json return_masks: False transforms: type: Compose ops: ~ shuffle: True num_workers: 2 drop_last: True collate_fn: type: BatchImageCollateFunction val_dataloader: type: DataLoader dataset: type: CocoDetection img_folder: ./dataset/bolt_washer_spacer/valid/ ann_file: ./dataset/bolt_washer_spacer/valid/_annotations.coco.json return_masks: False transforms: type: Compose ops: ~ shuffle: False num_workers: 2 drop_last: False collate_fn: type: BatchImageCollateFunction2.修改训练配置文件configs/rtdetrv2/your_train.yml__include__: [ ../dataset/your_detection.yml, ../runtime.yml, ./include/dataloader.yml, ./include/optimizer.yml, ./include/rtdetrv2_r50vd.yml, ] output_dir: ./output/your_file eval_spatial_size: [640, 640] PResNet: depth: 18 freeze_at: -1 freeze_norm: False pretrained: True HybridEncoder: in_channels: [128, 256, 512] hidden_dim: 256 expansion: 0.5 RTDETRTransformerv2: num_layers: 3 use_amp: True use_ema: True epoches: 72 clip_max_norm: 0.1 optimizer: type: AdamW params: - params: ^(?.*(?:norm|bn)).*$ weight_decay: 0. lr: 0.0001 betas: [0.9, 0.999] weight_decay: 0.0001 lr_scheduler: type: MultiStepLR milestones: [60] gamma: 0.1 lr_warmup_scheduler: type: LinearWarmup warmup_duration: 500 train_dataloader: dataset: transforms: ops: - {type: RandomPhotometricDistort, p: 0.5} - {type: RandomZoomOut, fill: 0} - {type: RandomIoUCrop, p: 0.8} - {type: SanitizeBoundingBoxes, min_size: 1} - {type: RandomHorizontalFlip} - {type: Resize, size: [640, 640]} - {type: SanitizeBoundingBoxes, min_size: 1} - {type: ConvertPILImage, dtype: float32, scale: True} - {type: ConvertBoxes, fmt: cxcywh, normalize: True} policy: name: stop_epoch epoch: 60 ops: [RandomPhotometricDistort, RandomZoomOut, RandomIoUCrop] collate_fn: scales: ~ total_batch_size: 1 num_workers: 2 val_dataloader: dataset: transforms: ops: - {type: Resize, size: [640, 640]} - {type: ConvertPILImage, dtype: float32, scale: True} total_batch_size: 1 num_workers: 23.训练命令python tools/train.py -c configs/rtdetrv2/your_train.yml -t path/to/pretrained.pth --use-amp4.测试集评估a.新建test数据配置__include__: [ ./your_train.yml, ] output_dir: ./output/your_test val_dataloader: dataset: img_folder: ./dataset/your_dataset/test/ ann_file: ./dataset/your_dataset/test/_annotations.coco.jsonb.测试命令python tools/train.py -c configs/rtdetrv2/your_test.yml -r output/best.pth --test-only -d cuda:0c.可视化新建批量可视化脚本Visualize RT-DETRv2 predictions on test images. import argparse import json import os import sys sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ..)) import torch import torch.nn as nn import torchvision.transforms as T from PIL import Image, ImageDraw, ImageFont from src.core import YAMLConfig CLASS_NAMES { 0: screw, 1: washer, 2: spacer, } COLORS { 0: #FF3838, 1: #FF9D97, 2: #FF701F, } def load_gt_boxes(ann_file, image_stem): with open(ann_file, encodingutf-8) as f: data json.load(f) image_id None for image in data[images]: if os.path.splitext(image[file_name])[0] image_stem: image_id image[id] break if image_id is None: return [] boxes [] for ann in data[annotations]: if ann[image_id] ! image_id: continue x, y, w, h ann[bbox] boxes.append({ label: ann[category_id], box: [x, y, x w, y h], }) return boxes def draw_boxes(image, boxes, modepred, score_mapNone): draw ImageDraw.Draw(image) for item in boxes: if mode pred: label int(item[label].item()) score float(item[score].item()) box [float(v) for v in item[box]] text f{CLASS_NAMES.get(label, label)} {score:.2f} color COLORS.get(label, #00FF00) else: label int(item[label]) box [float(v) for v in item[box]] text fGT {CLASS_NAMES.get(label, label)} color #00FF00 draw.rectangle(box, outlinecolor, width3) draw.text((box[0], max(0, box[1] - 12)), texttext, fillcolor) return image def build_model(cfg, checkpoint_path, device): checkpoint torch.load(checkpoint_path, map_locationcpu) if ema in checkpoint: state checkpoint[ema][module] else: state checkpoint[model] cfg.model.load_state_dict(state) class DeployModel(nn.Module): def __init__(self): super().__init__() self.model cfg.model.deploy() self.postprocessor cfg.postprocessor.deploy() def forward(self, images, orig_target_sizes): outputs self.model(images) return self.postprocessor(outputs, orig_target_sizes) return DeployModel().to(device).eval() def main(args): cfg YAMLConfig(args.config, resumeargs.resume) device torch.device(args.device if torch.cuda.is_available() else cpu) model build_model(cfg, args.resume, device) img_size cfg.yaml_cfg.get(eval_spatial_size, [640, 640]) if isinstance(img_size, list): img_size img_size[0] transforms T.Compose([ T.Resize((img_size, img_size)), T.ToTensor(), ]) os.makedirs(args.output_dir, exist_okTrue) image_files [ f for f in os.listdir(args.image_dir) if f.lower().endswith((.jpg, .jpeg, .png)) ] image_files.sort() if args.max_images 0: image_files image_files[:args.max_images] ann_file args.ann_file threshold args.threshold for image_name in image_files: image_path os.path.join(args.image_dir, image_name) im_pil Image.open(image_path).convert(RGB) w, h im_pil.size orig_size torch.tensor([[w, h]], devicedevice) im_data transforms(im_pil)[None].to(device) with torch.no_grad(): labels, boxes, scores model(im_data, orig_size) pred_items [] scr scores[0] for label, box, score in zip(labels[0], boxes[0], scr): if score.item() threshold: continue pred_items.append({label: label, box: box, score: score}) pred_image im_pil.copy() draw_boxes(pred_image, pred_items, modepred) gt_items load_gt_boxes(ann_file, os.path.splitext(image_name)[0]) gt_image im_pil.copy() draw_boxes(gt_image, gt_items, modegt) merged Image.new(RGB, (w * 2, h)) merged.paste(gt_image, (0, 0)) merged.paste(pred_image, (w, 0)) save_path os.path.join(args.output_dir, fvis_{image_name}) merged.save(save_path) print(fsaved: {save_path}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(-c, --config, typestr, requiredTrue) parser.add_argument(-r, --resume, typestr, requiredTrue) parser.add_argument(--image-dir, typestr, requiredTrue) parser.add_argument(--ann-file, typestr, requiredTrue) parser.add_argument(--output-dir, typestr, requiredTrue) parser.add_argument(--device, typestr, defaultcuda:0) parser.add_argument(--threshold, typefloat, default0.5) parser.add_argument(--max-images, typeint, default0, help0 means all images) args parser.parse_args() main(args)执行脚本命令python tools/visualize_test.py ^-c configs/rtdetrv2/your_test.yml ^-r output/best.pth ^--image-dir dataset/your_dataset/test ^--ann-file dataset/your_dataset/test/_annotations.coco.json ^--output-dir output/your_test/vis ^--device cuda:0 --threshold 0.55.导出onnx模型命令python tools/export_onnx.py ^-c configs/rtdetrv2/your_train.yml ^-r output/your_file/best.pth ^-o output/your_file/best.onnx ^-s 640 --check四、部署模型1.输出onnx-sim模型pip install onnxsimonnxsim /path/to/your_model.onnx /path/to/your_model_simplified.onnx注rt-detr模型需要做simplify操作以兼容tensorRT架构不进行onnx-sim输出engine模型会报错2.输出engine模型在isaac_ros容器中终端运行trtexec \--onnxbest_sim.onnx \--saveEnginert_detr.engine \--fp16 \--memPoolSizeworkspace:4096 \--verbose五、封装进节点1.下载rt-detr官方节点库源码isaac_ros_rtdetr — isaac_ros_docs documentation2.更改源码由于源码是针对工业相机我用的是logic c270并没有时间戳以及输出的图像分辨率也不是正方形所以需要修改几处地方新增launch文件需要提前准备好v4l2节点可以看我之前apriltag的文章# SPDX-License-Identifier: Apache-2.0 import os from ament_index_python.packages import get_package_share_directory import launch from launch.actions import DeclareLaunchArgument from launch.conditions import IfCondition from launch.substitutions import LaunchConfiguration from launch_ros.actions import ComposableNodeContainer, Node from launch_ros.descriptions import ComposableNode MODEL_INPUT_SIZE 640 MODEL_NUM_CHANNELS 3 IMAGE_WIDTH 640 IMAGE_HEIGHT 480 WORKSPACE_ROOT os.environ.get(ISAAC_ROS_WS, /workspaces/isaac_ros-dev) DEFAULT_ENGINE_PATH os.path.join( get_package_share_directory(isaac_ros_rtdetr), models, rt_detr.engine) DEFAULT_CAMERA_INFO_URL ( file:// os.path.join(WORKSPACE_ROOT, src/v4l2_camera/config/camera_info.yaml) ) def generate_launch_description(): launch_args [ DeclareLaunchArgument( video_device, default_value/dev/video0, descriptionV4L2 device path for the Logitech C270), DeclareLaunchArgument( engine_file_path, default_valueDEFAULT_ENGINE_PATH, descriptionAbsolute path to the RT-DETR TensorRT engine file), DeclareLaunchArgument( camera_info_url, default_valueDEFAULT_CAMERA_INFO_URL, descriptionCamera calibration URL for camera_info_manager), DeclareLaunchArgument( confidence_threshold, default_value0.7, descriptionMinimum detection confidence score), DeclareLaunchArgument( force_engine_update, default_valueFalse, descriptionWhether TensorRT should rebuild the engine file), DeclareLaunchArgument( launch_visualizer, default_valueTrue, descriptionLaunch the RT-DETR detection visualizer node), ] engine_file_path LaunchConfiguration(engine_file_path) confidence_threshold LaunchConfiguration(confidence_threshold) force_engine_update LaunchConfiguration(force_engine_update) launch_visualizer LaunchConfiguration(launch_visualizer) v4l2_camera_node Node( packagev4l2_camera, executablev4l2_camera_node, namev4l2_camera, parameters[{ video_device: LaunchConfiguration(video_device), image_size: [IMAGE_WIDTH, IMAGE_HEIGHT], output_encoding: rgb8, camera_info_url: LaunchConfiguration(camera_info_url), }], outputscreen, ) resize_node ComposableNode( nameresize_node, packageisaac_ros_image_proc, pluginnvidia::isaac_ros::image_proc::ResizeNode, parameters[{ input_width: IMAGE_WIDTH, input_height: IMAGE_HEIGHT, output_width: MODEL_INPUT_SIZE, output_height: MODEL_INPUT_SIZE, # RT-DETRv2 training uses direct resize to 640x640 (stretch). keep_aspect_ratio: False, encoding_desired: rgb8, disable_padding: True, }], remappings[ (image, /image_raw), (camera_info, /camera_info), ], ) image_to_tensor_node ComposableNode( nameimage_to_tensor_node, packageisaac_ros_tensor_proc, pluginnvidia::isaac_ros::dnn_inference::ImageToTensorNode, parameters[{ scale: True, tensor_name: image, }], remappings[ (image, resize/image), (tensor, image_tensor), ], ) normalize_node ComposableNode( namenormalize_node, packageisaac_ros_tensor_proc, pluginnvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode, parameters[{ mean: [0.485, 0.456, 0.406], stddev: [0.229, 0.224, 0.225], input_tensor_name: image, output_tensor_name: image, }], remappings[ (tensor, image_tensor), (normalized_tensor, normalized_tensor), ], ) interleave_to_planar_node ComposableNode( nameinterleaved_to_planar_node, packageisaac_ros_tensor_proc, pluginnvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode, parameters[{ input_tensor_shape: [MODEL_INPUT_SIZE, MODEL_INPUT_SIZE, MODEL_NUM_CHANNELS], }], remappings[(interleaved_tensor, normalized_tensor)], ) reshape_node ComposableNode( namereshape_node, packageisaac_ros_tensor_proc, pluginnvidia::isaac_ros::dnn_inference::ReshapeNode, parameters[{ output_tensor_name: input_tensor, input_tensor_shape: [MODEL_NUM_CHANNELS, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE], output_tensor_shape: [1, MODEL_NUM_CHANNELS, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE], }], remappings[(tensor, planar_tensor)], ) rtdetr_preprocessor_node ComposableNode( namertdetr_preprocessor, packageisaac_ros_rtdetr, pluginnvidia::isaac_ros::rtdetr::RtDetrPreprocessorNode, parameters[{ image_width: IMAGE_WIDTH, image_height: IMAGE_HEIGHT, }], remappings[(encoded_tensor, reshaped_tensor)], ) tensor_rt_node ComposableNode( nametensor_rt, packageisaac_ros_tensor_rt, pluginnvidia::isaac_ros::dnn_inference::TensorRTNode, parameters[{ model_file_path: , engine_file_path: engine_file_path, output_binding_names: [labels, boxes, scores], output_tensor_names: [labels, boxes, scores], input_tensor_names: [images, orig_target_sizes], input_binding_names: [images, orig_target_sizes], verbose: False, force_engine_update: force_engine_update, }], ) rtdetr_decoder_node ComposableNode( namertdetr_decoder, packageisaac_ros_rtdetr, pluginnvidia::isaac_ros::rtdetr::RtDetrDecoderNode, parameters[{ confidence_threshold: confidence_threshold, }], ) rtdetr_container ComposableNodeContainer( namertdetr_container, namespace, packagerclcpp_components, executablecomponent_container_mt, composable_node_descriptions[ resize_node, image_to_tensor_node, normalize_node, interleave_to_planar_node, reshape_node, rtdetr_preprocessor_node, tensor_rt_node, rtdetr_decoder_node, ], outputscreen, ) visualizer_node Node( packageisaac_ros_rtdetr, executablertdetr_c270_visualizer.py, namertdetr_c270_visualizer, parameters[{ image_topic: /image_raw, detections_topic: /detections_output, min_confidence: confidence_threshold, max_detections: 20, }], conditionIfCondition(launch_visualizer), ) return launch.LaunchDescription( launch_args [v4l2_camera_node, rtdetr_container, visualizer_node])新增可视化脚本源码可视化脚本需要相机输出时间戳#!/usr/bin/env python3 import cv2 import cv_bridge import rclpy from rclpy.node import Node from sensor_msgs.msg import Image from vision_msgs.msg import Detection2DArray class RtDetrC270Visualizer(Node): def __init__(self): super().__init__(rtdetr_c270_visualizer) self.declare_parameter(image_topic, /image_raw) self.declare_parameter(detections_topic, /detections_output) self.declare_parameter(min_confidence, 0.7) self.declare_parameter(max_detections, 20) self._bridge cv_bridge.CvBridge() self._latest_detections None self._color (0, 255, 0) self._bbox_thickness 2 self._font cv2.FONT_HERSHEY_SIMPLEX self._font_scale 0.5 image_topic self.get_parameter(image_topic).get_parameter_value().string_value detections_topic ( self.get_parameter(detections_topic).get_parameter_value().string_value ) self._min_confidence ( self.get_parameter(min_confidence).get_parameter_value().double_value ) self._max_detections ( self.get_parameter(max_detections).get_parameter_value().integer_value ) self._processed_image_pub self.create_publisher( Image, rtdetr_processed_image, 10) self.create_subscription( Detection2DArray, detections_topic, self._detections_callback, 10) self.create_subscription( Image, image_topic, self._image_callback, 10) self.get_logger().info( fVisualizing {image_topic}, min_confidence{self._min_confidence}) def _detections_callback(self, msg): self._latest_detections msg def _filter_detections(self, detections): scored [] for detection in detections: if not detection.results: continue score detection.results[0].hypothesis.score if score self._min_confidence: continue scored.append((score, detection)) scored.sort(keylambda item: item[0], reverseTrue) return [detection for _, detection in scored[:self._max_detections]] def _image_callback(self, img_msg): try: cv2_img self._bridge.imgmsg_to_cv2(img_msg, desired_encodingrgb8) except cv_bridge.CvBridgeError as exc: self.get_logger().warn(fFailed to convert image: {exc}) return if self._latest_detections is not None: filtered self._filter_detections(self._latest_detections.detections) if filtered: self.get_logger().info( fDrawing {len(filtered)} detections, throttle_duration_sec2.0) for detection in filtered: center_x detection.bbox.center.position.x center_y detection.bbox.center.position.y width detection.bbox.size_x height detection.bbox.size_y try: min_pt ( round(center_x - (width / 2.0)), round(center_y - (height / 2.0)), ) max_pt ( round(center_x (width / 2.0)), round(center_y (height / 2.0)), ) cv2.rectangle( cv2_img, min_pt, max_pt, self._color, self._bbox_thickness) label detection.results[0].hypothesis.class_id score detection.results[0].hypothesis.score cv2.putText( cv2_img, f{label} {score:.2f}, min_pt, self._font, self._font_scale, self._color, 2) except ValueError: pass processed_img self._bridge.cv2_to_imgmsg(cv2_img, encodingrgb8) processed_img.header img_msg.header self._processed_image_pub.publish(processed_img) def main(): rclpy.init() node RtDetrC270Visualizer() rclpy.spin(node) rclpy.shutdown() if __name__ __main__: main()增加依赖:cmakelist.txt# SPDX-FileCopyrightText: NVIDIA CORPORATION AFFILIATES # Copyright (c) 2024 NVIDIA CORPORATION AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the License); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an AS IS BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.22.1) project(isaac_ros_rtdetr LANGUAGES C CXX) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang) add_compile_options(-Wall -Wextra -Wpedantic) endif() find_package(ament_cmake_auto REQUIRED) ament_auto_find_build_dependencies() # RtDetrPreprocessorNode ament_auto_add_library(rtdetr_preprocessor_node SHARED src/rtdetr_preprocessor_node.cpp) rclcpp_components_register_nodes(rtdetr_preprocessor_node nvidia::isaac_ros::rtdetr::RtDetrPreprocessorNode) set(node_plugins ${node_plugins}nvidia::isaac_ros::rtdetr::RtDetrPreprocessorNode;$TARGET_FILE:rtdetr_preprocessor_node\n) # RtDetrDecoderNode ament_auto_add_library(rtdetr_decoder_node SHARED src/rtdetr_decoder_node.cpp) rclcpp_components_register_nodes(rtdetr_decoder_node nvidia::isaac_ros::rtdetr::RtDetrDecoderNode) set(node_plugins ${node_plugins}nvidia::isaac_ros::rtdetr::RtDetrDecoderNode;$TARGET_FILE:rtdetr_decoder_node\n) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() # The FindPythonInterp and FindPythonLibs modules are removed if(POLICY CMP0148) cmake_policy(SET CMP0148 OLD) endif() find_package(launch_testing_ament_cmake REQUIRED) add_launch_test(test/isaac_ros_rtdetr_pol_test.py TIMEOUT 600) endif() # Visualizer python scripts ament_python_install_package(${PROJECT_NAME}) install(PROGRAMS scripts/isaac_ros_rtdetr_visualizer.py scripts/rtdetr_c270_visualizer.py scripts/rtdetr_sam_visualizer.py DESTINATION lib/${PROJECT_NAME} ) install(DIRECTORY models DESTINATION share/${PROJECT_NAME} ) # Embed versioning information into installed files ament_index_get_resource(ISAAC_ROS_COMMON_CMAKE_PATH isaac_ros_common_cmake_path isaac_ros_common) include(${ISAAC_ROS_COMMON_CMAKE_PATH}/isaac_ros_common-version-info.cmake) generate_version_info(${PROJECT_NAME}) ament_auto_package(INSTALL_TO_SHARE launch)package.xml?xml version1.0? !-- SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION AFFILIATES. All rights reserved. Licensed under the Apache License, Version 2.0 (the License); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. SPDX-License-Identifier: Apache-2.0 -- ?xml-model hrefhttp://download.ros.org/schema/package_format3.xsd schematypenshttp://www.w3.org/2001/XMLSchema? package format3 nameisaac_ros_rtdetr/name version3.2.5/version descriptionRT-DETR model processing/description maintainer emailisaac-ros-maintainersnvidia.comIsaac ROS Maintainers/maintainer licenseApache-2.0/license url typewebsitehttps://developer.nvidia.com/isaac-ros//url authorJaiveer Singh/author buildtool_dependament_cmake_auto/buildtool_depend dependrclcpp/depend dependrclcpp_components/depend dependvision_msgs/depend dependisaac_ros_nitros/depend dependisaac_ros_nitros_tensor_list_type/depend dependisaac_ros_managed_nitros/depend dependisaac_ros_tensor_list_interfaces/depend build_dependisaac_ros_common/build_depend exec_dependisaac_ros_dnn_image_encoder/exec_depend exec_dependisaac_ros_image_proc/exec_depend exec_dependisaac_ros_tensor_proc/exec_depend exec_dependisaac_ros_tensor_rt/exec_depend exec_dependisaac_ros_segment_anything/exec_depend exec_dependisaac_ros_triton/exec_depend exec_dependv4l2_camera/exec_depend exec_dependrclpy/exec_depend exec_dependcv_bridge/exec_depend exec_dependsensor_msgs/exec_depend exec_dependgxf_isaac_optimizer/exec_depend test_dependament_lint_auto/test_depend test_dependament_lint_common/test_depend test_dependisaac_ros_test/test_depend export build_typeament_cmake/build_type /export /package编译源码colcon build --packages-select isaac_ros_rtdetr --symlink-installsource install/setup.bash运行启动文件ros2 launch isaac_ros_rtdetr rtdetr_c270.launch.py可以通过rviz2或者rqt订阅节点话题六、检测效果

相关新闻

Python生成器与迭代器深度解析:从原理到高性能实战

Python生成器与迭代器深度解析:从原理到高性能实战

引言 在Python开发中,你或许经常用for循环遍历列表、字典甚至文件,但你是否想过,这些对象为什么能被for循环依次取出元素?背后依赖的就是迭代器协议。而yield关键字塑造的生成器,则让迭代器的定义变得异常简洁&#xf…

2026/6/27 12:40:24阅读更多 →
耐摔的UV镜多系列深度解析

耐摔的UV镜多系列深度解析

不少摄影爱好者外出采风都会遇到相同困扰:徒步、爬山、海边拍摄时相机容易磕碰,普通UV镜一撞就碎,不仅失去防护作用,碎片还可能划伤昂贵镜头;也有新手疑惑,市面上UV镜品牌众多,哪些镜片抗冲击、…

2026/6/27 12:40:24阅读更多 →
BetterNCM插件管家:让网易云音乐变身智能播放器的神奇工具

BetterNCM插件管家:让网易云音乐变身智能播放器的神奇工具

BetterNCM插件管家:让网易云音乐变身智能播放器的神奇工具 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 还在为网易云音乐功能单一而烦恼吗?BetterNCM安装器帮…

2026/6/27 12:40:24阅读更多 →
模拟电路实验板设计与教学实践解析

模拟电路实验板设计与教学实践解析

1. 模拟电路实验板的设计初衷与教学价值在电子工程专业的实验教学中,模拟电路实验板作为基础训练平台,其重要性不言而喻。济南大学电子实验室自主研发的这套实验装置,正是针对模电教学中"理论抽象、实践脱节"的痛点而设计的。相比市…

2026/6/27 14:15:43阅读更多 →
高精度电压电流基准源设计与工程实践

高精度电压电流基准源设计与工程实践

1. 项目背景与核心价值在电子测量领域,电压电流基准源就像一把标尺的刻度线,决定了整个测量系统的准确度上限。我十年前第一次接触六位半万用表校准工作时,就深刻体会到基准源质量对测量结果的决定性影响——当时由于使用了劣质基准&#xff…

2026/6/27 14:15:43阅读更多 →
18650电芯标识解析与品牌编码规则详解

18650电芯标识解析与品牌编码规则详解

1. 18650电芯标识解析基础18650电芯作为最常见的锂离子电池规格之一,其钢印或喷码承载着关键的身份信息。这些看似简单的字母数字组合,实际上是厂家预设的"身份密码",包含了容量、型号、生产批次等核心参数。不同品牌采用不同的编码…

2026/6/27 14:15:43阅读更多 →
还在用FindBugs?这4个新一代静态分析插件已让92%的Java团队淘汰旧工具

还在用FindBugs?这4个新一代静态分析插件已让92%的Java团队淘汰旧工具

更多请点击: https://intelliparadigm.com 第一章:还在用FindBugs?这4个新一代静态分析插件已让92%的Java团队淘汰旧工具 FindBugs 自 2016 年正式归档后,其技术栈已无法适配 Java 8 的新字节码特性与模块化系统,更缺…

2026/6/27 14:15:43阅读更多 →
电子元件基础:电源、电阻、电容原理与应用

电子元件基础:电源、电阻、电容原理与应用

1. 电子元件基础入门:电源、电阻、电容解析刚接触电子技术的朋友经常会遇到这样的困惑:电路板上那些五颜六色的小元件到底都是干什么用的?为什么有些元件发热严重,有些却始终保持冰凉?今天我们就来聊聊电子电路中最基础…

2026/6/27 14:15:43阅读更多 →
告别低效Prompt!IDEA中Copilot的12个精准指令模板(含Spring Boot、K8s YAML、JUnit5生成场景)

告别低效Prompt!IDEA中Copilot的12个精准指令模板(含Spring Boot、K8s YAML、JUnit5生成场景)

更多请点击: https://codechina.net 第一章:告别低效Prompt:Copilot在IDEA中的认知跃迁 传统 Prompt 工程依赖开发者手动构造冗长、模糊甚至语义冲突的指令,例如“写一个 Java 方法,处理空字符串并返回默认值”&#…

2026/6/27 14:10:42阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/27 11:20:40阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/27 5:46:02阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/27 11:20:39阅读更多 →
10分钟AI语音克隆与实时变声:Retrieval-based-Voice-Conversion-WebUI完整指南

10分钟AI语音克隆与实时变声:Retrieval-based-Voice-Conversion-WebUI完整指南

10分钟AI语音克隆与实时变声&#xff1a;Retrieval-based-Voice-Conversion-WebUI完整指南 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrie…

2026/6/27 0:04:03阅读更多 →
Layerdivider:3分钟AI智能分层,彻底告别手动抠图时代

Layerdivider:3分钟AI智能分层,彻底告别手动抠图时代

Layerdivider&#xff1a;3分钟AI智能分层&#xff0c;彻底告别手动抠图时代 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 还在为复杂的图像分层工作烦…

2026/6/27 0:04:03阅读更多 →
Tomcat中X-Frame-Options配置实战:防御点击劫持的四种方法与最佳实践

Tomcat中X-Frame-Options配置实战:防御点击劫持的四种方法与最佳实践

1. 项目概述&#xff1a;为什么X-Frame-Options是Web安全的“防盗门”&#xff1f;最近在排查一个老项目的安全审计报告时&#xff0c;又被提到了“点击劫持”风险&#xff0c;矛头直指缺失的X-Frame-Options响应头。这已经不是第一次了&#xff0c;很多开发团队&#xff0c;尤…

2026/6/27 0:04:03阅读更多 →