智慧药房赛项资源包解析:ROS导航与YOLOX检测工程实践

发布时间:2026/10/22 5:22:43

智慧药房赛项资源包解析:ROS导航与YOLOX检测工程实践 简介这份资源是第二十五届中国机器人及人工智能大赛智慧药房组的完整参赛作品包面向备战同类机器人竞赛的高校学生与指导教师尤其适合需要参考赛题实现思路、搭建仿真与实机调试环境的中高级参赛者。压缩包共150个文件约38.19MB以39个yaml配置、23个Python脚本、11个launch启动文件、10个srv服务定义及若干cpp、msg、xml为主构成ROS工程骨架同时包含21张jpg与3张pgm图像、4份pdf设计文档、4份md说明、3段mp3语音及3mf模型等素材覆盖感知、导航、任务调度与文档记录多个环节。资源已有825人学习下载可帮助读者获取智慧药房赛项的完整方案参考包括节点通信结构、目标点下发逻辑、视觉检测脚本与设计文档便于对照复现、拆解模块并排查调试问题。1. 智慧药房赛项资源包从文件清单反推一套可复现的参赛系统拆开这个压缩包第一眼看到的不是整齐的工程目录而是一堆“混搭”文件main.cpp、send_goals_node.cpp是典型的 ROS 节点源码yolox.bin是训练好的检测模型权重reminder_basic.py.bak和launch_watcher.bash像是调试期留下的脚本备份还有Start_Point1.JPG、test_image.jpg这类现场标定和测试图以及一个珍贵的顶盖.gcode.3mf——这明显是打印件模型大概率是药房机构里的某个结构件。这套资源对应的是大学生竞赛里“智慧药房”方向的完整参赛作品包含设计文档和源码适合正在备赛、想找一套能跑通的参考实现、或者需要对照自己方案查漏补缺的队伍。它解决的核心问题是把“药房场景下的机器人取药、送药、识别、调度”这条链路从文档到代码到模型权重一次性给全让你不用从零搭框架而是站在一个已经跑过赛的工程上做二次开发。如果你正卡在“知道要做什么但不知道怎么组织工程”的阶段这份东西的参考价值比单纯看论文大得多。2. 工程结构拆解从文件后缀判断模块归属与依赖关系2.1 按后缀给文件分类先建一张依赖地图拿到一个没有 README 的压缩包最忌讳直接双击main.cpp开始读。我一般先按后缀和命名把文件归到几个功能域里再决定阅读顺序。这套资源里的文件可以分成四类文件类型推测功能阅读优先级main.cppC 源码主控节点负责整体状态机或任务调度高send_goals_node.cppC 源码向导航栈发送目标点控制机器人移动高yolox.bin模型权重YOLOX 检测模型用于药盒/药品识别中reminder_basic.py.bakPython 备份提醒逻辑或状态监控脚本低launch_watcher.bashShell 脚本启动监控可能负责拉起多个节点中Start_Point1.JPG/Start_Point2.JPG图片起点标定参考图低test_image.jpg图片检测模型测试输入低珍贵的顶盖.gcode.3mf3D 打印文件药房机构顶盖结构件低.gitignore配置文件版本控制忽略规则低这张表的价值在于你不需要通读所有文件先看main.cpp和send_goals_node.cpp就能抓住控制流yolox.bin决定感知能力上限其余是辅助。常见做法是先把两个 cpp 文件的#include和ros::init部分扫一遍确认 ROS 版本和依赖包。2.2 从main.cpp和send_goals_node.cpp还原控制流send_goals_node.cpp这个名字已经暴露了它的职责它大概率是一个 action client向move_base或类似导航包发送move_base_msgs::MoveBaseGoal。我一般会先找goal.target_pose.pose.position和orientation的赋值位置那里就是目标点的坐标来源。如果坐标是硬编码的说明这套代码的导航点是提前标定好的如果是从参数服务器或文件读取的那就有配置入口。main.cpp更可能是顶层状态机。重点看它有没有switch或状态枚举以及它如何调用检测结果和导航目标。一个典型的智慧药房流程是等待订单 → 导航到药架 → 检测药盒 → 抓取/确认 → 导航到取药口 → 完成。你需要在main.cpp里找到这个循环的入口和退出条件。// 典型 send_goals_node.cpp 片段根据常见 ROS 导航写法还原 #include ros/ros.h #include move_base_msgs/MoveBaseAction.h #include actionlib/client/simple_action_client.h typedef actionlib::SimpleActionClientmove_base_msgs::MoveBaseAction MoveBaseClient; int main(int argc, char** argv){ ros::init(argc, argv, send_goals_node); MoveBaseClient ac(move_base, true); // 等待导航服务器启动超时 60 秒 while(!ac.waitForServer(ros::Duration(60.0))){ ROS_INFO(Waiting for move_base action server...); } move_base_msgs::MoveBaseGoal goal; goal.target_pose.header.frame_id map; goal.target_pose.header.stamp ros::Time::now(); // 以下坐标通常来自标定需按实际场地修改 goal.target_pose.pose.position.x 1.2; goal.target_pose.pose.position.y -0.8; goal.target_pose.pose.orientation.w 1.0; ac.sendGoal(goal); ac.waitForResult(); return 0; }这段代码的逻辑说明MoveBaseClient连接名为move_base的 action serverwaitForServer确保导航栈已经就绪goal.target_pose里的frame_id必须是map坐标和朝向需要根据你的场地重新标定。参数说明position.x/y是目标点在 map 坐标系下的位置单位米orientation.w是四元数只给 w 表示朝向角为 0。如果你直接拿这套坐标去跑机器人大概率会撞墙因为场地尺寸不同。2.3yolox.bin的加载方式与输入输出约定yolox.bin是二进制权重不是可以直接cat看的文本。你需要知道它对应哪个推理框架。常见做法是如果工程里有 Python 推理脚本那大概率是 PyTorch 导出的 ONNX 或 TensorRT 引擎如果只有 C可能是 OpenVINO 或 NCNN 的模型文件。从文件后缀.bin看OpenVINO 的 IR 模型通常是.xml.bin成对出现这里只有.bin那更可能是 NCNN 的.param.bin组合但缺少.param文件。另一种可能是 TensorRT 序列化后的 engine 文件被重命名了。你需要在工程里搜索yolox关键字看哪个文件引用了它。如果找不到那这个权重可能只是备份实际推理用的是别的路径。我一般会先用file yolox.bin看文件类型再用strings yolox.bin | head看有没有框架特征字符串。如果确认是 NCNN你需要补一个.param文件才能加载如果是 TensorRT需要知道生成时的 CUDA 和 TensorRT 版本版本不匹配会直接报错。提示模型权重和推理代码必须版本对齐拿到的.bin如果缺少配套的.param或.onnx不要硬猜先找文档里有没有记录导出命令。3. 从源码到可运行环境搭建与节点启动的实操步骤3.1 ROS 工作空间初始化与依赖安装这套代码用了ros::init和 actionlib基本可以确定是 ROS1 环境。常见做法是建一个 catkin 工作空间把源码放进src下然后catkin_make。但直接编译大概率会报缺包因为move_base_msgs、actionlib这些不是默认全装的。# 创建 ROS 工作空间 mkdir -p ~/smart_pharmacy_ws/src cd ~/smart_pharmacy_ws/src # 把 main.cpp 和 send_goals_node.cpp 放到一个包目录下例如 pharmacy_ctrl/src/ # 假设包名为 pharmacy_ctrl需要先创建 package.xml 和 CMakeLists.txt catkin_create_pkg pharmacy_ctrl roscpp move_base_msgs actionlib std_msgs cd ~/smart_pharmacy_ws catkin_make source devel/setup.bash逻辑说明catkin_create_pkg会自动生成package.xml和CMakeLists.txt并把roscpp、move_base_msgs、actionlib、std_msgs列为依赖。参数说明包名pharmacy_ctrl可以改成你自己的但要和CMakeLists.txt里的add_executable对应。如果编译时报Could not find move_base_msgs说明 ROS 安装时没装导航包需要sudo apt install ros-distro-move-base-msgsdistro换成你的 ROS 版本比如noetic。3.2 启动顺序先导航栈再感知最后主控ROS 节点的启动顺序很关键。如果send_goals_node先起来move_base还没启动waitForServer会一直等虽然不会崩但会卡住。我一般按这个顺序启动机器人底盘驱动和传感器激光雷达、相机。启动move_base导航栈加载地图和代价地图参数。启动感知节点加载yolox.bin做检测。启动main.cpp对应的主控节点。最后启动send_goals_node或由主控节点内部调用。launch_watcher.bash很可能就是干这个的。你可以先cat launch_watcher.bash看它rosrun或roslaunch了哪些东西然后照着它的顺序手动跑一遍这样出问题能定位到具体节点。# 查看 launch_watcher.bash 内容了解启动顺序 cat launch_watcher.bash # 典型内容可能类似 # roslaunch robot_bringup bringup.launch # sleep 5 # rosrun move_base move_base # sleep 3 # rosrun pharmacy_ctrl main_node # rosrun pharmacy_ctrl send_goals_node 逻辑说明表示后台运行sleep给前一个节点留出初始化时间。参数说明sleep的秒数不是固定的取决于你的硬件启动速度激光雷达通常需要 5 到 10 秒才能出数据。如果move_base还没准备好就发目标点机器人会原地不动或报Goal rejected。3.3 用test_image.jpg验证检测链路是否通test_image.jpg是现成的测试输入你可以用它先单独跑一遍检测确认yolox.bin能正常加载并输出框。如果工程里没有现成的推理脚本常见做法是写一个最小的 Python 脚本用 OpenCV 读图用 ONNXRuntime 或 NCNN 加载模型打印输出维度。import cv2 import numpy as np # 假设模型是 ONNX 格式输入尺寸 640x640 net cv2.dnn.readNetFromONNX(yolox.onnx) # 如果只有 .bin需要先转成 ONNX img cv2.imread(test_image.jpg) blob cv2.dnn.blobFromImage(img, 1/255.0, (640, 640), swapRBTrue) net.setInput(blob) outs net.forward() print(输出维度:, outs.shape) # 期望 (1, 8400, 85) 或类似逻辑说明blobFromImage把图片缩放到 640x640 并归一化swapRBTrue是因为 OpenCV 默认 BGR 而模型训练常用 RGB。参数说明1/255.0是归一化系数(640,640)必须和训练时输入尺寸一致否则检测框会错位。如果outs.shape不是预期的检测头维度说明模型文件不对或输入尺寸不匹配。这一步能跑通再往 ROS 里集成就有底了。4. 避坑与排查五个让参赛队翻车的典型问题4.1 现象catkin_make报undefined reference to ros::init原因CMakeLists.txt里没有正确链接roscpp或者find_package(catkin REQUIRED COMPONENTS roscpp ...)写了但target_link_libraries漏了${catkin_LIBRARIES}。解决打开CMakeLists.txt确认add_executable之后有target_link_libraries(你的节点名 ${catkin_LIBRARIES})并且include_directories包含了${catkin_INCLUDE_DIRS}。4.2 现象机器人收到目标点后原地转圈或不动原因move_base的全局规划器没有收到有效地图或者目标点在障碍物里。Start_Point1.JPG和Start_Point2.JPG是起点标定图如果你没按图上的位置摆放机器人里程计初始位姿就是错的。解决先确认map坐标系下的机器人初始位姿和标定图一致再用rviz的2D Pose Estimate手动给一个初始位姿看激光点云和地图是否重合。4.3 现象yolox.bin加载时报Unsupported model format原因.bin文件缺少配套的.paramNCNN或.xmlOpenVINO或者文件本身是 TensorRT engine 但当前环境没有对应版本的 TensorRT。解决先用file yolox.bin确认文件类型如果是data且strings里出现ncnn字样去补.param如果出现TensorRT字样检查 CUDA 和 TensorRT 版本是否和生成时一致。不一致就重新导出 ONNX再用当前环境转一次。4.4 现象reminder_basic.py.bak直接运行报语法错误原因.bak是备份文件可能对应的是旧版本代码里面的 API 已经和当前 ROS 消息定义不匹配。解决不要直接跑.bak先看有没有同名的.py文件。如果没有把.bak复制成.py然后检查import的包是否还在消息类型是否和main.cpp里用的一致。常见的是std_msgs/String和自定义消息混用导致反序列化失败。4.5 现象珍贵的顶盖.gcode.3mf打印出来装不上原因.3mf文件里包含的模型可能不是最终版或者打印材料收缩率没补偿。解决先用切片软件打开.3mf看模型尺寸和你的机构设计图是否一致。如果差 0.2 到 0.5 毫米在切片软件里做 XY 补偿。另外.gcode.3mf后缀说明它可能已经切片过直接打印会沿用原来的参数换机器前要重新切片。注意避坑的核心是“先验证单点再串联全链路”。不要一上来就roslaunch全部节点那样报错信息会混在一起根本定位不到是谁的问题。5. 进阶用法把send_goals_node改成参数化目标点并用test_image做回归验证这套资源里最值得动手改的是send_goals_node.cpp的硬编码坐标。比赛场地每年可能变硬编码意味着每次都要重新编译。我一般会把它改成从 ROS 参数服务器读取目标点列表这样只需要改 YAML 文件不用动代码。// 改造后的 send_goals_node.cpp 片段从参数服务器读取多个目标点 #include ros/ros.h #include move_base_msgs/MoveBaseAction.h #include actionlib/client/simple_action_client.h #include vector typedef actionlib::SimpleActionClientmove_base_msgs::MoveBaseAction MoveBaseClient; int main(int argc, char** argv){ ros::init(argc, argv, send_goals_node); ros::NodeHandle nh(~); MoveBaseClient ac(move_base, true); while(!ac.waitForServer(ros::Duration(60.0))){ ROS_INFO(Waiting for move_base...); } XmlRpc::XmlRpcValue goals; nh.getParam(goal_list, goals); // 从参数服务器读取列表 for(int i 0; i goals.size(); i){ move_base_msgs::MoveBaseGoal goal; goal.target_pose.header.frame_id map; goal.target_pose.header.stamp ros::Time::now(); goal.target_pose.pose.position.x (double)goals[i][x]; goal.target_pose.pose.position.y (double)goals[i][y]; goal.target_pose.pose.orientation.w (double)goals[i][w]; ac.sendGoal(goal); ac.waitForResult(); ROS_INFO(Reached goal %d, i); } return 0; }逻辑说明ros::NodeHandle nh(~)让节点读取私有命名空间下的参数nh.getParam(goal_list, goals)把 YAML 里的列表读进XmlRpcValue。参数说明goals[i][x]对应 YAML 里的x字段需要显式转成double。对应的 YAML 文件可以这样写# goals.yaml goal_list: - {x: 1.2, y: -0.8, w: 1.0} - {x: 2.5, y: 0.3, w: 0.707} - {x: 0.5, y: 1.1, w: 0.0}启动时用rosparam load goals.yaml加载再rosrun pharmacy_ctrl send_goals_node。这样改完场地变了只需要改 YAML不用重新编译。另一个进阶技巧是用test_image.jpg做回归验证。每次改完检测相关代码先跑一遍test_image.jpg把输出的框画出来存成图片和上一次的结果对比。如果框的位置偏移超过几个像素说明预处理或后处理被改动了。我一般会写一个verify_detection.py固定随机种子固定输入输出框的坐标和置信度存成 JSON。下次跑的时候 diff 一下 JSON就能快速发现回归。import cv2, json, numpy as np net cv2.dnn.readNetFromONNX(yolox.onnx) img cv2.imread(test_image.jpg) blob cv2.dnn.blobFromImage(img, 1/255.0, (640,640), swapRBTrue) net.setInput(blob) outs net.forward() # 只保留置信度大于 0.5 的框存成 JSON boxes [] for det in outs[0]: conf det[4] if conf 0.5: boxes.append({x: float(det[0]), y: float(det[1]), w: float(det[2]), h: float(det[3]), conf: float(conf)}) with open(detection_baseline.json, w) as f: json.dump(boxes, f, indent2) print(f保存 {len(boxes)} 个检测框)逻辑说明outs[0]是检测头输出det[4]是置信度det[0:4]是框的中心点和宽高。参数说明置信度阈值0.5可以按场景调药盒检测一般可以设到0.6减少误检。把结果存成 JSON 后下次改动代码再跑一遍用diff对比就能知道有没有引入回归。从那以后我每次拿到没有 README 的参赛资源包都强制先做三件事按后缀分类建依赖地图、用一张测试图验证感知链路、把硬编码参数改成可配置。这三步走完后面无论怎么改都不会迷路。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/22 5:22:43

