Files

301 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工位前端与设备后端
启动:`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` 并重启工位即可恢复本站采集模式。