| name | trajectory |
| description | 轨迹规划技能 - 关节空间规划、笛卡尔空间规划、插值算法、时间最优轨迹 |
| argument-hint | 轨迹规划 OR trajectory OR 插值 OR path planning OR 时间最优 |
| user-invocable | true |
轨迹规划技能
机器人轨迹规划:关节空间、笛卡尔空间、插值算法、时间最优轨迹
何时使用
- 机械臂关节空间点到点轨迹
- 末端笛卡尔空间直线/圆弧轨迹
- S 曲线加减速(速度平滑)
- 时间最优轨迹优化
- 多段连续轨迹拼接
关节空间规划(Joint Space)
5 次多项式(平滑加减速)
#include <Eigen/Dense>
struct JointTrajectoryPoint {
std::vector<double> positions;
std::vector<double> velocities;
std::vector<double> accelerations;
double time_from_start;
};
class QuinticPolynomial {
public:
std::vector<double> compute(double t, double T,
double q0, double q1,
double v0, double v1,
double a0, double a1) {
double a3 = (20*(q1-q0) - (8*v1+12*v0)*T - (3*a1-a0)*T*T) / (2*T*T*T);
double a4 = (30*(q0-q1) + (14*v1+16*v0)*T + (3*a1-2*a0)*T*T) / (2*T*T*T*T);
double a5 = (12*(q1-q0) - (6*v1+6*v0)*T - (a1-a0)*T*T) / (2*T*T*T*T*T);
double q = q0 + v0*t + 0.5*a0*t*t + a3*t*t*t + a4*t*t*t*t + a5*t*t*t*t*t;
double v = v0 + a0*t + 3*a3*t*t + 4*a4*t*t*t + 5*a5*t*t*t*t;
double a = a0 + 6*a3*t + 12*a4*t*t + 20*a5*t*t*t;
return {q, v, a};
}
};
class JointSpacePlanner : public rclcpp::Node {
public:
JointSpacePlanner() : Node("joint_space_planner") {
traj_pub_ = this->create_publisher<trajectory_msgs::msg::JointTrajectory>("/joint_trajectory", 10);
timer_ = this->create_wall_timer(10ms, [this]() { this->publish_next_point(); });
std::vector<std::vector<double>> via_points = {
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{1.0, 0.5, -0.5, 0.3, 0.2, 0.1},
{1.5, 1.0, -1.0, 0.5, 0.3, 0.2},
};
plan_trajectory(via_points);
}
void plan_trajectory(const std::vector<std::vector<double>>& via_points) {
for (size_t i = 0; i < via_points.size() - 1; ++i) {
double seg_time = compute_segment_time(via_points[i], via_points[i+1]);
segment_times_.push_back(seg_time);
}
total_time_ = std::accumulate(segment_times_.begin(), segment_times_.end(), 0.0);
}
void publish_next_point() {
double t = (this->get_clock()->now().seconds()) - start_time_;
if (t > total_time_) { return; }
size_t seg = 0;
double cumsum = 0;
for (size_t i = 0; i < segment_times_.size(); ++i) {
cumsum += segment_times_[i];
if (t < cumsum) { seg = i; break; }
}
trajectory_msgs::msg::JointTrajectoryPoint pt;
pt.time_from_start = rclcpp::Duration::from_seconds(t);
for (size_t j = 0; j < 6; ++j) {
auto [pos, vel, acc] = quintic_.compute(
t, segment_times_[seg],
via_points_[seg][j], via_points_[seg+1][j],
0, 0, 0, 0);
pt.positions.push_back(pos);
pt.velocities.push_back(vel);
pt.accelerations.push_back(acc);
}
traj_pub_->publish(pt);
}
private:
double compute_segment_time(const std::vector<double>& a,
const std::vector<double>& b) {
double max_delta = 0;
for (size_t i = 0; i < a.size(); ++i) {
max_delta = std::max(max_delta, std::abs(b[i] - a[i]));
}
return max_delta / max_joint_velocity_;
}
QuinticPolynomial quintic_;
rclcpp::Publisher<trajectory_msgs::msg::JointTrajectory>::SharedPtr traj_pub_;
rclcpp::TimerBase::SharedPtr timer_;
std::vector<std::vector<double>> via_points_;
std::vector<double> segment_times_;
double total_time_;
double start_time_;
double max_joint_velocity_ = 0.5;
};
笛卡尔空间规划(Cartesian Space)
直线插补
class CartesianLinearPlanner : public rclcpp::Node {
public:
CartesianLinearPlanner() : Node("cartesian_linear_planner") {
traj_pub_ = create_publisher<trajectory_msgs::msg::JointTrajectoryPoint>("/cartesian_trajectory", 10);
}
std::vector<Eigen::Matrix4d> linear_interpolate(const Eigen::Matrix4d& T_start,
const Eigen::Matrix4d& T_end,
int N) {
std::vector<Eigen::Matrix4d> traj;
for (int i = 0; i <= N; ++i) {
double alpha = static_cast<double>(i) / N;
Eigen::Vector3d p = (1-alpha)*T_start.block<3,1>(0,3) + alpha*T_end.block<3,1>(0,3);
Eigen::Quaterniond q_start(T_start.block<3,3>(0,0));
Eigen::Quaterniond q_end(T_end.block<3,3>(0,0));
Eigen::Quaterniond q = q_start.(alpha, q_end);
Eigen::Matrix4d T = Eigen::Matrix4d::();
T.<,>(,) = p;
T.<,>(,) = q.();
traj.(T);
}
traj;
}
{
std::vector<Eigen::Matrix4d> traj;
Eigen::Vector3d p_start = T_start.<,>(,);
Eigen::Vector3d p_end = T_end.<,>(,);
dist = (p_end - p_start).();
t_accel = v_max / a_max;
d_accel = * a_max * t_accel * t_accel;
t_cruise = (dist - *d_accel) / v_max;
(t_cruise < ) {
t_accel = std::(dist / a_max);
t_cruise = ;
v_max = a_max * t_accel;
d_accel = * a_max * t_accel * t_accel;
}
total_time = *t_accel + t_cruise;
( i = ; i <= N; ++i) {
t = total_time * i / N;
s, ds, dds;
(t < t_accel) {
s = * a_max * t * t;
ds = a_max * t;
dds = a_max;
} (t < t_accel + t_cruise) {
s = d_accel + v_max * (t - t_accel);
ds = v_max;
dds = ;
} {
t_dec = t - t_accel - t_cruise;
s = dist - * a_max * (t_accel - t_dec) * (t_accel - t_dec);
ds = a_max * (t_accel - t_dec);
dds = -a_max;
}
alpha = s / dist;
Eigen::Vector3d p = (-alpha)*p_start + alpha*p_end;
Eigen::Matrix4d T = Eigen::Matrix4d::();
T.<,>(,) = p;
traj.(T);
}
traj;
}
};
时间最优轨迹(Time-Optimal)
class TimeOptimalPlanner {
public:
trajectory_msgs::msg::JointTrajectory
compute_time_optimal(const std::vector<std::vector<double>>& path,
const JointLimits& limits) {
trajectory_msgs::msg::JointTrajectory traj;
std::vector<double> seg_lengths;
for (size_t i = 0; i < path.size() - 1; ++i) {
double len = 0;
for (size_t j = 0; j < path[i].size(); ++j) {
len += std::abs(path[i+1][j] - path[i][j]);
}
seg_lengths.push_back(len);
}
double v_prev = 0;
for (size_t i = 0; i < path.size() - 1; ++i) {
double s = seg_lengths[i];
double v_max_seg = compute_v_max_seg(path[i], path[i+1], limits);
double t_accel = (v_max_seg - v_prev) / limits.max_acceleration[0];
double v_next = (i+1 < path.()) ? (path[i], path[i], limits) : ;
t_decel = (v_max_seg - v_next) / limits.max_acceleration[];
(t_accel + t_decel > ) {
t_cruise = std::(, s - *(v_max_seg+v_prev)/limits.max_acceleration[]
- *(v_max_seg+v_next)/limits.max_acceleration[]);
= t_accel + t_cruise + t_decel;
}
}
traj;
}
};
性能指标
| 指标 | 目标 | 说明 |
|---|
| 轨迹生成时间 | < 10ms | 实时规划需要 |
| 插补周期 | 1-10ms | 控制周期 |
| 位置误差 | < 1mm | 笛卡尔规划 |
| 速度连续性 | C¹ 连续 | 避免冲击 |
| 加速度限制 | 满足关节限位 | 保护机械 |