增加机器人奇异点检测

This commit is contained in:
zhangshun
2026-06-16 12:56:26 +08:00
parent a100e44ac0
commit 0c7dc9e05b
8 changed files with 327 additions and 15 deletions

View File

@@ -133,6 +133,7 @@ $compileResult = emcc `
-s USE_PTHREADS=0 `
-O3 `
-Wno-deprecated-literal-operator `
-Wno-deprecated-declarations `
-o public/wasm/smart_math.js 2>&1
$endTime = Get-Date

View File

@@ -142,6 +142,19 @@ function buildInverseRequest(command, robotUuid, poseStr, qInitStr, extra = {})
};
}
function buildSingularityRequest(robotUuid, joints) {
return {
msg: "singularity check",
req_code: "AUTO_SINGULARITY",
req_from: "wasm_test",
req_cmd: "Cmd_Kinematics_check_singularity",
req_param: {
robot_uuid: robotUuid,
joints_str: jointArrayToString(joints),
},
};
}
// 构造 link 名称被整体替换的 URDF用于验证初始化能自动推导运动学链。
function buildRenamedUrdfFromDocs() {
const urdfPath = path.join(rootDir, "docs", "urdf.xml");
@@ -195,6 +208,25 @@ async function runJointLimitCase(module) {
return response;
}
// 检查奇异点分析接口会返回完整的雅可比 SVD 指标。
async function runSingularityCheckCase(module) {
const response = callBusinessApi(module, buildSingularityRequest("abb_irb120_3_58", [0.15, -0.25, 0.35, 0.1, -0.2, 0.3]));
ensure(response.success === true, "singularity check request failed");
ensure(getByPath(response, "res_data.success") === true, "singularity check result is not successful");
ensure(Array.isArray(getByPath(response, "res_data.singular_values")), "singular values are missing");
ensure(getByPath(response, "res_data.singular_values").length === 6, "singular value count should be 6");
ensure(typeof getByPath(response, "res_data.condition_number") === "number", "condition number is missing");
ensure(typeof getByPath(response, "res_data.risk_level") === "string", "risk level is missing");
return {
details: {
riskLevel: getByPath(response, "res_data.risk_level"),
rank: getByPath(response, "res_data.rank"),
minSingularValue: getByPath(response, "res_data.min_singular_value"),
conditionNumber: getByPath(response, "res_data.condition_number"),
},
};
}
function assertStaticCase(response, assertions) {
for (const assertion of assertions) {
const actual = getByPath(response, assertion.path);
@@ -414,6 +446,10 @@ const suite = [
id: "kinematics_joint_limit_rejects_fk",
type: "joint_limit",
},
{
id: "kinematics_singularity_check",
type: "singularity_check",
},
{
id: "spc_basic_5x30",
type: "static",
@@ -587,6 +623,8 @@ async function runCase(module, testCase) {
return runRenamedUrdfChainCase(module, testCase);
case "joint_limit":
return runJointLimitCase(module, testCase);
case "singularity_check":
return runSingularityCheckCase(module, testCase);
default:
throw new Error(`Unknown test type: ${testCase.type}`);
}