Files

工位前端与设备后端

启动:bash station/run_station.sh。旧入口 desk/run_desk.sh 也转到这里。 离线打开界面:bash station/run_station.sh --offline。 回归测试:bash station/run_station.sh --self-test。

界面

侧边导航只保留“首页”和“标定”,顶部保留“设置”按钮及设置界面。顶部左右臂按钮切换当前臂,悬停显示对应设备 IP。 首页左侧为机器人状态、机器人实时预览和夹爪 3D 预留区;右侧为当前臂腕部及共用 手外相机、对应臂左右指光流预留区和三层任务按钮。开始全部流程暂未接入。 标定页预留相机预览、采点、求解和保存区域,后续接入 handeye/,当前不运行标定命令。 原手动控制、自动任务、相机与视觉、工艺参数页面均已移除。

没有可靠反馈的使能/TCP 状态显示未知;光流、夹爪 3D 和软件急停尚未接入。 拍照保存当前 RGB 预览至 ~/Pictures/ORISYS/;放大和全屏预览使用同一图像缓存。 没有查看需求的相机订阅会撤销。离线运行不连接 ROS 或设备,也不下发控制。

首页动作接口

  • 检测 → stage_track,运行现有第一阶段跟踪:D405 优先、丢失目标后 D435 补位;会移动机械臂,不进入对孔或旋入。
  • 对孔 → stage_locate,运行新版第二阶段:精对孔并下降至配置的进孔高度;不自动转 A7。当前 hole_entry_mm=1.0。
  • 旋转 → 独立 a7_rotation ROS 节点,复用上游首轮 A7 正反转和四轮松爪、微抬、空回、降回、合爪旋转;不重新跟踪/对孔。夹爪通过本站 GripperExecute 服务控制,不调用 shell。起点高度/姿态、夹爪或限位不满足时中止。
  • 开始全部流程 → 跟踪、对孔、A7 旋转依次执行,失败/取消后不进入下一段。
  • 回零 → 所选臂命名点文件中的 servo.home_pose_name(默认 home),经原生轨迹 Action 返回初始姿态,不是关节全零。
  • 停止 → 取消本站任务、通过控制器管理服务暂停对应臂的运动控制器(硬件接口调用 Robot::Stop),停止本站流程,再等待新鲜关节反馈持续稳定。失败时明确报告停止未确认,并阻止继续运动。仅下一次显式动作恢复本站暂停的控制器。

有本站任务正在运行时,停止始终指向正在执行的臂,不受界面左右臂切换影响。 没有指定机械臂的后台关闭请求只取消本站任务,不暂停外部控制器。软件停止不替代硬件急停。 外部跟踪/对孔节点存在时拒绝重复启动,不批量结束外部节点;第一阶段子 launch 留在受管进程组内。 动作依赖对应臂关节反馈、控制器归属、相机和手眼配置,检查不通过则拒绝执行。

边界与设备归属

Qt 前端只负责控件、绘图和本地状态缓存。runtime_client.py 在后台线程通过私有 Unix socket 提交命令、读取任务结果和状态,绘图/定时刷新不会等待 ROS、串口或脚本。 同一工作区只允许一个工位窗口。后端失联后不重发命令,旧状态会标成过期。

control_service.py 是独立后台进程,tasks.py 保留任务编号、排队/执行/完成/失败/取消状态, 并按设备锁定资源;界面线程结束不等于动作完成。ros_gateway.py 用持续 ROS 订阅读取 关节、夹爪和触觉数据,用原生服务和轨迹 Action 执行操作。工位不另开 RDK 连接,也不读串口。 机械臂连接由现有 ros2_control 硬件驱动持有,串口由对应的常驻夹爪驱动独占。

回点与 home 通过 /rizon_arm_controller/follow_joint_trajectory 执行,取消使用目标句柄的回执, 不取消其他客户端的目标。发送前检查关节状态、控制器激活与关节归属;有外部轨迹执行时拒绝抢占。 仅有位置反馈时显示“使能未知”,不把关节消息冒充使能/Remote 状态。