LSTM自编码器实现智能电表计量异常检测与Python复现

干计量运维的人都有过这种感觉:台区线损突然偏高,心里第一反应就是“又有一块表不准了”。可一个台区挂了三四十块户表,线损的锅到底扣在哪一块头上?靠经验猜不靠谱,一块块现场校验又太慢。深度学习能不能干这个活&…

2026/10/22 5:22:43

PHP项目安全HTTP头配置实战:从CSP到HSTS的完整加固指南

我们平时给PHP项目做安全审计,最容易看到的一个结果就是“缺少安全响应头”。你说功能、权限、参数过滤都做得不错,但安全头一排看下来全是空白——攻击者只要用浏览器直接访问,就知道这个站点有没有做基础加固。其实设置安全HTTP头是投入产出…

2026/10/22 5:22:43

OPNET MANET仿真实战:从模型解析到参数调优与避坑指南

简介:这是一份基于OPNET平台的MANET无线自组织网络仿真实例压缩包,面向网络仿真初学者、高校学生及研究MANET路由协议的技术人员。资源围绕AODV、DSR、TORA、OLSR、GRP等多种主流路由协议,构建了50节点、100节点、移动场景等不同实验模型&…

2026/10/22 6:27:47

裸机编程实战:树莓派从零解析SD卡FAT文件系统

