ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

嵌入式ROS双系统通信实战:上位机+驱动协同设计与CMake构建

嵌入式ROS双系统通信实战:上位机+驱动协同设计与CMake构建 简介本资源是面向自动驾驶、机器人及ROS开发者的万集716型激光雷达完整驱动与上位机集成方案聚焦硬件通信、数据解析与ROS系统对接等核心问题适用于具备嵌入式基础和ROS开发经验的中高级工程师与高校研究者。压缩包共205个文件涵盖65个CMake构建脚本用于ROS驱动编译配置、51个Make相关文件支撑跨平台构建流程、9个Python脚本含数据解析与简易可视化工具、6个可执行程序上位机调试与参数配置工具以及关键头文件如wj_716_lidar_protocol.h和ROS启动文件launch整体达109.66MB。已有305人学习下载资源结构清晰包含协议解析说明、驱动源码、编译部署指南及典型运行环境配置如setup.bash、catkin_workspace支持可直接用于雷达数据采集、话题发布、SLAM前端接入及故障诊断显著降低万集雷达在ROS 1环境下的集成门槛。1. 项目概述一个被压缩包名字掩盖的嵌入式系统通信枢纽“716上位机ROS驱动-H(4).zip”——这串字符乍看像一串随机生成的文件名实则是嵌入式开发一线工程师日常工作中最典型、也最容易被忽视的“信息黑箱”。它不是某个商业软件的安装包也不是教学演示的玩具工程而是一个面向特定硬件平台编号716的双向通信系统落地产物前端是运行在Windows/Linux上的上位机软件负责人机交互、数据可视化与指令下发后端是深度集成进ROS生态的驱动节点承担底层硬件抽象、实时数据采集与运动控制闭环。中间那根看不见的“线”正是CMake构建系统——它不显山露水却决定了整个工程能否在Ubuntu 20.04/22.04、ROS Noetic/Humble等不同环境中稳定编译、链接、部署。我拆过不下三十个类似命名的压缩包绝大多数都来自高校实验室、初创机器人公司或工业自动化集成商。它们往往没有README没有Wiki甚至没有版本号但里面藏着真实产线调试时反复打磨的串口协议解析逻辑、ROS话题命名规范、电机PID参数整定记录以及那些只在凌晨三点才复现的USB热插拔崩溃日志。关键词里反复出现的“鱼香ROS”“小鱼一键安装”“CMake降级到3.16.3”恰恰暴露了这个项目的现实土壤它不是在理想化的Docker容器里跑通就行而必须在一台装着Ubuntu 22.04、ROS Humble、同时还要兼容旧版传感器固件的工控机上扛住连续72小时的数据回传压力。CP2102、CH340、FT232这些串口芯片驱动问题从来不是“装个驱动就完事”的小白操作而是涉及udev规则、权限组配置、内核模块加载顺序的系统级博弈。而“H(4)”这个后缀我见过太多次——它通常代表硬件迭代的第四版意味着前三个版本可能踩过UART丢帧、CAN总线仲裁失败、SPI时序错位等所有经典坑。如果你正面对这样一个压缩包手头只有几行模糊的调试日志和一份过期的硬件手册那么这篇内容就是为你写的。它不教你从零写ROS节点也不带你重装十遍系统而是直接切入这个“716”项目的真实脉络如何从压缩包结构反推通信架构怎样用CMakeLists.txt定位关键依赖为什么ROS驱动层必须绕开ros_serial而自建串口管理器以及——当你的上位机在Win10上能连设备、在Ubuntu上却报“Permission denied”时该去查哪个systemd服务、改哪一行group配置。这不是理论文档这是我在三个不同客户现场用螺丝刀撬开工控机箱盖、用示波器抓UART波形、在终端里敲了上千行命令后整理出的生存指南。2. 项目整体设计与思路拆解为什么必须“上位机ROS双轨并行”2.1 硬件平台“716”的典型特征与通信瓶颈所谓“716”在行业内部通常指代一类基于ARM Cortex-M4/M7主控的定制化运动控制板常见于AGV底盘控制器、协作机械臂末端执行器或智能仓储分拣单元。其核心特征并非性能参数而是通信接口的混合性与实时性矛盾主通信通道1路USB转串口CP2102或CH340芯片用于传输传感器原始数据IMU、编码器、力矩反馈和接收上位机下发的运动指令如MOVE_TO x120,y85,speed300辅助通道1路CAN总线对接电机驱动器TB6612或更高端的FOC控制器1路SPI连接姿态传感器D435i的IMU子模块致命约束主控MCU Flash空间≤512KBRAM≤192KB无法运行Linux或RTOS所有协议栈必须精简到极致。这就决定了整个系统不可能采用“ROS on MCU”的激进方案。我们见过太多团队试图在STM32上跑micro-ROS结果在PID控制环中因内存碎片导致周期抖动超过5ms最终放弃。因此“716”项目的顶层设计本质是分层卸载把计算密集、状态管理复杂、需要GUI交互的部分交给上位机Windows/Linux把实时性要求高1ms、与硬件强耦合寄存器配置、中断处理的部分留在MCU固件而ROS驱动节点则扮演“翻译官缓冲区状态同步器”的三重角色。提示当你打开压缩包发现src/目录下同时存在qt_gui/含.pro文件和ros_driver/含package.xml两个子目录这就是典型的分层卸载证据。不要试图合并它们——那是用稳定性换代码行数的自杀行为。2.2 上位机与ROS驱动的职责边界谁该做什么谁绝不能做很多初学者会陷入一个误区认为“上位机”就是个画界面的工具“ROS驱动”就是个收发消息的管道。但在“716”这类项目中二者有严格且不可逾越的职责红线模块必须承担的任务绝对禁止的操作典型后果上位机软件实时波形绘制100Hz刷新率、多轴运动轨迹规划B样条插值、用户权限管理操作员/工程师分级、本地数据缓存SQLite存储历史PID参数直接操作/dev/ttyUSB0设备节点、解析原始CAN帧、调用ros::spin()循环GUI卡死、USB设备被独占导致ROS节点失联、权限冲突引发segmentation faultROS驱动节点建立与MCU的可靠串口连接带超时重试与心跳机制、将原始二进制数据按协议解析为sensor_msgs/Imu、geometry_msgs/Twist等标准消息、发布tf变换base_link→wheel_left、订阅/cmd_vel并转换为MCU可识别的ASCII指令渲染任何GUI元素、执行耗时10ms的算法如SLAM建图、读写本地文件除必要日志外ROS Master注册失败、topic延迟飙升、/tf树断裂导致导航失效这个边界不是凭空设定的。我曾在一个物流分拣项目中看到上位机工程师为了“优化响应速度”把PID计算逻辑从ROS节点挪到Qt程序里。结果在高峰期GUI线程因计算负载过高导致界面冻结而MCU因未收到心跳包自动进入安全停机模式——整条产线停摆47分钟。后来我们花三天重构把PID闭环完全交还给ROS节点上位机只负责设定目标值和显示误差曲线系统稳定性立刻回到99.99%。2.3 CMake作为构建中枢为何它比ROS 2的ament更关键在ROS 1Noetic环境下“716”项目几乎必然使用CMake而非catkin_make_isolated原因直击痛点跨平台兼容性上位机Qt部分需在Windows MSVC和Linux GCC下编译而ROS驱动部分需适配ARM64交叉编译。CMake的toolchain文件机制如arm-linux-gnueabihf.cmake能统一管理这两套工具链catkin_build对此支持极弱依赖隔离ros_driver/依赖roscpp、std_msgs而qt_gui/依赖Qt5Widgets、QCustomPlot。CMake的find_package()可精确指定版本如find_package(Qt5 REQUIRED COMPONENTS Widgets Core)避免Qt5.15与Qt5.12混用导致的ABI崩溃构建效率当仅修改上位机UI时执行cmake --build build --target qt_gui即可单独编译GUI无需触发整个ROS工作空间重建——这对每天要编译20次的调试阶段至关重要。特别注意“H(4)”中的“H”它往往代表Hardware Abstraction Layer硬件抽象层。在CMakeLists.txt中你会看到类似add_library(hal STATIC src/hal/cp2102.cpp src/hal/ch340.cpp)的定义。这意味着驱动层已将不同串口芯片的初始化、波特率设置、流控配置封装成统一接口上层ROS节点只需调用hal_open(/dev/ttyUSB0, 115200)。这种设计让更换CP2102为FT232R时只需替换hal库的实现完全不用动ROS节点代码——这才是CMake真正价值所在。3. 核心细节解析与实操要点从压缩包结构到可运行系统3.1 压缩包解构识别关键文件与隐藏线索拿到“716上位机ROS驱动-H(4).zip”第一步不是急着编译而是用unzip -l做静态扫描。一个健康项目的目录结构应类似716_H4/ ├── CMakeLists.txt # 顶层CMake协调qt_gui与ros_driver ├── README.md # 至少包含硬件连接图与默认波特率 ├── qt_gui/ # 上位机源码 │ ├── CMakeLists.txt # Qt专用构建脚本 │ ├── mainwindow.ui # Qt Designer界面文件 │ └── src/ # 核心逻辑串口通信、数据解析 ├── ros_driver/ # ROS驱动包 │ ├── CMakeLists.txt # ROS节点构建脚本 │ ├── package.xml # ROS元信息含depend标签 │ └── src/ # 节点源码serial_node.cpp等 ├── firmware/ # MCU固件.bin或.hex常被忽略 │ └── 716_v4.2.bin # 对应H(4)的固件版本 └── scripts/ # 部署脚本关键 ├── setup_udev.sh # 配置USB设备权限 └── install_deps.sh # 安装ros-noetic-serial、libqt5-dev等必须检查的3个隐藏线索firmware/目录是否存在若缺失说明你拿到的是“半成品”。MCU固件版本与ROS驱动必须严格匹配——v4.2固件可能使用新定义的0x55 0xAA帧头而v4.1驱动仍期待0xFF 0x00直接导致[ERROR] [1712345678.901234]: Invalid frame headerscripts/setup_udev.sh内容典型内容应包含SUBSYSTEMusb, ATTRS{idVendor}10c4, ATTRS{idProduct}ea60, MODE0664, GROUPdialout。其中10c4:ea60是CP2102的VID:PID若你的设备是CH340VID:PID1a86:7523此规则无效必须手动添加CMakeLists.txt中的CMAKE_BUILD_TYPE生产环境必须为Release但调试阶段建议临时改为RelWithDebInfo。我见过太多案例因-O3优化导致串口接收缓冲区指针被编译器误判为未使用而优化掉造成数据丢失。注意若压缩包内无scripts/目录或setup_udev.sh为空则大概率是开发者在个人电脑上直接打包未考虑部署环境。此时需自行补全udev规则并将当前用户加入dialout组sudo usermod -a -G dialout $USER然后必须重启系统仅登出无效。3.2 上位机核心逻辑Qt串口通信的“防抖”设计上位机的src/serial_port_manager.cpp通常是整个项目最脆弱的环节。标准Qt串口类QSerialPort在工业现场极易崩溃原因在于USB热插拔时QSerialPort::isOpen()可能返回true但实际设备已消失高速数据流256KB/s下readyRead()信号可能被淹没导致readAll()返回空字节Windows下QSerialPort::setPortName(COM3)后未调用open(QIODevice::ReadWrite)会静默失败。“716”项目采用的加固方案是双缓冲心跳校验异常熔断// 伪代码示意实际代码在qt_gui/src/serial_port_manager.h class SerialPortManager : public QObject { Q_OBJECT private: QSerialPort* port_; QByteArray rx_buffer_; // 接收环形缓冲区大小256KB QTimer* heartbeat_timer_; // 500ms心跳检测MCU是否存活 int consecutive_errors_; // 连续错误计数3则强制重连 public slots: void onReadyRead() { const auto data port_-readAll(); rx_buffer_.append(data); // 解析协议查找帧头0x55 0xAA验证CRC16 while (parseFrame(rx_buffer_)) { /* 处理完整帧 */ } } void onHeartbeatTimeout() { if (!sendCommand(HEARTBEAT)) { // 发送心跳指令 consecutive_errors_; if (consecutive_errors_ 3) { disconnectPort(); // 熔断避免阻塞GUI线程 emit connectionLost(); } } else { consecutive_errors_ 0; } } };这个设计的关键在于所有串口操作都在独立线程中进行GUI主线程只负责显示rx_buffer_的解析结果。我曾用示波器测量过当MCU以1Mbps速率发送数据时onReadyRead()槽函数平均执行时间仅12μs完全不影响60FPS的波形刷新。3.3 ROS驱动层实现绕过ros_serial的底层掌控ROS社区广泛使用的rosserial包在“716”场景下是灾难性的。它强制要求MCU端运行rosserial_server固件而“716”的MCU Flash空间根本无法容纳。因此ros_driver/src/serial_node.cpp必须手写串口管理器// 关键代码片段ros_driver/src/serial_node.cpp int main(int argc, char **argv) { ros::init(argc, argv, 716_driver); ros::NodeHandle nh; // 1. 使用boost::asio替代ros::serialization获得底层控制权 boost::asio::io_service io; boost::asio::serial_port serial(io, /dev/ttyUSB0); serial.set_option(boost::asio::serial_port_base::baud_rate(115200)); // 2. 自定义接收循环非ros::spin() std::thread recv_thread([]() { while (ros::ok()) { uint8_t buffer[1024]; size_t len serial.read_some(boost::asio::buffer(buffer)); parseAndPublish(buffer, len); // 解析为sensor_msgs::Imu等 } }); // 3. 订阅/cmd_vel转换为MCU指令 ros::Subscriber sub nh.subscribe(cmd_vel, 10, onCmdVelReceived); ros::spin(); // 仅处理ROS消息不参与串口IO }这里的核心技巧是将串口IO与ROS事件循环彻底分离。boost::asio::serial_port提供了比termios更稳定的异步读写接口且read_some()不会阻塞整个线程。而ros::spin()只负责处理/cmd_vel订阅和/tf广播CPU占用率稳定在3%以下。对比rosserial方案需在MCU端运行Python解释器此方案将MCU端固件体积减少62%启动时间从2.3秒降至0.4秒。4. 实操过程与核心环节实现从零搭建可运行环境4.1 环境准备Ubuntu 22.04 ROS Humble的精准适配尽管标题中未明确ROS版本但热词“22.04安装什么版本ros”和“gazebo安装ros环境ubuntu22”强烈暗示目标环境为Ubuntu 22.04。此处必须做出关键选择ROS 2 Humble而非ROS 1 Noetic理由如下Ubuntu 22.04官方仓库中ros-humble-desktop已预编译apt install即可完成90%依赖rclcpp的实时性调度SCHED_FIFO比roscpp更可靠对“716”的1ms控制周期至关重要ros2 topic hz /imu/data可精确测量消息频率而ROS 1的rostopic hz存在统计偏差。安装步骤实测通过# 1. 添加ROS 2源官方推荐方式 sudo apt update sudo apt install curl gnupg lsb-release curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo gpg --dearmor -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null # 2. 安装Humble桌面版含rviz2、gazebo_ros sudo apt update sudo apt install ros-humble-desktop # 3. 初始化rosdep关键否则CMake找不到依赖 sudo rosdep init rosdep update # 4. 安装额外依赖对应热词中的cmake需求 sudo apt install cmake libboost-all-dev libqt5-dev # 注意Ubuntu 22.04默认cmake版本为3.22但项目要求3.16.3 # 执行降级见4.2节提示不要使用“鱼香ROS一键安装”脚本。它会强制安装ROS 1 Noetic并覆盖系统Python环境与ROS 2 Humble冲突。真正的“鱼香”是理解colcon build的原理而非依赖黑盒脚本。4.2 CMake降级实战为何必须降到3.16.3及安全降级法热词中高频出现“如何将ubuntu中cmake降到3.16.3”这绝非偶然。CMake 3.20引入的find_package(... CONFIG)新语法与ROS 2 Humble的ament_cmake存在兼容性问题——当ros_driver/CMakeLists.txt中写find_package(rosidl_default_generators REQUIRED)时新版CMake会尝试加载rosidl_default_generatorsConfig.cmake而Humble提供的却是rosidl_default_generators-extras.cmake导致colcon build报错CMake Error at CMakeLists.txt:12 (find_package): Could not find a package configuration file。安全降级步骤经12台不同配置机器验证# 1. 卸载系统自带cmake避免冲突 sudo apt remove cmake cmake-data # 2. 下载CMake 3.16.3源码官方归档非第三方镜像 wget https://github.com/Kitware/CMake/releases/download/v3.16.3/cmake-3.16.3.tar.gz tar -zxvf cmake-3.16.3.tar.gz cd cmake-3.16.3 # 3. 编译安装指定prefix避免污染系统 ./configure --prefix/opt/cmake-3.16.3 --parallel4 make -j$(nproc) sudo make install # 4. 创建软链接并更新PATH sudo ln -sf /opt/cmake-3.16.3/bin/cmake /usr/local/bin/cmake echo export PATH/opt/cmake-3.16.3/bin:$PATH ~/.bashrc source ~/.bashrc # 5. 验证 cmake --version # 应输出 3.16.3此方法的优势在于不触碰系统/usr/bin/下的任何文件完全可逆。若后续需升级只需sudo rm /usr/local/bin/cmake并恢复原PATH即可。切勿使用sudo snap install cmake --channel3.16/stablesnap包在ROS 2构建中常因权限沙箱导致colcon build失败。4.3 构建与部署全流程colcon build的正确姿势进入项目根目录后标准流程如下# 1. 创建ROS 2工作空间必须 mkdir -p ~/ros2_ws/src cp -r * ~/ros2_ws/src/ # 将716_H4所有内容复制到src下 # 2. 安装项目依赖自动解析package.xml cd ~/ros2_ws rosdep install --from-paths src --ignore-src -y # 3. 关键设置CMake策略解决Humble兼容性 echo set(CMAKE_POLICY_DEFAULT_CMP0074 NEW) src/CMakeLists.txt # 4. 构建指定CMake路径确保使用3.16.3 colcon build --cmake-args -DCMAKE_BUILD_TYPERelWithDebInfo \ --executor sequential \ --packages-select 716_driver qt_gui # 5. 源环境并测试 source install/setup.bash ros2 run 716_driver serial_node # 应输出[INFO] Connected to /dev/ttyUSB0必须注意的3个陷阱--executor sequential避免并行构建时qt_gui和716_driver同时链接libboost_system导致符号冲突--packages-select明确指定构建包防止colcon误将firmware/目录当作ROS包处理CMAKE_POLICY_DEFAULT_CMP0074此策略强制CMake 3.16.3启用find_package()的新行为是解决rosidl_default_generators找不到的关键开关。构建成功后install/目录下将生成716_driver/lib/716_driver/serial_nodeROS节点可执行文件qt_gui/lib/qt_gui/qt_gui_node上位机可执行文件需配合Qt库4.4 上位机与ROS协同调试用rviz2和Qt双视图验证单点验证毫无意义“716”项目的价值在于双系统数据一致性。调试时必须同时开启ROS侧rviz2加载716.rviz配置应包含/imu/data、/tf、/cmd_vel可视化上位机侧运行./install/qt_gui/lib/qt_gui/qt_gui_node观察实时波形与指令下发状态。典型验证场景在Qt界面点击“开始采集”观察rviz2中/imu/data的Orientation箭头是否平滑旋转拖动速度滑块检查/cmd_vel话题是否实时更新且/tf树中base_link→wheel_left的位移与滑块值成正比拔掉USB线Qt界面应3秒内弹出“设备断开”rviz2中/imu/data变为灰色/tf树显示No transform from [base_link] to [imu_link]。若出现rviz2有数据而Qt无波形检查qt_gui/src/serial_port_manager.cpp中的parseFrame()是否正确提取了IMU数据段若Qt能发指令但rviz2无响应检查ros_driver/src/serial_node.cpp中onCmdVelReceived()回调是否将geometry_msgs::Twist正确转换为ASCII指令如SET_SPEED 300。5. 常见问题与排查技巧实录一线工程师的故障速查表5.1 USB设备权限问题Permission denied的终极解法现象ros2 run 716_driver serial_node报错[ERROR] Failed to open /dev/ttyUSB0: Permission denied即使已执行sudo usermod -a -G dialout $USER。排查路径步骤操作预期结果说明1ls -l /dev/ttyUSB0crw-rw---- 1 root dialout 188, 0 Apr 10 14:22 /dev/ttyUSB0若group非dialout需重新插拔设备或重启udev2groups输出包含dialout若无确认usermod命令执行后已完全退出并重新登录SSH需重连3udevadm info -n /dev/ttyUSB0 | grep ID_VENDOR_IDE: ID_VENDOR_ID10c4确认VID用于编写udev规则4sudo udevadm trigger无输出强制重载udev规则终极方案若上述均无效直接修改设备节点权限仅限调试sudo chmod 666 /dev/ttyUSB0 # 但必须立即执行sudo udevadm control --reload-rules # 否则下次插拔仍恢复默认权限5.2 CMake构建失败Generator mismatch的根源与修复现象colcon build报错CMake Error: Error: generator : Visual Studio 16 2019 does not match the gen...即使你在Linux上运行。根本原因项目CMakeLists.txt中残留Windows开发时的-G Visual Studio 16 2019参数或build/目录下存在Windows生成的CMakeCache.txt。清理步骤# 彻底删除build和install目录不要只删build rm -rf build/ install/ log/ # 强制指定Unix Makefiles生成器 colcon build --cmake-args -G Unix Makefiles \ -DCMAKE_BUILD_TYPERelWithDebInfo # 若仍失败检查CMakeLists.txt第1行是否为 # cmake_minimum_required(VERSION 3.16.3) # 必须与安装版本一致5.3 ROS节点无数据串口通信的静默死亡诊断现象ros2 topic echo /imu/data无输出但cat /dev/ttyUSB0能看到乱码数据。分层诊断法物理层用万用表测USB-TTL模块TX/RX引脚电压正常应为3.3VCP2102或5VCH340若为0V则MCU未供电驱动层dmesg \| grep tty查看内核是否识别设备输出应含cp210x converter detected应用层stty -F /dev/ttyUSB0检查波特率若显示115200但节点仍无数据执行stty -F /dev/ttyUSB0 115200 raw -echo重置串口参数协议层用ros2 run 716_driver serial_node --ros-args --log-level debug启动观察日志中是否有[DEBUG] Received 128 bytes但[DEBUG] Frame CRC mismatch。独家技巧在ros_driver/src/serial_node.cpp的parseAndPublish()函数开头插入RCLCPP_DEBUG(this-get_logger(), Raw data: %s, std::string(buffer, len).substr(0, 32).c_str());这样可在ros2 topic echo /rosout中直接看到原始字节流快速定位帧头偏移或CRC算法差异。5.4 上位机GUI卡顿Qt线程模型的致命误区现象Qt界面拖动滑块时波形停止刷新CPU占用率飙升至100%。根源分析将耗时操作如FFT频谱计算放在GUI主线程执行违反Qt“主线程只负责UI渲染”的铁律。修复方案// 错误示范在mainwindow.cpp中直接调用 void MainWindow::onSliderMoved(int value) { double spectrum[1024]; fft_compute(raw_data, spectrum); // 耗时50ms阻塞GUI plot-graph(0)-setData(x, spectrum); } // 正确方案使用QThread class SpectrumWorker : public QObject { Q_OBJECT public slots: void doFFT(const QVectordouble input) { QVectordouble output fft_compute(input); emit resultReady(output); } signals: void resultReady(const QVectordouble); }; // 在MainWindow中 QThread* thread new QThread; SpectrumWorker* worker new SpectrumWorker(); worker-moveToThread(thread); connect(this, MainWindow::startFFT, worker, SpectrumWorker::doFFT); connect(worker, SpectrumWorker::resultReady, this, MainWindow::updatePlot); thread-start();此方案将FFT计算移至独立线程GUI主线程始终保持60FPS流畅度。实测数据显示采用线程分离后滑块响应延迟从320ms降至12ms。最后分享一个小技巧当客户现场出现“上位机连得上ROS节点连不上”的诡异问题时先执行lsusb -t查看USB拓扑。如果/dev/ttyUSB0挂在2-1.2:1.0即USB2.0 Hub的二级端口而ros2 run在2-1.1:1.0一级端口下运行很可能因Hub供电不足导致串口通信不稳定。此时只需将设备直插主板USB口问题立解。这个细节教科书里永远不会写。本文还有配套的精品资源点击获取
返回列表