尝试对MoveIt2的轨迹进行插值控制舵机机械手
Posted 韭菜钟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尝试对MoveIt2的轨迹进行插值控制舵机机械手相关的知识,希望对你有一定的参考价值。
1.现状分析
机械手在运动时,要尽可能平稳、少冲击,显得丝滑。
Moveit给到的轨迹,除了描述了各个关键时刻的各个关节的位置,还描述了此时的各个关节的速度、加速度。假如我们的执行机构可以完全按照这些位置、速度、加速度来进行操作,理论上就可以实现平滑的移动。
但是由于我们目前用的是舵机,除了位置可控外,速度、加速度都不能直接控制。
所以,只能通过插值来插补两个位置之间的各个小位置,让运动大概实现【加速-匀速-减速 】的过程。
舵机的转动速度会因为负载的不同而不同,不能够简单认为是一个固定值。因此,想要间接通过设置定位的方式来设定速度,可行性也不太。
假如我们让舵机从位置a走到位置b,等待时间为T1。舵机实际从a走到b的时间为T2。
那么只有T1大于等于T2才能确保舵机运动到位。
2.委曲求全
由于测算舵机实际的运算速度太麻烦了。
我们先进行固定时间步进的方式算了。
从ros上可以收到各个点位的信息,以及时间戳。因此可以根据时间戳,把位置均分一下。然后再发送给下位机。
实现的代码如下,仅供参考
void MainWindow::processGoal(std::shared_ptr<const FollowJointTrajectory::Goal> goal)
// 该轨迹分成若干个点位(每个点位都包括了此点位下,若干关节的角度信息)
int pointSize = goal->trajectory.points.size();
if(pointSize <= 0)
return;
// 关于time_from_start,
// 第一个点位的time_from_start为0,后面的每个时间都是与第一个位置时间做的偏移。
// 直观地理解,该轨迹要求在各个时间点,机械手要在要求的位置
// 我们先人为地规定,timeStep(ms)更新一次位置给下位机
int timeStep = 50; // 更新频率
// 先不进行加速、匀速、减速的操作。现在直接均分。
// 目前是按照时间来插值
// 存放移动数据的队列
static QQueue<QList<float>> angleQueue;
angleQueue.clear();
QList<float> angleList;
for(int i = 0; i < pointSize; i++)
// 当前点位
auto curPoint = goal->trajectory.points.at(i);
if(i == 0) // 第一个是起始位置,直接放进去进行
angleList.clear();
foreach(auto pos, curPoint.positions)
angleList << qRadiansToDegrees(pos);
angleQueue << angleList;
continue;
auto lastPoint = goal->trajectory.points.at(i - 1);
auto last_time = lastPoint.time_from_start;
auto current_time = curPoint.time_from_start;
rclcpp::Time time1(last_time.sec, last_time.nanosec);
rclcpp::Time time2(current_time.sec, current_time.nanosec);
auto duration_time = time2 - time1;
// 从上一个点位跑到此处计划耗时
qint64 duration_ms = duration_time.nanoseconds() / 1e6;
qDebug() << "[sec:" << current_time.sec << ", nanosec:" << current_time.nanosec << "]" << duration_time.seconds() << duration_time.nanoseconds() ;
qDebug() << "duration ms:" << duration_ms;
if(duration_ms < timeStep) // 小于时间步进,无需插值
angleList.clear();
foreach(auto pos, lastPoint.positions)
angleList << qRadiansToDegrees(pos);
angleQueue << angleList;
else // 需要插值
// 需要分几步进行插值
float divCount = duration_ms / timeStep;
if(duration_ms % timeStep)
divCount++;
// 插值步进
QList<float> stepList;
for(int idx = 0; idx < lastPoint.positions.size(); idx++)
stepList << (curPoint.positions[idx] - lastPoint.positions[idx]) / divCount;
// 均分插值
for(int divIdx = 0; divIdx < divCount; divIdx++)
angleList.clear();
for(int idx = 0; idx < lastPoint.positions.size(); idx++)
angleList << qRadiansToDegrees(lastPoint.positions[idx] + stepList[idx] * (divIdx + 1));
angleQueue << angleList;
qDebug() << angleQueue;
// 算出了整个移动队列,通过定时器定时发送该数据给单片机
//(也可以把整个移动数据发送至单片机,然后单片机再在自己内部慢慢跑)
QTimer *timer = new QTimer();
timer->setInterval(timeStep);
connect(timer, &QTimer::timeout, [=]()
if(angleQueue.length() == 0)
timer->stop();
timer->deleteLater();
return;
QList<float> angleList = angleQueue.dequeue();
// 转动方向修正(根据实际情况)
angleList[0] = -angleList[0];
angleList[1] = -angleList[1];
angleList[3] = -angleList[3];
sendPose(angleList);
);
timer->start();
3.实际效果
不好。还是晃得厉害,有时候甚至还不如原来的不插值的。下面视频的是用原来的没有插值的。
【Ros2中使用MoveIt进行虚拟避障】
参考资料
http://www.51hei.com/mcu/2548.html
https://www.elecfans.com/d/608940.html
ROS2机器人实验报告提示06➡抓阄⬅MoveIt2
由于此部分和各位朋友工业机器人相关内容一致但是又不同,避免困扰,提供一些资料供阅读,报告内容直接选用工业机器人部分即可。
只需了解用ROS2控制工业机械臂和自带软件控制机械臂区别。
官网文档存在一些不统一的地方--
例如:
MoveIt2 文档只有 Foxy ,但是 主页 推荐如下:
MoveIt 2第一款正式版推出支持ROS2 Foxy_zhangrelay的专栏-CSDN博客
来日方长,等2022年5月再来学习和研究MoveIt 2也不迟。
以上是关于尝试对MoveIt2的轨迹进行插值控制舵机机械手的主要内容,如果未能解决你的问题,请参考以下文章