PGEA 的常驻驱动提供:

  • /station/grippers/arm_<臂号>/execute:arm_msgs/srv/GripperExecute。
  • /station/grippers/arm_<臂号>/status:arm_msgs/msg/GripperStatus。

位置字段沿用消息契约,单位为 mm;前端的千分比在后端转换。连接/探测不会初始化、回零。 驱动拒绝非有限值、越界位置、非法力/速度和不支持的旋转操作。写入成功和实际到位分别判断; 结果未知时阻止继续发夹爪运动,要求先探测确认。

图像链路

RealSense 采集节点只在显式请求时启动。外部发布者不会被接管,工位也不会按进程名终止它。只有本站进程记录与当前唯一发布者身份同时匹配,才允许预览和启动后续算法。 采集配置继续来自 site.yaml,算法使用的彩色/深度独立于界面预览。

cam_bridge.py 负责 ROS 图像接收与共享内存输出。当前 Qt 界面只请求真机彩色流; 没有查看需求的图像与检测订阅会撤销。彩色预览不再额外限到 15 fps,保留最高 1920×1080 的显示图像, 算法输入不受影响,采集端现有原图话题的发布速率不会因此降低。 图像或桥进程超过 2 秒未更新时清除旧显示。共享内存由生产者释放,读取者只解除映射。 深度、仿真及标定流的后端能力保留给独立工具,不再提供首页切换入口。

触觉三力和预览由同一个常驻 arm_application.tactile_node 提供。后端订阅结构化力反馈, 仅在开启预览时订阅 /tactile/<side>/image、/vector,不会另外调用传感器 SDK。 关闭预览不停止力反馈。只有两指反馈新鲜时才允许旋入。已有外部触觉节点会被复用; 若其 image_hz=0,需在原启动入口启用预览输出。

进程与关闭

日常夹爪操作和示教回点不调用 shell。跟踪/对孔使用 Python 生成的 ROS launch 参数, 默认不自动拉额外查看器、红外或触觉流。上电的网络/工具准备与手眼标定保留原维护脚本, 由后端作为明确的系统/标定工具运行。operator_console.py 的旧 HTTP/脚本控制路径已经退役。

processes.py 只登记并停止自己启动的进程树,不扫描和终止外部进程。Linux 子进程设置 父进程退出信号,后端也监测界面退出,避免留下预览或控制孤儿进程。 “停止动作”取消工位任务,保持采集和驱动;窗口关闭时取消任务并退出自己持有的进程。 软件流程停止不能证明控制器已停稳;轨迹、夹爪停止未确认时会明确报告结果未知。

现有工程的边界

  • 当前 ROS 控制栈的 controller/action 名称是全局的,因此机械臂动作统一串行;没有假装支持双臂同时控制。
  • 本项目未包含 RGI 旋转夹爪实现。可接入同契约的常驻驱动,当前不会猜寄存器或调用缺失的模块。
  • 软拖拽只接受 /station/arms/arm_<臂号>/soft_drag 常驻控制驱动服务;未提供时按钮禁用, 不再建立第二条 RDK 连接。记录点和轨迹回点不依赖软拖拽桥。
  • 缺失的通止规整套状态机入口明确禁用,不再调用不存在的脚本。
  • 已运行的旧夹爪节点可以继续提供只读状态;要迁移到新命令服务,需先在其原启动终端停止旧节点, 再用“系统设置 → 连接夹爪”启动新驱动。工位不会擅自抢占串口或杀掉外部节点。
  • 仿真预览要求相应 DDS 域可达;切换显示来源不会改写真机控制后端的 DDS 域。

测试使用独立 DDS 域与模拟传输,覆盖 Qt 设备隔离、消息/共享内存图像、命令参数边界、 到位等待、资源互斥、轨迹取消、状态过期、父进程退出和正常关闭。没有以测试名义下发真机运动。

每次后端启动会生成一份私有站点配置快照,所有子进程使用同一份设备身份与参数。 现场 site.yaml 发生变化后拒绝新操作并提示重启,已执行任务继续使用原快照,避免运行中换臂或换相机。

