新增四类连杆统一仿真接口

This commit is contained in:
zhangshun
2026-06-16 11:40:58 +08:00
parent a66952e636
commit f142b42dbe
9 changed files with 940 additions and 248 deletions

View File

@@ -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_Simulate" ||
req_cmd == "Cmd_FourBar_CrankSlider" ||
req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
{
return handleFourBarCommand(req_cmd, req_param);

View File

@@ -1,9 +1,45 @@
#include "KinematicsWebAPI.h"
#include "FourBarMechanism/CrankSliderMechanism.h"
#include "FourBarMechanism/CrankRockingBlockMechanism_Forward.h"
#include "FourBarMechanism/FourBarMechanism.h"
#include "FourBarMechanism/SliderCrankMechanism.h"
#include <cmath>
#include <memory>
namespace
{
// 读取兼容字段,便于新统一接口沿用旧曲柄滑块参数名。
double valueAny(const json &param, std::initializer_list<const char *> keys, double defaultValue)
{
for (const char *key : keys)
{
if (param.contains(key) && param[key].is_number())
{
return param[key].get<double>();
}
}
return defaultValue;
}
// 构造统一的业务失败返回,供四类连杆仿真复用。
json buildErrorJson(const std::string &error, const json &validationErrors = json::array(), const json &validationWarnings = json::array())
{
json result = {
{"success", false},
{"error", error}};
if (!validationErrors.empty())
{
result["validation_errors"] = validationErrors;
}
if (!validationWarnings.empty())
{
result["validation_warnings"] = validationWarnings;
}
return result;
}
// 将机构点位转换为接口统一 JSON。
json buildPointsJson(const MechanismState &state)
{
@@ -60,12 +96,14 @@ json buildTrajectoryJson(const std::vector<Vector2D> &points)
}
// 组装单帧机构状态,供实时接口和批量仿真接口复用。
json buildFrameJson(const MechanismState &state, int frame, double time, double angleDeg)
json buildFrameJson(const MechanismState &state, int frame, double time, double inputValue, const std::string &inputName)
{
json frame_json;
frame_json["frame"] = frame;
frame_json["time"] = time;
frame_json["angleDeg"] = angleDeg;
frame_json[inputName] = inputValue;
frame_json["inputName"] = inputName;
frame_json["inputValue"] = inputValue;
frame_json["input_value"] = state.InputValue;
frame_json["points"] = buildPointsJson(state);
frame_json["poses"] = buildPosesJson(state);
@@ -76,6 +114,260 @@ json buildFrameJson(const MechanismState &state, int frame, double time, double
}
return frame_json;
}
// 校验仿真时间参数并返回帧数。
json validateSimulationTimeline(double duration, double stepTime, int &frameCount)
{
if (duration <= 0.0)
{
return buildErrorJson("Invalid simulation parameters", json::array({"duration 必须大于 0"}));
}
if (stepTime <= 0.0 || stepTime > duration)
{
return buildErrorJson("Invalid simulation parameters", json::array({"stepTime 必须大于 0 且不能大于 duration"}));
}
frameCount = static_cast<int>(std::ceil(duration / stepTime)) + 1;
if (frameCount < 2 || frameCount > 1000)
{
return buildErrorJson("Invalid simulation parameters", json::array({"仿真帧数必须在 2 到 1000 之间"}));
}
return json();
}
// 将 ValidationResult 转换为统一业务错误。
json validateOrError(const ValidationResult &validation)
{
if (validation.isValid())
{
return json();
}
return buildErrorJson("Invalid parameters", validation.Errors, validation.Warnings);
}
// 按统一格式生成逐帧仿真数据。
template <typename CalculateFunc>
json buildSimulationFrames(int frameCount, double duration, double stepTime, double startValue, double endValue, const std::string &inputName, CalculateFunc calculate)
{
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 currentValue = startValue + (endValue - startValue) * progress;
MechanismState state = calculate(currentValue);
if (state.hasError())
{
return {
{"success", false},
{"error", state.ErrorMessage},
{"failed_frame", frame},
{"failed_time", currentTime},
{"failed_input", currentValue},
{"failed_input_name", inputName}};
}
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentValue, inputName));
}
return frames_json;
}
// 统一四连杆仿真接口,覆盖 PDPS 的 PRRR/RPRR/RRRP/RRRR 四类结构。
json simulateUnifiedFourBar(const json &req_param)
{
const std::string mechanismType = req_param.value("mechanismType", "RRRP");
const double duration = req_param.value("duration", 2.0);
const double stepTime = req_param.value("stepTime", 0.02);
int frameCount = 0;
json timelineError = validateSimulationTimeline(duration, stepTime, frameCount);
if (!timelineError.is_null())
{
return timelineError;
}
if (mechanismType == "RRRP")
{
const double L_AB = req_param.value("L_AB", 0.5);
const double L_BS = req_param.value("L_BS", 2.0);
const double S_OFS = req_param.value("S_OFS", 0.0);
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 360.0);
std::unique_ptr<CrankSliderMechanism> mechanism(createCrankSliderMechanism());
mechanism->setL_AB(L_AB);
mechanism->setL_BS(L_BS);
mechanism->setS_OFS(S_OFS);
json validationError = validateOrError(mechanism->validateParameters());
if (!validationError.is_null())
{
return validationError;
}
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
return mechanism->calculate(value);
});
if (!frames_json.is_array())
{
return frames_json;
}
return {
{"success", true},
{"mechanismType", mechanismType},
{"inputName", "angleDeg"},
{"frames", frames_json},
{"frame_count", frameCount},
{"duration", duration},
{"stepTime", stepTime},
{"startValue", startAngleDeg},
{"endValue", endAngleDeg},
{"startAngleDeg", startAngleDeg},
{"endAngleDeg", endAngleDeg},
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
{"slider_trajectory", buildTrajectoryJson(mechanism->getSliderTrajectory())},
{"parameters", {{"mechanismType", mechanismType}, {"L_AB", L_AB}, {"L_BS", L_BS}, {"S_OFS", S_OFS}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
}
if (mechanismType == "PRRR")
{
const double L_AB = req_param.value("L_AB", 0.5);
const double L_BS = req_param.value("L_BS", 2.0);
const double S_OFS = req_param.value("S_OFS", 0.0);
std::unique_ptr<SliderCrankMechanism> mechanism(createSliderCrankMechanism());
mechanism->setLink(L_AB, L_BS, S_OFS);
json validationError = validateOrError(mechanism->validateParameters());
if (!validationError.is_null())
{
return validationError;
}
const auto inputRange = mechanism->getInputRange();
const double startSliderX = valueAny(req_param, {"startSliderX", "startValue", "sliderX"}, inputRange.first);
const double endSliderX = valueAny(req_param, {"endSliderX", "endValue"}, inputRange.second);
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startSliderX, endSliderX, "sliderX", [&](double value) {
return mechanism->calculate(value);
});
if (!frames_json.is_array())
{
return frames_json;
}
return {
{"success", true},
{"mechanismType", mechanismType},
{"inputName", "sliderX"},
{"inputRange", {{"min", inputRange.first}, {"max", inputRange.second}}},
{"frames", frames_json},
{"frame_count", frameCount},
{"duration", duration},
{"stepTime", stepTime},
{"startValue", startSliderX},
{"endValue", endSliderX},
{"startSliderX", startSliderX},
{"endSliderX", endSliderX},
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
{"slider_trajectory", buildTrajectoryJson(mechanism->getSliderTrajectory())},
{"alternative_trajectory", buildTrajectoryJson(mechanism->getAlternativeTrajectory())},
{"parameters", {{"mechanismType", mechanismType}, {"L_AB", L_AB}, {"L_BS", L_BS}, {"S_OFS", S_OFS}, {"startSliderX", startSliderX}, {"endSliderX", endSliderX}, {"duration", duration}, {"stepTime", stepTime}}}};
}
if (mechanismType == "RRRR")
{
const double L1 = valueAny(req_param, {"L1", "L_AB"}, 1.0);
const double L2 = valueAny(req_param, {"L2", "L_BS"}, 3.0);
const double L3 = req_param.value("L3", 2.5);
const double L4 = req_param.value("L4", 3.5);
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 180.0);
std::unique_ptr<FourBarMechanism> mechanism(createFourBarMechanism());
mechanism->setLink(L1, L2, L3, L4);
json validationError = validateOrError(mechanism->validateParameters());
if (!validationError.is_null())
{
return validationError;
}
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
return mechanism->calculate(value);
});
if (!frames_json.is_array())
{
return frames_json;
}
return {
{"success", true},
{"mechanismType", mechanismType},
{"inputName", "angleDeg"},
{"frames", frames_json},
{"frame_count", frameCount},
{"duration", duration},
{"stepTime", stepTime},
{"startValue", startAngleDeg},
{"endValue", endAngleDeg},
{"startAngleDeg", startAngleDeg},
{"endAngleDeg", endAngleDeg},
{"trajectory", buildTrajectoryJson(mechanism->getTrajectoryPoints())},
{"trajectory_c", buildTrajectoryJson(mechanism->getTrajectoryC())},
{"crank_circle", buildTrajectoryJson(mechanism->getCrankCirclePoints())},
{"parameters", {{"mechanismType", mechanismType}, {"L1", L1}, {"L2", L2}, {"L3", L3}, {"L4", L4}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
}
if (mechanismType == "RPRR")
{
const double OA = valueAny(req_param, {"OA", "L_AB"}, 1.0);
const double AB = valueAny(req_param, {"AB", "L_BS"}, 3.0);
const double OC = valueAny(req_param, {"OC", "L4"}, 3.0);
const double startAngleDeg = valueAny(req_param, {"startAngleDeg", "startValue", "angleDeg"}, 0.0);
const double endAngleDeg = valueAny(req_param, {"endAngleDeg", "endValue"}, 180.0);
CrankRockingBlockMechanism_Forward mechanism;
mechanism.setLink(OA, AB, OC);
json validationError = validateOrError(mechanism.validateParameters());
if (!validationError.is_null())
{
return validationError;
}
json frames_json = buildSimulationFrames(frameCount, duration, stepTime, startAngleDeg, endAngleDeg, "angleDeg", [&](double value) {
return mechanism.calculate(value);
});
if (!frames_json.is_array())
{
return frames_json;
}
return {
{"success", true},
{"mechanismType", mechanismType},
{"inputName", "angleDeg"},
{"frames", frames_json},
{"frame_count", frameCount},
{"duration", duration},
{"stepTime", stepTime},
{"startValue", startAngleDeg},
{"endValue", endAngleDeg},
{"startAngleDeg", startAngleDeg},
{"endAngleDeg", endAngleDeg},
{"trajectory", buildTrajectoryJson(mechanism.getTrajectoryPoints())},
{"crank_circle", buildTrajectoryJson(mechanism.getCrankCirclePoints())},
{"rocker_trajectory", buildTrajectoryJson(mechanism.getRockerTrajectory())},
{"parameters", {{"mechanismType", mechanismType}, {"OA", OA}, {"AB", AB}, {"OC", OC}, {"startAngleDeg", startAngleDeg}, {"endAngleDeg", endAngleDeg}, {"duration", duration}, {"stepTime", stepTime}}}};
}
return buildErrorJson("Unsupported mechanismType: " + mechanismType, json::array({"mechanismType 必须是 PRRR、RPRR、RRRP、RRRR 之一"}));
}
} // namespace
json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const json &req_param)
@@ -84,6 +376,11 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
{
log("Handling " + req_cmd + " command");
if (req_cmd == "Cmd_FourBar_Simulate")
{
return simulateUnifiedFourBar(req_param);
}
double L_AB = req_param.value("L_AB", 0.5);
double L_BS = req_param.value("L_BS", 2.0);
double S_OFS = req_param.value("S_OFS", 0.0);
@@ -111,10 +408,10 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
if (!validation.isValid())
{
return {
{"success", false},
{"error", "Invalid parameters"},
{"validation_errors", validation.Errors},
{"validation_warnings", validation.Warnings}};
{"success", false},
{"error", "Invalid parameters"},
{"validation_errors", validation.Errors},
{"validation_warnings", validation.Warnings}};
}
if (req_cmd == "Cmd_FourBar_CrankSlider_Simulate")
@@ -164,7 +461,7 @@ json KinematicsWebAPI::handleFourBarCommand(const std::string &req_cmd, const js
{"failed_angleDeg", currentAngleDeg}};
}
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentAngleDeg));
frames_json.push_back(buildFrameJson(state, frame, currentTime, currentAngleDeg, "angleDeg"));
}
json result;