贴面后不再抬回悬高;MoveL 切回关节后不再把整段位移当前进速度。
搜孔开局会抬到悬高再落回 hole_hover_z_m,螺纹还没咬合臂就离开孔口。没有接触传感器,到位只认 final_gap 算出的法兰高度,贴面后停在这个 Z。09:25 厂家 MoveL 切回 NRT_JOINT_POSITION 的第一拍把整段关节差算成 1.686 rad/s,前瞻再推 0.07 rad,光心从 5 mm 冲到 276 mm 后急停。切回后钉住结束瞬间的关节角并清零速度,JTC 还指着出发前的关节时不放行。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -51,8 +51,8 @@ HEADER = """\
|
||||
# 用法:复制本文件为 config/site.yaml,然后把所有 <占位符> 换成现场实际值。
|
||||
# 现场值包括:机械臂 SN / IP、相机序列号、夹爪串口、传感器 USB 路径。
|
||||
#
|
||||
# 说明:仓库里的文件用相对仓库根的路径(例如 models/yolo/...)。
|
||||
# 不要改回某个用户的绝对家目录,换台电脑就会找不到文件。
|
||||
# 说明:site.yaml 里的路径出现 /home/USER 是因为生成时匿名化了用户名;
|
||||
# 部署时请改成实际用户的 home 目录。
|
||||
# =============================================================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -42,13 +42,7 @@ def load_site(path: Path | None = None) -> dict:
|
||||
data = yaml.safe_load(text) or {}
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"无效的 site.yaml: {path}")
|
||||
# 相对路径按仓库根展开。site.yaml 里不再写 /home/某用户/ros2。
|
||||
src = str(_root() / "src" / "arm_application")
|
||||
if src not in sys.path:
|
||||
sys.path.insert(0, src)
|
||||
from arm_application.site_io import absolutize_site_paths
|
||||
|
||||
return absolutize_site_paths(data, _root())
|
||||
return data
|
||||
|
||||
|
||||
def list_robots(data: dict) -> list[dict]:
|
||||
@@ -947,7 +941,7 @@ def servo_overrides(data: dict) -> dict:
|
||||
if "d435_handback_sec" not in servo:
|
||||
raise RuntimeError(
|
||||
"servo.d435_handback_sec 必须显式写"
|
||||
"(厂家 MoveL 到位后,再确认多久才交回 D405)。不许用代码默认秒数。"
|
||||
"(厂家 MoveL 结束后,法兰停稳多久才打开 D405 框心)。不许用代码默认秒数。"
|
||||
)
|
||||
if "wrist_align_sec" not in servo:
|
||||
raise RuntimeError(
|
||||
|
||||
@@ -33,7 +33,6 @@ RDK 的 FloatingJoint/FloatingCartesian 通常需要额外授权。
|
||||
{"cmd":"drag_on","mode":"soft"} # soft | floating
|
||||
{"cmd":"drag_off"}
|
||||
{"cmd":"joints"}
|
||||
{"cmd":"move_joints","q":[0,0,0,0,0,0,0],"speed":0.15}
|
||||
{"cmd":"quit"}
|
||||
|
||||
每个命令的应答都是单行 JSON,含 ``ok`` 字段;失败时另有 ``error``。
|
||||
@@ -170,69 +169,6 @@ def _ensure_operational(robot) -> Optional[str]:
|
||||
return ensure_operational(robot)
|
||||
|
||||
|
||||
def _restore_tracking_impedance(robot, flexivrdk) -> None:
|
||||
"""把关节阻抗从软拖拽拉回名义值,否则回放点位时臂跟不住指令。"""
|
||||
try:
|
||||
info = robot.info()
|
||||
groups = dict(getattr(info, 'single_arm_groups', None) or {})
|
||||
except Exception as exc:
|
||||
log(f'读名义阻抗失败: {exc}')
|
||||
groups = {}
|
||||
if groups:
|
||||
for group in groups:
|
||||
try:
|
||||
nominal = [float(v) for v in list(info.K_q_nom[group])]
|
||||
robot.SetJointImpedance(group, nominal)
|
||||
log(f'恢复阻抗 {group} K={nominal}')
|
||||
except Exception as exc:
|
||||
log(f'恢复阻抗 {group}: {exc}')
|
||||
return
|
||||
try:
|
||||
robot.SetJointImpedance([350.0] * 7, [7.0] * 7)
|
||||
except Exception as exc:
|
||||
log(f'恢复阻抗: {exc}')
|
||||
|
||||
|
||||
def _send_joint_target(robot, flexivrdk, target: List[float], grouped: Optional[bool]) -> bool:
|
||||
"""下发 7 关节目标。返回是否走了分组接口。"""
|
||||
if grouped is True:
|
||||
_send_grouped_joint_target(robot, flexivrdk, target)
|
||||
return True
|
||||
try:
|
||||
n = len(target)
|
||||
robot.SendJointPosition(target, [0.0] * n, [1.5] * n, [2.0] * n)
|
||||
return False
|
||||
except Exception as exc:
|
||||
if grouped is False:
|
||||
raise
|
||||
log(f'扁平关节指令失败,改分组: {exc}')
|
||||
_send_grouped_joint_target(robot, flexivrdk, target)
|
||||
return True
|
||||
|
||||
|
||||
def _send_grouped_joint_target(robot, flexivrdk, target: List[float]) -> None:
|
||||
info = robot.info()
|
||||
groups = dict(getattr(info, 'single_arm_groups', None) or {})
|
||||
if not groups:
|
||||
raise RuntimeError('没有分组,扁平关节指令也失败了')
|
||||
states = robot.states()
|
||||
cmds = {}
|
||||
placed = False
|
||||
for group in groups:
|
||||
try:
|
||||
current = [float(v) for v in list(states[group].q)]
|
||||
except Exception:
|
||||
current = list(target)
|
||||
use = target if (not placed and len(current) == len(target)) else current
|
||||
if use is target:
|
||||
placed = True
|
||||
n = len(use)
|
||||
cmds[group] = flexivrdk.NrtJointPositionCmd(use, [0.0] * n, [1.5] * n, [2.0] * n)
|
||||
if not placed:
|
||||
raise RuntimeError('没有一组关节数和示教点一致,拒绝下发')
|
||||
robot.SendJointPosition(cmds)
|
||||
|
||||
|
||||
def _idle_async(robot, lock: threading.Lock, flexivrdk, timeout: float = 4.0) -> bool:
|
||||
"""切到 IDLE,同时避免把 stdin 循环永久阻塞住。"""
|
||||
done = threading.Event()
|
||||
@@ -487,65 +423,6 @@ def main() -> int:
|
||||
info['tool'] = ''
|
||||
reply(info)
|
||||
|
||||
elif cmd == 'move_joints':
|
||||
if robot is None:
|
||||
reply({'ok': False, 'error': '尚未连接'})
|
||||
continue
|
||||
raw = msg.get('q')
|
||||
if not isinstance(raw, list) or len(raw) < 7:
|
||||
reply({'ok': False, 'error': '需要 7 个关节角(弧度)'})
|
||||
continue
|
||||
try:
|
||||
target = [float(v) for v in raw[:7]]
|
||||
except (TypeError, ValueError):
|
||||
reply({'ok': False, 'error': '关节角不是数字'})
|
||||
continue
|
||||
if any(v != v or abs(v) > 12.6 for v in target):
|
||||
reply({'ok': False, 'error': '关节角不是有限值,或超出约两圈'})
|
||||
continue
|
||||
try:
|
||||
speed = float(msg.get('speed', 0.15))
|
||||
except (TypeError, ValueError):
|
||||
speed = 0.15
|
||||
speed = max(0.05, min(0.35, speed))
|
||||
if soft is not None:
|
||||
soft.stop()
|
||||
soft = None
|
||||
drag_on = False
|
||||
err = _ensure_operational(robot)
|
||||
if err:
|
||||
reply({'ok': False, 'error': err})
|
||||
continue
|
||||
try:
|
||||
with rlock:
|
||||
robot.SwitchMode(flexivrdk.Mode.NRT_JOINT_IMPEDANCE)
|
||||
current = [float(v) for v in list(robot.states().q)[:7]]
|
||||
_restore_tracking_impedance(robot, flexivrdk)
|
||||
max_delta = max(abs(a - b) for a, b in zip(current, target))
|
||||
step_s = 0.05
|
||||
steps = max(1, int(max_delta / (speed * step_s)))
|
||||
if steps * step_s > 150.0:
|
||||
reply({
|
||||
'ok': False,
|
||||
'error': f'路程太长({max_delta:.2f} rad),请先拖近一点再回放',
|
||||
})
|
||||
continue
|
||||
grouped: Optional[bool] = None
|
||||
for index in range(1, steps + 1):
|
||||
alpha = index / steps
|
||||
sample = [a + (b - a) * alpha for a, b in zip(current, target)]
|
||||
with rlock:
|
||||
grouped = _send_joint_target(robot, flexivrdk, sample, grouped)
|
||||
time.sleep(step_s)
|
||||
for _hold in range(8):
|
||||
with rlock:
|
||||
grouped = _send_joint_target(robot, flexivrdk, target, grouped)
|
||||
time.sleep(step_s)
|
||||
cached_q = list(target)
|
||||
reply({'ok': True, 'q': target, 'from': current, 'steps': steps, 'speed': speed})
|
||||
except Exception as exc:
|
||||
reply({'ok': False, 'error': f'回点失败: {exc}'})
|
||||
|
||||
elif cmd == 'quit':
|
||||
if soft is not None:
|
||||
soft.stop()
|
||||
|
||||
@@ -23,9 +23,7 @@ sys.path.insert(0, str(ROOT / "scripts" / "lib"))
|
||||
|
||||
from rdk_release import is_control_stack_cmd # noqa: E402
|
||||
|
||||
RDK_PYTHON = os.environ.get("RDK_PYTHON", "").strip() or str(
|
||||
Path.home() / "rdk_py310" / "bin" / "python"
|
||||
)
|
||||
RDK_PYTHON = os.environ.get("RDK_PYTHON", "/home/cc/rdk_py310/bin/python")
|
||||
|
||||
|
||||
def _procs() -> list[tuple[int, str]]:
|
||||
|
||||
+6
-14
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# 跟踪 → 对孔。搜孔轻压已取消,本脚本不再进入那一步,也不能用参数打开。
|
||||
# 三阶段装配流程的完整入口:一阶段跟踪 → 二阶段对孔 → 三阶段搜孔轻压。
|
||||
#
|
||||
# 为什么要单独有这个脚本:
|
||||
# pipeline.launch.py 只管两件事 —— 发布手眼静态 TF、起 assembly_pipeline 串联节点。
|
||||
@@ -13,10 +13,10 @@
|
||||
# 4) exec pipeline.launch.py,把下面的 CLI 参数映射成它的 launch 参数
|
||||
#
|
||||
# 用法:
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 # 跟踪 + 对孔
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 # 全流程
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --hole k2 # 指定对 k2 孔
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --skip-stage1 # 臂已在工件上方,跳过跟踪
|
||||
# 旋入不在这里。用 run_stage3_search.sh --arm 89 --screw
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --depth 6 --pitch 1.0
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --camera-preview # 相机带 Qt 预览窗
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --no-camera # 相机已在别处拉起了
|
||||
# bash ~/ros2/scripts/run_all.sh --arm 89 --no-tactile # 不起触觉服务
|
||||
@@ -37,7 +37,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# ---- 默认值:全部沿用 site.yaml / launch 默认,CLI 只做覆盖 ----
|
||||
SKIP_STAGE1=false
|
||||
SKIP_STAGE2=false
|
||||
SKIP_STAGE3=true # 搜孔已取消。--skip-stage3 仍然接受,但没有开关能把它重新打开。
|
||||
SKIP_STAGE3=false
|
||||
HOLE=""
|
||||
DEPTH=""
|
||||
PITCH=""
|
||||
@@ -83,12 +83,6 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "${DEPTH}" || -n "${PITCH}" ]]; then
|
||||
echo "ERROR: 搜孔轻压已取消,整体流程不再搜孔,也不在这里旋入。" >&2
|
||||
echo " 旋入请用: bash ~/ros2/scripts/run_stage3_search.sh --arm ${SITE_ROBOT_ARM:-89} --screw" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${FINAL_GAP}" && "${NO_DESCENT}" == true ]]; then
|
||||
echo "ERROR: 同时写了 --final-gap 和 --no-descent,不知道听谁的。" >&2
|
||||
exit 1
|
||||
@@ -145,9 +139,7 @@ if [[ "${D435_ONLY}" == true ]]; then
|
||||
fi
|
||||
echo "=== MoveL:XY 对准 K0,工具尖高度保持当前(K0 的 z 是工件表面)==="
|
||||
# --go:不按直线距离拦截。800 mm 是脚本自己的保护,不是机械臂限位。
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/lib/dev_tools.sh"
|
||||
exec "$(dev_rdk_python)" "${ROOT}/scripts/rdk_goto_xyz.py" \
|
||||
exec /home/cc/rdk_py310/bin/python "${ROOT}/scripts/rdk_goto_xyz.py" \
|
||||
--xyz "${kx}" "${ky}" "${kz}" --keep-z --go --vel 0.05
|
||||
fi
|
||||
|
||||
@@ -247,7 +239,7 @@ fi
|
||||
# 每次 echo 吃满超时 × 60 次 ≈ 3 分钟,终端上没有任何新行(2026-10-09 真机)。
|
||||
_SKIP_TACTILE_REASON=""
|
||||
if [[ "${SKIP_STAGE3}" == true && "${WITH_TACTILE}" == true ]]; then
|
||||
echo "=== 搜孔已取消:跟踪和对孔不用触觉,跳过拉起 ==="
|
||||
echo "=== --skip-stage3:一/二阶段不用触觉,跳过拉起 ==="
|
||||
WITH_TACTILE=false
|
||||
_SKIP_TACTILE_REASON="stage3"
|
||||
fi
|
||||
|
||||
@@ -31,8 +31,6 @@ done
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/env.sh"
|
||||
# 用源码里的采集逻辑,不走 install 里那份旧的。install 的入口仍由 ros2 run 找到。
|
||||
export PYTHONPATH="${ROOT}/src/arm_application${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/lib/load_site_config.sh"
|
||||
|
||||
@@ -129,14 +127,12 @@ DO_PREVIEW="${SITE_CAM_PREVIEW:-1}"
|
||||
if [[ "${PREVIEW}" -ge 0 ]]; then
|
||||
DO_PREVIEW="${PREVIEW}"
|
||||
fi
|
||||
# 程序界面只看彩色和深度。红外是旧的四窗格查看器才要的。
|
||||
# 彩色要等红外齐帧时,场景一动,整组帧对不齐,彩色就被丢掉。
|
||||
if [[ "${DO_PREVIEW}" == "1" && "${SITE_CAM_ENABLE_INFRA:-1}" == "1" ]]; then
|
||||
if [[ "${SITE_CAM_ENABLE_INFRA:-1}" == "1" ]]; then
|
||||
CAM_ARGS+=(-p enable_infra:=true)
|
||||
else
|
||||
CAM_ARGS+=(-p enable_infra:=false)
|
||||
fi
|
||||
if [[ "${DO_PREVIEW}" == "1" && "${SITE_CAM_PREVIEW_STEREO:-1}" == "1" ]]; then
|
||||
if [[ "${SITE_CAM_PREVIEW_STEREO:-1}" == "1" ]]; then
|
||||
CAM_ARGS+=(-p preview_stereo:=true)
|
||||
else
|
||||
CAM_ARGS+=(-p preview_stereo:=false)
|
||||
|
||||
@@ -17,8 +17,6 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/env.sh"
|
||||
# 用源码里的采集逻辑,不走 install 里那份旧的。install 的入口仍由 ros2 run 找到。
|
||||
export PYTHONPATH="${ROOT}/src/arm_application${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/lib/load_site_config.sh"
|
||||
SN="${SITE_D435_SERIAL:-}"
|
||||
|
||||
+4
-11
@@ -19,22 +19,15 @@ while [[ $# -gt 0 ]]; do
|
||||
--list|-l) LIST_ONLY=true; shift ;;
|
||||
-h|--help)
|
||||
sed -n '2,8p' "$0"
|
||||
echo " 机械臂模型在工位程序里看,这里默认不开 RViz。"
|
||||
echo " 其余参数传给 ros2 launch。要临时打开 RViz:use_rviz:=true"
|
||||
echo " Extra args are passed to ros2 launch (e.g. use_rviz:=false)."
|
||||
exit 0
|
||||
;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/lib/dev_tools.sh"
|
||||
dev_source_conda
|
||||
source /home/cc/anaconda3/etc/profile.d/conda.sh
|
||||
conda activate ros_flexiv
|
||||
if [[ ! -f "${ROOT}/install/setup.bash" ]]; then
|
||||
echo "ERROR: ${ROOT}/install/setup.bash 不存在。先在仓库根目录 colcon build,不要在中文路径下编。" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/install/setup.bash"
|
||||
# shellcheck disable=SC1091
|
||||
@@ -51,7 +44,7 @@ export LD_LIBRARY_PATH="${FLEXIV_PREFIX}/lib:${LD_LIBRARY_PATH:-}"
|
||||
ROBOT_SN="${SITE_ROBOT_SN:?run_real.sh 需要已配置的机械臂 SN —— 见 scripts/lib/load_site_config.sh 的报错}"
|
||||
ROBOT_IP="${ROBOT_IP:-${SITE_ROBOT_IP:-192.168.100.1}}"
|
||||
NET_IFACE="${SITE_NET_IFACE:-}"
|
||||
RDK_PYTHON="$(dev_rdk_python)"
|
||||
RDK_PYTHON="${RDK_PYTHON:-/home/cc/rdk_py310/bin/python}"
|
||||
|
||||
if [[ "${LIST_ONLY}" == true ]]; then
|
||||
python3 "${ROOT}/scripts/lib/export_site_env.py" --print
|
||||
@@ -128,4 +121,4 @@ if ! "${RDK_PYTHON}" "${ROOT}/scripts/rdk_switch_tool.py" "${RDK_TOOL_ARGS[@]}";
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec ros2 launch arm_bringup stack.launch.py backend:=real robot_sn:="${ROBOT_SN}" use_rviz:=false "$@"
|
||||
exec ros2 launch arm_bringup stack.launch.py backend:=real robot_sn:="${ROBOT_SN}" "$@"
|
||||
|
||||
Reference in New Issue
Block a user