裸机系列写到这里已经是第10篇了。前几篇我们把树莓派的GPIO、UART调试串口、SPI总线上的SD卡驱动都跑通了,但冷静下来看,之前所有代码都有一个共同的尴尬:数据全靠硬编码在C数组里。哪怕只是想改一个启动参数,都要重新编译烧录&a…

2026/10/22 6:27:47

51单片机入门:定时器0与外部中断0驱动数码管倒计时报警器

很多人在51单片机入门时,做完流水灯和按键控制之后,都会卡在数码管这一关。尤其是你要同时用上“数码管动态显示 外部中断0 定时器0 蜂鸣器”这四个东西时,程序一烧进去,数码管要么乱闪,要么亮度不均匀,…

2026/10/22 6:27:47

AI辅助开题报告写作:从选题凝练到答辩预演的完整实操

1. 从选题荒到导师约谈:我为什么会对一款AI写作辅助工具动心1.1 那场把我逼到灵感的开题答辩预演先说个背景。我在某高校读研那阵,研究方向定得早,可开题报告整整憋了三周。一开始我以为自己有进度,结果把初稿交过去,某…

2026/10/22 6:27:47

A-59工业级语音模组:双波束拾音与100dB消回音深度解析

从做音频方案的第一天起,我听得最多的抱怨就是三个词:喂喂喂、滋滋滋、你说什么。尤其是通话类产品,拾音距离不够、回声压不住、背景噪声盖过人声,这三座大山能把一个硬件规格看着不错的产品,硬生生拖成用户嘴里那句“…

2026/10/22 6:27:47

AI辅助编程:游击战与阵地战的战术选择与切换之道

最近大半年,我在好几个团队里都看到同一种分裂:一部分人借助AI写代码的速度已经快到让人怀疑人生,一天能产出几百行甚至上千行,改起需求来像换衣服一样随意;另一部分人却越写越慢,先画架构图、再定接口、再…

2026/10/22 6:22:46

微电网母线电压稳定控制:模糊自适应PID原理与仿真调试

做微电网仿真或者碰过现场调压的工程师,应该都见过这种画面:负载一突变,母线电压要么掉得很深,要么来回震荡好几个控制周期。手头的PI参数在某个工况下还能用,换一个出力点又不行。模糊自适应PID参数调节在微电网母线电…

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