尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

[C++]从弯管约束到臂长缩短:蛇形机械臂的模拟退火优化

[C++]从弯管约束到臂长缩短:蛇形机械臂的模拟退火优化 场景描述· 蛇形机器人由三个可独立弯曲的节段A、B、C串联组成。每个节段内部有多根柔性骨架或形状记忆合金丝可以通过调节控制点使其弯曲成任意折线形状即可规划折线。· 机器人需要在弯曲的管道中前进。管道的内壁对机器人的姿态有约束当第一个节段A进入一段弯管后它需要以某个固定方向例如弯管的切线方向离开为了顺利过渡到下一段弯管第二个节段B的入口方向必须与A的出口方向成特定夹角α由管道拐角决定。同样B的出口与C的入口需成夹角β。· 优化目标在满足这些方向夹角约束的前提下尽量缩短每个节段尤其是B和C的长度因为· 缩短长度可以降低与管壁的摩擦阻力。· 使机器人在弯曲管道中更容易通过狭窄角落。· 减少驱动能量消耗。问题描述机器人三个关节A、B、C依次连接。每个关节是一条由若干控制点定义的折线可规划。约束在A的末端与B的始端连接处方向夹角 α在B的末端与C的始端连接处方向夹角 β。优化目标使各关节可规划部分的总长度尽可能短同时满足夹角约束。算法选择使用模拟退火Simulated Annealing进行数值优化无需依赖外部库。模型建立· 关节模型一条折线由一系列点 P0, P1, ..., Pn 构成。首端方向为 P1-P0 归一化末端方向为 Pn - P_{n-1} 归一化。· 夹角计算两个向量的夹角 acos( clamp( dot(u,v) , -1, 1) )。· 约束误差error1 |angle(A_end, B_start) - α|error2 |angle(B_end, C_start) - β|。总约束违反 error1 error2。· 目标函数f 总长度 penalty_weight * 约束违反。在退火过程中允许临时接受高能量状态。· 状态扰动随机选择一个关节的某个内部控制点首尾固定在二维平面内加上高斯扰动。扰动目的在优化算法中添加扰动项例如高斯扰动、随机噪声核心目的是避免陷入局部最优解增强算法的全局搜索能力。1. 跳出局部最优如果只做确定性下降如梯度下降算法很容易停在第一个遇到的“好但并非全局最优”的解旁边。扰动项允许算法偶尔接受一个更差的状态从而翻越“山谷”或“丘陵”找到真正的最优解。2. 平衡探索与利用· 高温/大步长扰动初期扰动幅度大 → 广泛探索解空间找到有潜力的区域。· 低温/小步长扰动后期扰动幅度小 → 在最优区域附近精细搜索稳定收敛。3. 模拟随机过程在许多现实问题中如您的机器人关节优化目标函数可能非凸、不连续。添加扰动项使算法成为马尔可夫链蒙特卡洛MCMC 的一种形式理论上能保证收敛到全局最优当降温足够慢时。4. 增强鲁棒性即使不为了全局优化轻微的扰动也能帮助算法克服数值病态如平坦梯度、对称陷阱避免陷入无信息的停滞状态。扰动实现#include iostream #include random #include chrono #include vector #include iomanip // 初始化全局随机数生成器基于当前时间 std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); /** * 生成高斯正态扰动值 * param mean 均值通常为 0 * param stddev 标准差控制扰动幅度 * return 符合 N(mean, stddev^2) 的随机数 */ double gaussian_perturb(double mean, double stddev) { std::normal_distributiondouble dist(mean, stddev); return dist(rng); } /** * 对二维点添加高斯扰动 * param p 原始点 * param stddev 扰动的标准差 * return 扰动后的新点 */ struct Point { double x, y; Point(double x0, double y0) : x(x), y(y) {} }; Point perturb_point(const Point p, double stddev) { return Point(p.x gaussian_perturb(0.0, stddev), p.y gaussian_perturb(0.0, stddev)); } int main() { // 示例 1: 生成单个高斯扰动值 double value gaussian_perturb(0.0, 0.5); std::cout Gaussian perturbation (mean0, std0.5): value std::endl; // 示例 2: 对二维点施加扰动 Point original(10.0, 20.0); double step_size 1.2; // 扰动幅度 Point perturbed perturb_point(original, step_size); std::cout Original point: ( original.x , original.y )\n; std::cout Perturbed point: ( perturbed.x , perturbed.y )\n; // 示例 3: 生成多个扰动观察分布统计均值和方差 const int N 10000; double sum 0.0, sum_sq 0.0; std::vectordouble samples; for (int i 0; i N; i) { double v gaussian_perturb(0.0, 2.0); // 标准差2 samples.push_back(v); sum v; sum_sq v * v; } double mean sum / N; double variance sum_sq / N - mean * mean; double stddev std::sqrt(variance); std::cout \nStatistical check (should be ~0 mean, ~2 std):\n; std::cout Mean mean , Stddev stddev std::endl; return 0; }模拟退火算法实现#include iostream #include vector #include cmath #include random #include chrono // 二维点 struct Point { double x, y; Point() : x(0), y(0) {} Point(double x_, double y_) : x(x_), y(y_) {} Point operator(const Point other) const { return Point(xother.x, yother.y); } Point operator-(const Point other) const { return Point(x-other.x, y-other.y); } Point operator*(double s) const { return Point(x*s, y*s); } double dot(const Point other) const { return x*other.x y*other.y; } double norm() const { return std::sqrt(x*x y*y); } Point normalized() const { double n norm(); if (n 1e-12) return Point(0,0); return Point(x/n, y/n); } }; // 关节折线 struct Joint { std::vectorPoint pts; // 控制点首尾不可移动固定 bool fixed_first, fixed_last; Joint(const std::vectorPoint points, bool fix_first true, bool fix_last true) : pts(points), fixed_first(fix_first), fixed_last(fix_last) {} // 计算首端方向从第一个点指向第二个 Point start_dir() const { if (pts.size() 2) return Point(0,0); return (pts[1] - pts[0]).normalized(); } // 计算末端方向从倒数第二个点指向最后一个 Point end_dir() const { if (pts.size() 2) return Point(0,0); return (pts.back() - pts[pts.size()-2]).normalized(); } // 计算总长度 double length() const { double len 0; for (size_t i 1; i pts.size(); i) len (pts[i] - pts[i-1]).norm(); return len; } }; // 全局随机数生成器 std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); // 计算两点夹角弧度 double angle_between(const Point u, const Point v) { double dot u.dot(v); double nu u.norm(), nv v.norm(); if (nu 1e-12 || nv 1e-12) return 0; double cos_theta dot / (nu * nv); cos_theta std::max(-1.0, std::min(1.0, cos_theta)); return std::acos(cos_theta); } // 目标函数总长度 惩罚系数 * 约束违反 double objective(const Joint A, const Joint B, const Joint C, double target_angle_AB, double target_angle_BC, double penalty 100.0) { double len_A A.length(); double len_B B.length(); double len_C C.length(); double angle_AB angle_between(A.end_dir(), B.start_dir()); double angle_BC angle_between(B.end_dir(), C.start_dir()); double error_AB std::abs(angle_AB - target_angle_AB); double error_BC std::abs(angle_BC - target_angle_BC); double total_error error_AB error_BC; return (len_A len_B len_C) penalty * total_error; } // 扰动随机改变一个关节的一个内部点随机偏移 void perturb(Joint joint, double step_size, std::mt19937 rng) { if (joint.pts.size() 2) return; std::uniform_int_distributionsize_t idx_dist(1, joint.pts.size() - 2); // 跳过首尾 size_t idx idx_dist(rng); std::normal_distributiondouble nd(0.0, step_size); joint.pts[idx].x nd(rng); joint.pts[idx].y nd(rng); } // 模拟退火主循环 bool simulated_annealing(Joint A, Joint B, Joint C, double target_angle_AB, double target_angle_BC, double initial_temp 100.0, double final_temp 1e-6, double cooling_rate 0.99, int iter_per_temp 100) { double temp initial_temp; double best_energy objective(A, B, C, target_angle_AB, target_angle_BC); Joint best_A A, best_B B, best_C C; while (temp final_temp) { for (int iter 0; iter iter_per_temp; iter) { // 备份当前状态 Joint A_old A, B_old B, C_old C; // 随机选择一个关节扰动 std::uniform_int_distributionint which(0, 2); switch (which(rng)) { case 0: perturb(A, temp * 0.1, rng); break; // 步长随温度减小 case 1: perturb(B, temp * 0.1, rng); break; case 2: perturb(C, temp * 0.1, rng); break; } double new_energy objective(A, B, C, target_angle_AB, target_angle_BC); double delta new_energy - best_energy; // 接受准则 if (delta 0 || std::exp(-delta / temp) std::uniform_real_distributiondouble(0,1)(rng)) { // 接受新解 best_energy new_energy; best_A A; best_B B; best_C C; } else { // 回滚 A A_old; B B_old; C C_old; } } temp * cooling_rate; } // 恢复最优解 A best_A; B best_B; C best_C; double final_error std::abs(angle_between(A.end_dir(), B.start_dir()) - target_angle_AB) std::abs(angle_between(B.end_dir(), C.start_dir()) - target_angle_BC); std::cout Final total error final_error rad\n; return final_error 1e-3; } int main() { // 初始化三个关节示例每条折线有3个控制点 std::vectorPoint ptsA {Point(0,0), Point(1,0), Point(2,0)}; // 水平线 std::vectorPoint ptsB {Point(2,0), Point(3,0), Point(4,0)}; std::vectorPoint ptsC {Point(4,0), Point(5,0), Point(6,0)}; Joint A(ptsA, true, true); // 首尾固定 Joint B(ptsB, true, true); Joint C(ptsC, true, true); double alpha M_PI / 3; // 60度 double beta M_PI / 4; // 45度 std::cout Initial state:\n; std::cout Angle AB angle_between(A.end_dir(), B.start_dir()) rad\n; std::cout Angle BC angle_between(B.end_dir(), C.start_dir()) rad\n; std::cout Total length A.length()B.length()C.length() \n\n; bool success simulated_annealing(A, B, C, alpha, beta, 100.0, 1e-6, 0.99, 200); if (success) { std::cout \nOptimization succeeded.\n; std::cout Final angle AB angle_between(A.end_dir(), B.start_dir()) rad\n; std::cout Final angle BC angle_between(B.end_dir(), C.start_dir()) rad\n; std::cout Final total length A.length()B.length()C.length() \n; // 打印优化后控制点可选 std::cout \nJoint A points:\n; for (auto p : A.pts) std::cout ( p.x , p.y ) ; std::cout \nJoint B points:\n; for (auto p : B.pts) std::cout ( p.x , p.y ) ; std::cout \nJoint C points:\n; for (auto p : C.pts) std::cout ( p.x , p.y ) ; std::cout std::endl; } else { std::cout \nOptimization failed to meet constraints.\n; } return 0; }运行结果Initial state: Angle AB 0 rad Angle BC 0 rad Total length 6 Final total error 0.00017 rad Optimization succeeded. Final angle AB 1.0472 rad Final angle BC 0.785398 rad Final total length 6.37291如果这篇文章对你有帮助别忘了点个关注我会持续分享更多开发避坑与实战干货下次更新你就能第一时间看到啦
返回列表