第一批节点治理

后端按设备驱动、运动控制、算法任务、界面辅助分类展示 ROS 图,并保留完整命名空间及同名实例数。 分类仅表示职责:发现一个节点不等于确认它健康,更不代表工位拥有它。 监控页另列出本站实际启动的进程 PID、启动时间和退出状态,以及当前任务占用的资源、臂号和取消请求。 后端断联时这些缓存统一降为未知。

Linux 启动器作为 subreaper 持续监督每条启动命令,主命令结束后先终止并回收其遗留子进程, 包括自行创建 session 的节点,再允许任务返回。控制栈、相机、算法继续分进程运行;不按节点名字杀外部进程。 日志保留历史,但任务结果仅截取本次启动写入的部分,避免成功任务显示上次错误。

使能显示同时要求本臂关节反馈新鲜、原生驱动 RobotStates 中的机器人时间戳持续推进; 仅 ROS header 更新或重复缓存帧不能确认使能。依据是本项目原生驱动只在 connected/operational 时更新该结构。 这属于应用层运行状态判断,不替代机器人自身安全状态或安全控制器。

本批尚未迁移全局运动接口到双臂命名空间,也未替换旧命令行维护/标定脚本。 这些入口仍需后续逐条迁移;目前继续使用全局运动资源互斥,禁止双臂同时下发。 相机未连接时不更改采集分辨率、USB 模式或宣称实测帧率。预览改动使用模拟图像验证。

项目发现配置统一在 config/ros_runtime.env:默认 domain 42、仅本机发现,不再继承 shell 里 不明来源的 domain 0 或仿真域。显式部署覆盖使用 REAL_ROS_DOMAIN_ID 和 REAL_ROS_LOCALHOST_ONLY,所有子启动脚本继承同一值。CLI 观察节点前需先 source 本项目的 scripts/env.sh。ROS 域用于防止误混入,不属于访问控制或机器人安全功能。

运行中的旧工位仍保持原域,代码修改不会热切换它。迁移需要在任务结束后完整关闭旧工位, 再重新启动;旧域外部控制栈必须由原入口退出,不能同时对同一硬件启动第二份驱动。

操作并发与收尾

资源策略集中在 command_policy.py:Qt 只合并同一臂的同一按钮重复请求,其他请求交由后端 按实际写入设备判断。机械臂使能/跟踪/对孔/回零、相机刷新、夹爪控制和传感器刷新/底图重置 可跨模块并行;读取图像和力反馈不等于独占采集模块。A7 与全流程实际控制夹爪,所以执行期间 保留该夹爪的互锁。仍共用控制器的机械臂运动保持互斥。同一相机初始化由相机适配器内部串行, 只有断流或配置不匹配才重启;机械臂使能不阻止相机重连。触觉预览不占用触觉采集资源。 机械臂反馈中断仅记录在 backend.device_errors.arms,不提升为全后端错误,不禁用其他模块。

冲突提示显示动作名称、臂号和执行/收尾/停止阶段。子进程启动器通过私有阶段文件报告收尾, 直到整个所属进程树退出才释放资源。工位 pipeline 的结果展示停留改为 0.3 秒;手工 CLI 默认仍为 15 秒。此停留仅发布结果/预览,不更改轨迹、定位精度或控制器保持行为。

停止 RPC 不经过任务队列;明确停止机械臂时只取消运动控制任务,保留独立的相机连接和只读任务。 停止未确认仍锁住后续机械臂控制入口,不能靠并发改动绕过停止确认。

高清相机配置

当前请求 D405 彩色/原生深度 1280×720@15,D435 彩色 1920×1080@15、原生深度 1280×720@15,由 SDK 对齐到彩色像素坐标。采集器只尝试本台设备实际枚举支持的配置, 不再把 1080p 一律过滤,也不强迫 D435 彩色和原生深度使用同一分辨率。

