新增曲柄滑块批量仿真接口
This commit is contained in:
@@ -406,6 +406,44 @@ func
|
||||
- 命令层负责把参数取出来、调机制对象、再把结果组装成 JSON
|
||||
- 真正的机构计算在 `src/FourBarMechanism/CrankSliderMechanism.cpp`
|
||||
|
||||
### 6.2 `Cmd_FourBar_CrankSlider_Simulate`
|
||||
|
||||
用途:
|
||||
|
||||
- 输入曲柄滑块初始参数、起始角度、结束角度、总时长和步长时间
|
||||
- 一次性生成整段仿真帧数据,适合前端播放、导出轨迹和离线验证
|
||||
|
||||
调用履历:
|
||||
|
||||
```text
|
||||
func
|
||||
-> dispatchCommand
|
||||
-> handleFourBarCommand
|
||||
-> createCrankSliderMechanism
|
||||
-> CrankSliderMechanism::setL_AB / setL_BS / setS_OFS
|
||||
-> CrankSliderMechanism::validateParameters
|
||||
-> for each frame:
|
||||
-> CrankSliderMechanism::calculate(currentAngleDeg)
|
||||
-> CrankSliderMechanism::getTrajectoryPoints
|
||||
-> CrankSliderMechanism::getSliderTrajectory
|
||||
```
|
||||
|
||||
主要入参:
|
||||
|
||||
- `L_AB`:曲柄长度
|
||||
- `L_BS`:连杆长度
|
||||
- `S_OFS`:滑块偏移
|
||||
- `startAngleDeg`:起始曲柄角度
|
||||
- `endAngleDeg`:结束曲柄角度
|
||||
- `duration`:仿真总时长,单位秒
|
||||
- `stepTime`:仿真步长时间,单位秒
|
||||
|
||||
主要出参:
|
||||
|
||||
- `frames[]`:逐帧仿真数据,每帧包含 `frame`、`time`、`angleDeg`、`points`、`poses`、`angles`
|
||||
- `trajectory`:B 点整段轨迹
|
||||
- `slider_trajectory`:S 点整段滑块轨迹
|
||||
|
||||
## 7. 四足机器人接口履历
|
||||
|
||||
命令文件:
|
||||
@@ -596,4 +634,3 @@ smart_test_match
|
||||
1. `src/api/KinematicsWebAPI.SpcCommands.cpp`
|
||||
2. `src/spc_core.cpp`
|
||||
3. `include/spc_core.h`
|
||||
|
||||
|
||||
@@ -579,20 +579,24 @@
|
||||
<input id="angleInput" type="number" step="1" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="stepInput">扫描步长</label>
|
||||
<input id="stepInput" type="number" min="1" max="90" step="1" />
|
||||
<label for="stepInput">stepTime 步长时间</label>
|
||||
<input id="stepInput" type="number" min="0.01" max="1" step="0.01" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="scanEndInput">扫描终止角</label>
|
||||
<input id="scanEndInput" type="number" min="1" max="720" step="1" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="durationInput">duration 总时长</label>
|
||||
<input id="durationInput" type="number" min="0.02" max="60" step="0.01" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="secondary" id="validButton" type="button">有效样例</button>
|
||||
<button class="secondary" id="invalidButton" type="button">异常样例</button>
|
||||
<button class="primary" id="runButton" type="button">调用接口</button>
|
||||
<button class="primary" id="scanButton" type="button">角度扫描</button>
|
||||
<button class="primary" id="scanButton" type="button">仿真扫描</button>
|
||||
</div>
|
||||
<div class="message" id="inputMessage" aria-live="polite"></div>
|
||||
|
||||
@@ -756,6 +760,7 @@
|
||||
const angleInput = document.getElementById("angleInput");
|
||||
const stepInput = document.getElementById("stepInput");
|
||||
const scanEndInput = document.getElementById("scanEndInput");
|
||||
const durationInput = document.getElementById("durationInput");
|
||||
const requestOutput = document.getElementById("requestOutput");
|
||||
const topSuccess = document.getElementById("topSuccess");
|
||||
const businessSuccess = document.getElementById("businessSuccess");
|
||||
@@ -813,6 +818,25 @@
|
||||
};
|
||||
}
|
||||
|
||||
// 按批量仿真接口格式组装曲柄滑块请求。
|
||||
function buildSimulationRequest(input, sim, reqCode = `FOURBAR_SIM_${Date.now()}`) {
|
||||
return {
|
||||
msg: "fourbar crank slider simulate",
|
||||
req_code: reqCode,
|
||||
req_from: "fourbar_test_page",
|
||||
req_cmd: "Cmd_FourBar_CrankSlider_Simulate",
|
||||
req_param: {
|
||||
L_AB: input.L_AB,
|
||||
L_BS: input.L_BS,
|
||||
S_OFS: input.S_OFS,
|
||||
startAngleDeg: input.angleDeg,
|
||||
endAngleDeg: sim.endAngleDeg,
|
||||
duration: sim.duration,
|
||||
stepTime: sim.stepTime,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 将当前表单同步到请求 JSON 文本框。
|
||||
function refreshRequestPreview() {
|
||||
try {
|
||||
@@ -828,8 +852,9 @@
|
||||
lBsInput.value = sample.L_BS;
|
||||
sOfsInput.value = sample.S_OFS;
|
||||
angleInput.value = sample.angleDeg;
|
||||
stepInput.value = stepInput.value || "10";
|
||||
stepInput.value = stepInput.value || "0.02";
|
||||
scanEndInput.value = scanEndInput.value || "360";
|
||||
durationInput.value = durationInput.value || "2";
|
||||
refreshRequestPreview();
|
||||
}
|
||||
|
||||
@@ -1116,6 +1141,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 调用曲柄滑块批量仿真接口并解析 JSON 响应。
|
||||
async function callFourBarSimulation(input, sim) {
|
||||
const module = await ensureWasm();
|
||||
const payload = JSON.stringify(buildSimulationRequest(input, sim));
|
||||
let requestPtr = 0;
|
||||
let responsePtr = 0;
|
||||
try {
|
||||
requestPtr = allocString(module, payload);
|
||||
responsePtr = module._func(requestPtr);
|
||||
return JSON.parse(module.UTF8ToString(responsePtr));
|
||||
} finally {
|
||||
if (responsePtr) module._smart_free_string(responsePtr);
|
||||
if (requestPtr) module._free(requestPtr);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算前端几何校验值,用于和 WASM 返回点位对照。
|
||||
function calculateLocalGeometry(input) {
|
||||
const angle = (input.angleDeg * Math.PI) / 180;
|
||||
@@ -1419,43 +1460,52 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 多角度调用接口,生成轨迹和位移曲线。
|
||||
// 调用批量仿真接口,生成轨迹和位移曲线。
|
||||
async function runScan() {
|
||||
try {
|
||||
setMessage("");
|
||||
const baseInput = readInput();
|
||||
const step = Number(stepInput.value);
|
||||
const stepTime = Number(stepInput.value);
|
||||
const scanEnd = Number(scanEndInput.value);
|
||||
if (!Number.isInteger(step) || step < 1 || step > 90) {
|
||||
throw new Error("扫描步长必须是 1 到 90 之间的整数");
|
||||
const duration = Number(durationInput.value);
|
||||
if (!Number.isFinite(stepTime) || stepTime <= 0 || stepTime > 1) {
|
||||
throw new Error("stepTime 必须是 0 到 1 秒之间的有效数字");
|
||||
}
|
||||
if (!Number.isFinite(scanEnd) || scanEnd <= 0 || scanEnd > 720) {
|
||||
throw new Error("扫描终止角必须在 1 到 720 之间");
|
||||
}
|
||||
if (!Number.isFinite(duration) || duration <= 0 || duration > 60) {
|
||||
throw new Error("duration 必须在 0 到 60 秒之间");
|
||||
}
|
||||
|
||||
requestStatus.textContent = "扫描中";
|
||||
requestStatus.className = "badge warn";
|
||||
const rows = [];
|
||||
for (let angle = 0; angle <= scanEnd + 0.000001; angle += step) {
|
||||
const input = { ...baseInput, angleDeg: angle };
|
||||
const response = await callFourBar(input, `FOURBAR_SCAN_${angle}`);
|
||||
const data = response.res_data || {};
|
||||
rows.push({
|
||||
angle,
|
||||
ok: response.success === true && data.success === true && !data.error,
|
||||
B: data.points?.B,
|
||||
S: data.points?.S,
|
||||
error: data.error || response.msg || "",
|
||||
});
|
||||
const response = await callFourBarSimulation(baseInput, {
|
||||
endAngleDeg: scanEnd,
|
||||
duration,
|
||||
stepTime,
|
||||
});
|
||||
const data = response.res_data || {};
|
||||
if (response.success !== true || data.success !== true || data.error) {
|
||||
throw new Error(data.error || response.msg || "批量仿真接口返回失败");
|
||||
}
|
||||
const rows = (data.frames || []).map((frame) => ({
|
||||
frame: frame.frame,
|
||||
time: frame.time,
|
||||
angle: frame.angleDeg,
|
||||
ok: Boolean(frame.points?.B && frame.points?.S),
|
||||
B: frame.points?.B,
|
||||
S: frame.points?.S,
|
||||
error: "",
|
||||
}));
|
||||
|
||||
latestScan = rows;
|
||||
scanOutput.textContent = JSON.stringify(rows, null, 2);
|
||||
scanOutput.textContent = JSON.stringify(response, null, 2);
|
||||
renderScanCharts(rows);
|
||||
scanAnimation.frame = 0;
|
||||
requestStatus.textContent = "扫描完成";
|
||||
requestStatus.className = "badge ok";
|
||||
setMessage(`扫描完成:${rows.filter((row) => row.ok).length}/${rows.length} 个角度有效`, "is-ok");
|
||||
setMessage(`仿真完成:${rows.filter((row) => row.ok).length}/${rows.length} 帧有效`, "is-ok");
|
||||
} catch (error) {
|
||||
requestStatus.textContent = "扫描失败";
|
||||
requestStatus.className = "badge error";
|
||||
@@ -1472,7 +1522,7 @@
|
||||
scanOutput.classList.toggle("hidden", isResponse);
|
||||
}
|
||||
|
||||
[lAbInput, lBsInput, sOfsInput, angleInput].forEach((input) => {
|
||||
[lAbInput, lBsInput, sOfsInput, angleInput, stepInput, scanEndInput, durationInput].forEach((input) => {
|
||||
input.addEventListener("input", refreshRequestPreview);
|
||||
});
|
||||
|
||||
@@ -1495,8 +1545,9 @@
|
||||
scanTab.addEventListener("click", () => setActiveTab("scan"));
|
||||
|
||||
loadSample(validSample);
|
||||
stepInput.value = "10";
|
||||
stepInput.value = "0.02";
|
||||
scanEndInput.value = "360";
|
||||
durationInput.value = "2";
|
||||
refreshRequestPreview();
|
||||
renderThreeMechanism(calculateLocalGeometry(validSample), validSample, {
|
||||
title: "有效样例预览",
|
||||
|
||||
@@ -401,6 +401,23 @@ const suite = [
|
||||
{ path: "res_data.validation_errors", lengthGte: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "fourbar_simulate",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/fourbar/crank_slider_simulate.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: true },
|
||||
{ path: "res_data.frame_count", equals: 5 },
|
||||
{ path: "res_data.frames", lengthEquals: 5 },
|
||||
{ path: "res_data.frames.0.angleDeg", equals: 0 },
|
||||
{ path: "res_data.frames.4.angleDeg", equals: 180 },
|
||||
{ path: "res_data.frames.0.points.B.x", exists: true },
|
||||
{ path: "res_data.frames.0.poses.TCP.tx", exists: true },
|
||||
{ path: "res_data.trajectory", lengthEquals: 5 },
|
||||
{ path: "res_data.slider_trajectory", lengthEquals: 5 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "quadruped_points_from_motor_angles",
|
||||
type: "static",
|
||||
|
||||
@@ -122,7 +122,8 @@ json KinematicsWebAPI::dispatchCommand(const std::string &req_cmd, const json &r
|
||||
return handleSpcCommand(req_cmd, req_param);
|
||||
}
|
||||
|
||||
if (req_cmd == "Cmd_FourBar_CrankSlider")
|
||||
if (req_cmd == "Cmd_FourBar_CrankSlider" ||
|
||||
req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
|
||||
{
|
||||
return handleFourBarCommand(req_cmd, req_param);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
#include "KinematicsWebAPI.h"
|
||||
#include "FourBarMechanism/CrankSliderMechanism.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace
|
||||
{
|
||||
// 将机构点位转换为接口统一 JSON。
|
||||
json buildPointsJson(const MechanismState &state)
|
||||
{
|
||||
json points_json;
|
||||
for (const auto &point_pair : state.Points)
|
||||
{
|
||||
points_json[point_pair.first] = {
|
||||
{"x", point_pair.second.X},
|
||||
{"y", point_pair.second.Y}};
|
||||
}
|
||||
return points_json;
|
||||
}
|
||||
|
||||
// 将机构姿态转换为接口统一 JSON。
|
||||
json buildPosesJson(const MechanismState &state)
|
||||
{
|
||||
json poses_json;
|
||||
for (const auto &pose_pair : state.Poses)
|
||||
{
|
||||
poses_json[pose_pair.first] = {
|
||||
{"tx", pose_pair.second.tx},
|
||||
{"ty", pose_pair.second.ty},
|
||||
{"tz", pose_pair.second.tz},
|
||||
{"qx", pose_pair.second.qx},
|
||||
{"qy", pose_pair.second.qy},
|
||||
{"qz", pose_pair.second.qz},
|
||||
{"qw", pose_pair.second.qw}};
|
||||
}
|
||||
return poses_json;
|
||||
}
|
||||
|
||||
// 将机构角度数据转换为接口统一 JSON。
|
||||
json buildAnglesJson(const MechanismState &state)
|
||||
{
|
||||
json angles_json;
|
||||
for (const auto &angle_pair : state.Angles)
|
||||
{
|
||||
angles_json[angle_pair.first] = angle_pair.second;
|
||||
}
|
||||
return angles_json;
|
||||
}
|
||||
|
||||
// 将二维点列表转换为轨迹 JSON 数组。
|
||||
json buildTrajectoryJson(const std::vector<Vector2D> &points)
|
||||
{
|
||||
json trajectory_json = json::array();
|
||||
for (const auto &point : points)
|
||||
{
|
||||
trajectory_json.push_back({
|
||||
{"x", point.X},
|
||||
{"y", point.Y}});
|
||||
}
|
||||
return trajectory_json;
|
||||
}
|
||||
|
||||
// 组装单帧机构状态,供实时接口和批量仿真接口复用。
|
||||
json buildFrameJson(const MechanismState &state, int frame, double time, double angleDeg)
|
||||
{
|
||||
json frame_json;
|
||||
frame_json["frame"] = frame;
|
||||
frame_json["time"] = time;
|
||||
frame_json["angleDeg"] = angleDeg;
|
||||
frame_json["input_value"] = state.InputValue;
|
||||
frame_json["points"] = buildPointsJson(state);
|
||||
frame_json["poses"] = buildPosesJson(state);
|
||||
frame_json["angles"] = buildAnglesJson(state);
|
||||
if (state.hasWarning())
|
||||
{
|
||||
frame_json["warning"] = state.WarningMessage;
|
||||
}
|
||||
return frame_json;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const json &req_param)
|
||||
{
|
||||
@@ -11,11 +88,19 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
double L_BS = req_param.value("L_BS", 2.0);
|
||||
double S_OFS = req_param.value("S_OFS", 0.0);
|
||||
double angleDeg = req_param.value("angleDeg", 0.0);
|
||||
double startAngleDeg = req_param.value("startAngleDeg", 0.0);
|
||||
double endAngleDeg = req_param.value("endAngleDeg", 360.0);
|
||||
double duration = req_param.value("duration", 2.0);
|
||||
double stepTime = req_param.value("stepTime", 0.02);
|
||||
|
||||
log("Parameters: L_AB=" + std::to_string(L_AB) +
|
||||
", L_BS=" + std::to_string(L_BS) +
|
||||
", S_OFS=" + std::to_string(S_OFS) +
|
||||
", angleDeg=" + std::to_string(angleDeg));
|
||||
", angleDeg=" + std::to_string(angleDeg) +
|
||||
", startAngleDeg=" + std::to_string(startAngleDeg) +
|
||||
", endAngleDeg=" + std::to_string(endAngleDeg) +
|
||||
", duration=" + std::to_string(duration) +
|
||||
", stepTime=" + std::to_string(stepTime));
|
||||
|
||||
std::unique_ptr<CrankSliderMechanism> mechanism(createCrankSliderMechanism());
|
||||
mechanism->setL_AB(L_AB);
|
||||
@@ -32,6 +117,78 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
{"validation_warnings", validation.Warnings}};
|
||||
}
|
||||
|
||||
if (req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
|
||||
{
|
||||
if (duration <= 0.0)
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", "Invalid simulation parameters"},
|
||||
{"validation_errors", json::array({"duration 必须大于 0"})},
|
||||
{"validation_warnings", json::array()}};
|
||||
}
|
||||
|
||||
if (stepTime <= 0.0 || stepTime > duration)
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", "Invalid simulation parameters"},
|
||||
{"validation_errors", json::array({"stepTime 必须大于 0 且不能大于 duration"})},
|
||||
{"validation_warnings", json::array()}};
|
||||
}
|
||||
|
||||
const int frameCount = static_cast<int>(std::ceil(duration / stepTime)) + 1;
|
||||
if (frameCount < 2 || frameCount > 1000)
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", "Invalid simulation parameters"},
|
||||
{"validation_errors", json::array({"仿真帧数必须在 2 到 1000 之间"})},
|
||||
{"validation_warnings", json::array()}};
|
||||
}
|
||||
|
||||
json frames_json = json::array();
|
||||
for (int frame = 0; frame < frameCount; ++frame)
|
||||
{
|
||||
const double currentTime = frame == frameCount - 1 ? duration : stepTime * frame;
|
||||
const double progress = duration <= 0.0 ? 0.0 : currentTime / duration;
|
||||
const double currentAngleDeg = startAngleDeg + (endAngleDeg - startAngleDeg) * progress;
|
||||
MechanismState state = mechanism->calculate(currentAngleDeg);
|
||||
if (state.hasError())
|
||||
{
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", state.ErrorMessage},
|
||||
{"failed_frame", frame},
|
||||
{"failed_time", currentTime},
|
||||
{"failed_angleDeg", currentAngleDeg}};
|
||||
}
|
||||
|
||||
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentAngleDeg));
|
||||
}
|
||||
|
||||
json result;
|
||||
result["success"] = true;
|
||||
result["frames"] = frames_json;
|
||||
result["frame_count"] = frameCount;
|
||||
result["duration"] = duration;
|
||||
result["stepTime"] = stepTime;
|
||||
result["startAngleDeg"] = startAngleDeg;
|
||||
result["endAngleDeg"] = endAngleDeg;
|
||||
result["trajectory"] = buildTrajectoryJson(mechanism->getTrajectoryPoints());
|
||||
result["slider_trajectory"] = buildTrajectoryJson(mechanism->getSliderTrajectory());
|
||||
result["parameters"] = {
|
||||
{"L_AB", L_AB},
|
||||
{"L_BS", L_BS},
|
||||
{"S_OFS", S_OFS},
|
||||
{"startAngleDeg", startAngleDeg},
|
||||
{"endAngleDeg", endAngleDeg},
|
||||
{"duration", duration},
|
||||
{"stepTime", stepTime}};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MechanismState state = mechanism->calculate(angleDeg);
|
||||
if (state.hasError())
|
||||
{
|
||||
@@ -43,35 +200,9 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
json result;
|
||||
result["success"] = true;
|
||||
|
||||
json points_json;
|
||||
for (const auto &point_pair : state.Points)
|
||||
{
|
||||
points_json[point_pair.first] = {
|
||||
{"x", point_pair.second.X},
|
||||
{"y", point_pair.second.Y}};
|
||||
}
|
||||
result["points"] = points_json;
|
||||
|
||||
json poses_json;
|
||||
for (const auto &pose_pair : state.Poses)
|
||||
{
|
||||
poses_json[pose_pair.first] = {
|
||||
{"tx", pose_pair.second.tx},
|
||||
{"ty", pose_pair.second.ty},
|
||||
{"tz", pose_pair.second.tz},
|
||||
{"qx", pose_pair.second.qx},
|
||||
{"qy", pose_pair.second.qy},
|
||||
{"qz", pose_pair.second.qz},
|
||||
{"qw", pose_pair.second.qw}};
|
||||
}
|
||||
result["poses"] = poses_json;
|
||||
|
||||
json angles_json;
|
||||
for (const auto &angle_pair : state.Angles)
|
||||
{
|
||||
angles_json[angle_pair.first] = angle_pair.second;
|
||||
}
|
||||
result["angles"] = angles_json;
|
||||
result["points"] = buildPointsJson(state);
|
||||
result["poses"] = buildPosesJson(state);
|
||||
result["angles"] = buildAnglesJson(state);
|
||||
|
||||
result["input_value"] = state.InputValue;
|
||||
if (state.hasWarning())
|
||||
@@ -79,23 +210,8 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
|
||||
result["warning"] = state.WarningMessage;
|
||||
}
|
||||
|
||||
json trajectory_json = json::array();
|
||||
for (const auto &point : mechanism->getTrajectoryPoints())
|
||||
{
|
||||
trajectory_json.push_back({
|
||||
{"x", point.X},
|
||||
{"y", point.Y}});
|
||||
}
|
||||
result["trajectory"] = trajectory_json;
|
||||
|
||||
json slider_trajectory_json = json::array();
|
||||
for (const auto &point : mechanism->getSliderTrajectory())
|
||||
{
|
||||
slider_trajectory_json.push_back({
|
||||
{"x", point.X},
|
||||
{"y", point.Y}});
|
||||
}
|
||||
result["slider_trajectory"] = slider_trajectory_json;
|
||||
result["trajectory"] = buildTrajectoryJson(mechanism->getTrajectoryPoints());
|
||||
result["slider_trajectory"] = buildTrajectoryJson(mechanism->getSliderTrajectory());
|
||||
|
||||
result["parameters"] = {
|
||||
{"L_AB", L_AB},
|
||||
|
||||
15
tests/testdata/fourbar/crank_slider_simulate.json
vendored
Normal file
15
tests/testdata/fourbar/crank_slider_simulate.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"msg": "fourbar simulate",
|
||||
"req_code": "CASE_FB_SIM_001",
|
||||
"req_from": "wasm_test",
|
||||
"req_cmd": "Cmd_FourBar_CrankSlider_Simulate",
|
||||
"req_param": {
|
||||
"L_AB": 0.5,
|
||||
"L_BS": 2.0,
|
||||
"S_OFS": 0.0,
|
||||
"startAngleDeg": 0.0,
|
||||
"endAngleDeg": 180.0,
|
||||
"duration": 1.0,
|
||||
"stepTime": 0.25
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user