Qt 预览通过本站采集节点的 color/preview/compressed 接收 JPEG 95 图像,只有订阅者存在 时才编码;算法继续订阅完整 color/image_raw,无算法订阅者时不序列化这份大图。 预览桥支持到 1920×1080,原生分辨率在该范围内保持不缩小。压缩仅用于显示,不用于定位计算。

两台相机共用 5Gbps Hub 的现场并发测试出现断流,单台可接近 30fps;部署应使用独立 USB3 连接并重新核对两路同时采集的实测值,不能仅凭配置数字认为帧率已达标。

“刷新相机”核对本站采集所有权及新鲜 CameraInfo:正常且分辨率匹配的流保持运行, 断流或配置不匹配才重启本站对应进程,并等待 DDS 旧发现记录退出、收到实际新帧后报告成功。 相机刷新与机械臂模块独立,不接管外部节点。SDK 留出四个帧缓冲供对齐持有, 应用层仍只保留最新一帧;每轮处理后释放 SDK 引用,避免等待新帧时占住旧缓冲。

使能与轨迹恢复

使能的连接检查不恢复本站暂停的轨迹控制器;只有明确的新动作才恢复控制器。 关节控制循环不再自动 ClearFault/Enable。JTC 使用实际关节反馈作为新轨迹起点 (open_loop_control=false)。硬件激活、控制器恢复和厂家 MoveL 结束后, 旧目标被拦截,直到指令流从实测关节附近重新开始;同时清空 NRT 前馈缓存。 更换驱动库使用原子替换,运行中的驱动保持原版本,重启工位后加载更新。 离线回归包括暂停后使能不恢复控制器,以及旧目标持续重放不能通过硬件保护。

实体急停后的任务取消

原生驱动在 connected/operational 丢失、故障或硬件时间戳停滞时锁存中断, read/write 返回 ERROR,要求控制器停止;write 在处理厂家 MoveL 请求前检查锁存。 仅恢复 Enable 或清故障不会解除锁存,需要显式重新激活硬件生命周期 (当前工位可通过重启控制栈重新连接)。重新激活会丢弃旧 MoveL 请求序号。 后端在运动期间监控原生反馈,故障或连续 0.75 秒没有推进时取消机器人控制任务, 走既有停止与子进程清理流程;取消后的迟到成功也按 cancelled 记录。 显式重新连接时,驱动先调用 Stop 清除机器人端残留动作,再请求 Enable;Stop 失败则不使能。 此软件逻辑不替代实体急停。Qt 顶部急停图标仍未接硬件接口,保持禁用。 验证使用隔离 ROS 域和纯 C++ 状态锁存测试,没有真实机械臂动作测试。

底部操作日志

底部日志记录按钮点击、重复点击拦截,以及后端收到请求、拒绝、任务开始、阶段变化、 取消和最终结果。时间精确到毫秒,显示臂号和任务编号;短任务的状态变化也保留为事件, 不依赖界面轮询碰巧看到 running。任务返回的 detail 和完整进程日志路径也会显示。 界面保留最近 3000 行,翻看历史时不强制滚到底部;后端缓存最近 512 条事件, 完整操作事件追加保存到 log/station/operations.jsonl,进程输出保留原独立日志。

重写的触觉与平行夹爪模块

参考 ctag2f-90d-sdk/gripper/panel.py 的数据处理方式,自行实现本站节点与界面, 不导入、不运行它的 panel、GripperClient、GripperWorker 或自动抓取流程。 低层触觉算法使用 /home/cheng/orisys-sdk/src 中的 Orisys SDK。

  • arm_application.tactile_flow_node:left/right 各自独立 ROS2 节点和进程, 每路独占一个触觉相机;CPU 目标 30 Hz。读取 SDK 的 VRAW 矢量场绘制箭头, 不重复解算光流;没有画面订阅者时不编码 JPEG。不强制 USB reset、不停止别人的进程。
  • /tactile/{left,right}/status:TactileStatus,含在线、帧率、接触、三向原始力和错误。 这里保留 RAW 通道量纲以兼容现有装配阈值,不把未经校准的值标为牛顿。
  • /tactile/{left,right}/flow/compressed:CompressedImage/JPEG。预览与状态使用独立通道, 不被 Qt 的低频状态刷新限制。队列只留最新样本,断流显示离线并尝试本侧重连。
  • arm_application.gripper_node:一个串口一个节点,使用现有经过验证的 DH Modbus 协议层, 仅暴露 /station/grippers/arm_<id>/execute 类型化服务与 status 状态话题。 无旧 Float32 数组控制入口;连接/探测绝不初始化或移动。当前支持 PGE/PGEA 平行夹爪, 右臂 RGI 旋转夹爪仍须独立匹配驱动,不能用 PGE 寄存器替代。
  • 界面:光流区标题右侧提供“刷新传感器”,显示 left/right 帧率和三向力; 夹爪区标题右侧提供“连接 / 探测”,开合使用任务操作按钮和后端默认力/速度。 连接与刷新不下发运动,闭合、张开才写位置,并等待实际到位反馈。

主入口仍是 bash station/run_station.sh。重启后在首页分别点“刷新传感器”和夹爪“连接 / 探测”。 本站会自管相应节点,不要同时从其他入口重复启动。ROS 独立启动入口为 tactile.launch.py(左右触觉)与 station_gripper.launch.py arm:=89(平行爪)。 旧 dh_pgea.launch.py / tactile executable 保留为兼容入口,主界面不再启动它们。

站点的 tactile.arm_id 明确绑定机械臂(当前 89),sdk_python_path 为低层 SDK 的 src 路径; 配置 JSON 已复制进本仓库 src/arm_application/config/tactile/ctag2f90d.json。 left_video/right_video: auto 只选择 USB vendor 1bcf 的触觉 UVC 采集接口,排除 Intel RealSense、 笔记本摄像头与重复接口。首次插入后需确认物理左右;建议改成各自稳定的 /dev/v4l/by-path/..., 避免 USB 插口调整导致左右顺序变化。两侧不能指向同一设备,跨进程锁会拒绝重复占用。

额外 CPU 依赖见 station/requirements-tactile.txt,已装入现有 ros_flexiv;未另建 conda 环境。 验证为假串口、合成光流、隔离 ROS 域与 Qt 离线回归;未做真实开合或机械臂动作测试。

RGI STEP 三维模型

主界面机械臂与夹爪均使用 Qt QOpenGLWidget 内嵌渲染,不是 RViz;ROS 提供设备状态。 run_real.sh 默认 use_rviz:=false,RViz 可作为独立调试工具按需打开。 右臂 RGI 模型来自用户提供的 RGI-100-30-O-S_简易模型_V3.0_20241010.STEP(SolidWorks AP203,毫米)。 原件保存在 station/src/station/assets/models/rgi-100-30/source.step,离线通过 scripts/convert_step_mesh.py 生成 mesh.npz 与含原件 SHA256、单位和边界的 mesh.json。 当前缓存 35148 个三角面,加载后按米渲染,运行时不依赖 CAD 内核,不在 UI 中转换 STEP。 显示颜色做了提亮处理以便观察边缘,几何保持原始装配位姿。

选择右臂显示真实 RGI CAD 外形,可拖拽旋转观察、双击复位;目前是静态装配模型, 未猜测各零件的运动轴,也未把整个夹爪当作手指错误旋转。待建立零件/关节映射并有 旋转爪实时反馈后再加入开合与旋转动画。左臂仍显示标注为近似的 PGEA 尺寸模型, 不会把 RGI 外形套到左臂。本次没有更改机械臂控制或发出设备命令。

首页夹爪区已精简:标题右侧只留“连接 / 探测”,移除力、速度、目标位置控件; 开合继续使用任务操作按钮及后端默认参数。传感器标题右侧只留“刷新传感器”, 负责重启本站拥有的左右触觉采集节点,重新检测设备并恢复预览,不操作机械臂或夹爪。 两个入口尺寸均为 92×28,与“刷新相机”一致。

标定力显示与光流布局

界面 Fx、Fy 置于每张光流的左、右两侧,保留方向正负号;下方显示法向 Fn,均标注 N。 列宽和下方行高固定,数值更新不会推动布局。 tactile_flow_node 在保留 /tactile/<side>/status 原始值的同时,新增标准 /tactile/<side>/wrench(geometry_msgs/WrenchStamped):force.x=Fx、y=Fy、z=Fn,单位牛顿。 装配算法仍接收原始 status,判断阈值不变。没有新鲜标定力话题时界面显示横线, 不会把原始数值直接标成 N。

换算曲线来自用户给定的 CTAG2F-90D 示例项目 config/gripper.json,已作为纯数据复制到 src/arm_application/config/tactile/force_calibration.json。使用分段线性插值、配置的外推和 零下限,切向分量按绝对值换算后恢复正负号。这是参考项目的现有曲线,并非本次 对左右两只设备逐只做出的实测标定。可在 tactile 配置中设置 left_force_calibration_file / right_force_calibration_file 覆盖各自数据, 或用 force_calibration_file 指定共用曲线;不导入或运行示例面板代码。

光流旁的切向力曲线与底图重置

每张光流左右是 X/Y 切向力折线图,显示最近 10 秒(N);四张小图使用相同时间窗 与对称量程,零力在中线。所有图表与画面顶底对齐,下方仍显示 Fn 标定法向力。 采样来自标定 wrench 话题的有界历史,不使用原始力替代。

“重置底图”在“刷新传感器”前。它通过 /tactile/<side>/reset_reference(std_srvs/Trigger) 请求本站节点在采集线程里、获取新画面后执行 SDK Sensor.reset(),不重启采集、不操作夹爪。 成功后清空该侧的光流缓存和力历史;离线侧返回未重置,连接恢复后不自动补执行旧请求。 超时明确报告结果未知,不自动重试。建议传感器无受力时操作。 跟踪/对孔/旋入流程与底图重置互斥,避免执行中更换测量参考。

触觉区最新布局:每侧 X 力图、光流、Y 力图为三个等大的正方形画布,随可用宽高适配。 切向力图纵轴固定 −10~10 N,显示数值刻度,超出范围的曲线裁剪;不再自动缩放。 横轴固定滚动 60 秒(−60、−30、0s)。后端标定力历史容量扩至 2100 点, 按实际时间截取最近一分钟,满足 30 Hz 采样;重置底图仍清空对应历史。

触觉标题采用 left(30fps) 当前法向力 5N 形式,fps 小字、不使用圆点分隔符, 下方不再重复显示法向力。曲线末端的圆点标记也已去除。 曲线横轴改为最近 1800 个采样点的序号(0/900/1800),纵轴仍固定 ±10 N; 不再由墙钟时间推动横轴或剔除数据。没有新样本时曲线保持不动。 新增轻量 tactile_preview 增量通道,携带 JPEG 与自上次序号之后的标定力样本, 最高 30 Hz;Qt 绘图计时器直接消费该缓存,避免低频状态刷新导致曲线跳动。 每次重置/刷新会变更参考版本,使客户端清空旧曲线;重复返回的采样序号不会重复追加。

远端相机预览

config/site.yaml 的 remote_camera_preview 显式选择远端相机。当前读取 192.168.2.10(域 0)的 d405_left / d435 原始图像,通过本机有线地址 192.168.2.50 发现,避免组播走 VPN 虚拟网卡。远端未发布 JPEG 预览时使用 raw。

只有 station.cam_bridge 子进程改用远端 ROS 域和网卡;机械臂后端、夹爪与 传感器的环境不变。“刷新相机”重建只读预览订阅并等待画面,不启动/停止 本机或远端物理相机驱动。首页远端订阅不再要求该相机由本机后端持有。 远端模式仅用于显示,不把网络图像自动接入本机机械臂跟踪或标定运动。

关闭 remote_camera_preview.enabled 并重启工位即可恢复本站采集模式。