S加减速,脉冲S加减速

发布时间:2026/10/28 3:50:14

S加减速,脉冲S加减速 s型加减速在网格图上速度的变化现是缓慢增大然后快速增大再是缓慢增大再到匀速。也就是说加速度是变化的。f(x) 1/(1e^-x)这是y从左到右增加时的S曲线的原始函数用这个生成一个表加速正着用减速倒着用e是自然常数约为2.71828。当-3 ≤ x ≤ 3函数收敛较为明显此时y轴高度基本处于0到1之间更精确点当x -3时 y ≈ 0.04742当x 3时y ≈ 0.95257。如果精度不够就加大x的取值范围。现在假设3 ≤ x ≤ 3时y从0到1。实际使用时x作为加速次数y就是速度。加速次数不可能为负数所以需要对函数进行向右平移也就是1/(1e^(-x 3))这样当 x 0时y ≈ 0这时0 ≤ x ≤ 6。但是加速次数也不可能为小数而且6次加速太少了所以要横向拉长函数。改变x的系数就可以拉长或缩短y ≈ 0时x的范围。这个函数的x的系数越小函数看起来横向越长实际无限长x能取得的整数越多。当系数为0.1时1/(1e^(-0.1x 3))x的有效值就是0到60当系数为2时1/(1e^(-2x 3))x的有效值就是0到3。也就是说x取值的整数范围是系数的倒数即1/0.1 x 6个和1/2 x 6个。如果决定加速次数那么系数就是6/加速次数当加速次数为100次时也就是1/(1e^(-(6/100)x 3))。假如最大脉冲频率为1MHzy乘以最大频率就可以知道当前实际频率了即1000000 x 0.95257。如果将1/(1e^-x)计算后的速度比例存起来下次使用时只需查表再乘以最大频率即可。实现buff_pa是速度表数组。count_va是加速次数。pan_right_va是右移量代表了精度即右移多少后y强制为0一般用7最后一次速度在99.9%。count_va越大S曲线越平缓加速过程越平滑加速时间越长count_va越小S曲线越陡峭加速过程越急剧加速时间越短。void curve_s_init(float *buff_pa, uint16_t count_va, uint8_t pan_right_va) { for (uint16_t x 0; x count_va; x) buff_pa[x] 1.0 / (1.0 exp(((float)pan_right_va * 2 / count_va * -x) pan_right_va)); //buff_pa[x] 1.0 / (1.0 pow(sqrt(m_e_v), (float)pan_right_va * 2 / count_va * -x pan_right_va)); //这是通用计算方法pow函数的第一个参数越接近1该算式计算出的曲线越平缓可以改的和线性加减速无异 } 例 float curve_s_table_v[500]; curve_s_init(curve_s_table_v, 500, 7);以下为c版本#ifndef ivesStepMotorH #define ivesStepMotorH #include main.h namespace xiaguangbo { namespace motorNamespace { namespace ivesStepMotorNamespace { class ivesStepMotorClass { private: static constexpr float pi 3.1415926; //运行状态 static const uint8_t motorStopState 0; static const uint8_t motorAccelerateState 1; static const uint8_t motorMaxState 2; static const uint8_t motorDecelerateState 3; //固定参数 static const uint8_t sCurveExcursion 3; //s曲线偏移的量 static const uint16_t sCurvePointNumber 200; //在s曲线上取的点的数量 static const uint16_t aCircleStep 3200; //一圈的步数 static const uint16_t maxSpeedThreshold 0xffff; //速度的最大值因为速度变量是16位的所以最大就是0xffff //s曲线 static float sCurveTable[sCurvePointNumber]; //放s曲线数值的数组 static bool sCurveTableFlag; //s曲线是否已经计算过了 //算法核心参数 int32_t currentAbsolutePosition; //当前的绝对位置以步数为单位 int32_t currentNeedRunStep; //将要运行的步数。负数就往复位方向移动 uint32_t temporaryStepCounter; //临时的步数计数记录一次算法走了多少步 uint16_t currentSpeed; //当前速度 uint16_t targetSpeed; //要达到的速度 uint8_t runState; //运行状态 uint16_t accelerationInterval; //加减速一次的间隔 uint16_t interruptCounter; //加减速时的定时器中断次数计数 bool interruptCounterFlag; //中断计数的开关 uint32_t accelerationNumberCounter; //加减速次数计数 uint32_t accelerationStep; //加减速阶段的脉冲数 uint32_t speedAccumulator; //速度累加器 bool overflowFlag; //溢出标志 //定制化功能的参数 bool resetTimeDirPinLv; //复位时方向的io电平 uint16_t oneMmStep; //电机走1mm所需的步数 GPIO_TypeDef *stepGpioHandle, *dirGpioHandle, *enGpioHandle, *resetSensorGpioHandle; //IO组脉冲、方向、复位限位 uint16_t stepGpioNumber, dirGpioNumber, enGpioNumber, resetSensorGpioNumber; //IO号脉冲、方向、复位限位 bool resetSensorTriggerLv; //复位限位开关被触发后的电平 bool resetFlag; //是否处于复位 bool stopFlag; //是否被停止了 bool keepMoveFlag; //是否要一直转 bool enTimeEnPinSate; /* 初始化s曲线参数表 */ static void sCurveTableInit(); /* 电机每走一步都会执行的参数。主要用于检测复位开关 */ void runTimeImplement(); void setBeltOneMmStep(uint8_t pitch, uint8_t motorGearTeeth) { oneMmStep aCircleStep / (pitch * motorGearTeeth); } void setGearOneMmStep(uint8_t pitch, uint8_t motorGearTeeth) { setBeltOneMmStep(pitch * pi, motorGearTeeth); } void SetEnPinState(bool state) { HAL_GPIO_WritePin(enGpioHandle, enGpioNumber, GPIO_PinState(state)); } public: ivesStepMotorClass(); ~ivesStepMotorClass(); /* 初始化 */ void init(GPIO_TypeDef *stepGpioHandle, uint16_t stepGpioNumber, GPIO_TypeDef *dirGpioHandle, uint16_t dirGpioNumber, GPIO_TypeDef *enGpioHandle, uint16_t enGpioNumber, GPIO_TypeDef *resetSensorGpioHandle, uint16_t resetSensorGpioNumber, bool resetSensorTriggerLv, bool resetTimeDirPinLv, bool enTimeEnPinSate); /* 移动相应的步数 need_run_step步数。负值是向复位方向移动 acc_dec_interval加速度 */ void move(int32_t currentNeedRunStep, uint16_t targetSpeed, uint16_t accelerationInterval); /* 移动到绝对位置。单位为mm */ void moveToAbsolutePosition(uint16_t absolutePosition, uint16_t targetSpeed, uint16_t accelerationInterval); //在没有使用set_mm_step设置mm_step之前不要用 /* 移动到相对位置 */ void moveToRelativePosition(int16_t relativePosition, uint16_t targetSpeed, uint16_t accelerationInterval); /* 算法的循环。放在一个定时器里进行定时执行调用的速度就是电机的最大速度 */ void loop(); /* 根据参数计算并设置每走1mm需要的步数 beltOrGearfalse同步带。true齿条 pitch同步带的型号或齿条的模数 gear_teeth电机上的齿轮的齿数 */ void setOneMmStep(bool beltOrGear, uint8_t pitch, uint8_t motorGearTeeth) { if (beltOrGear 0) setBeltOneMmStep(pitch, motorGearTeeth); else setGearOneMmStep(pitch, motorGearTeeth); } /* 复位 */ void reset(uint16_t targetSpeed 5000, uint16_t accelerationInterval 100); /* 停止 */ void stop(); /* 一直转 */ void keepMove(bool dir, uint16_t targetSpeed, uint16_t accelerationInterval 100); bool getStopFlag() { return stopFlag; } void resetStopFlag() { stopFlag false; } int32_t getCurrentAbsolutePosition() { return currentAbsolutePosition; } }; } // namespace ivesStepMotorNamespace } // namespace motorNamespace } // namespace xiaguangbo #endif#include ivesStepMotor.hpp #include math.h namespace xiaguangbo { namespace motorNamespace { namespace ivesStepMotorNamespace { float ivesStepMotorClass::sCurveTable[sCurvePointNumber]; bool ivesStepMotorClass::sCurveTableFlag; ivesStepMotorClass::ivesStepMotorClass() { } ivesStepMotorClass::~ivesStepMotorClass() { } void ivesStepMotorClass::sCurveTableInit() { for (uint16_t x 0; x sCurvePointNumber; x) sCurveTable[x] 1.0 / (1.0 exp((sCurveExcursion * 2.0 / sCurvePointNumber * -x) sCurveExcursion)); sCurveTableFlag true; } void ivesStepMotorClass::init(GPIO_TypeDef *stepGpioHandle, uint16_t stepGpioNumber, GPIO_TypeDef *dirGpioHandle, uint16_t dirGpioNumber, GPIO_TypeDef *enGpioHandle, uint16_t enGpioNumber, GPIO_TypeDef *resetSensorGpioHandle, uint16_t resetSensorGpioNumber, bool resetSensorTriggerLv, bool resetTimeDirPinLv, bool enTimeEnPinSate) { if (sCurveTableFlag false) { sCurveTableInit(); sCurveTableFlag true; } this-stepGpioHandle stepGpioHandle; this-stepGpioNumber stepGpioNumber; this-dirGpioHandle dirGpioHandle; this-dirGpioNumber dirGpioNumber; this-enGpioHandle enGpioHandle; this-enGpioNumber enGpioNumber; this-resetSensorGpioHandle resetSensorGpioHandle; this-resetSensorGpioNumber resetSensorGpioNumber; this-resetSensorTriggerLv resetSensorTriggerLv; this-resetTimeDirPinLv resetTimeDirPinLv; stop(); SetEnPinState(enTimeEnPinSate); } void ivesStepMotorClass::move(int32_t currentNeedRunStep, uint16_t targetSpeed, uint16_t accelerationInterval) { if (currentNeedRunStep 0) { stop(); return; } this-currentNeedRunStep currentNeedRunStep; temporaryStepCounter 0; currentSpeed 0; this-targetSpeed targetSpeed maxSpeedThreshold - 100 ? maxSpeedThreshold : targetSpeed 100; //防止速度为0 interruptCounterFlag true; interruptCounter 0; accelerationNumberCounter 0; accelerationStep 0; this-accelerationInterval accelerationInterval; speedAccumulator 0; if (currentNeedRunStep 0) HAL_GPIO_WritePin(dirGpioHandle, dirGpioNumber, (GPIO_PinState)(!resetTimeDirPinLv)); else HAL_GPIO_WritePin(dirGpioHandle, dirGpioNumber, (GPIO_PinState)resetTimeDirPinLv); runState motorAccelerateState; } void ivesStepMotorClass::moveToAbsolutePosition(uint16_t absolutePosition, uint16_t targetSpeed, uint16_t accelerationInterval) { move(absolutePosition * oneMmStep - this-currentAbsolutePosition, targetSpeed, accelerationInterval); } void ivesStepMotorClass::moveToRelativePosition(int16_t relativePosition, uint16_t targetSpeed, uint16_t accelerationInterval) { move(relativePosition * oneMmStep, targetSpeed, accelerationInterval); } void ivesStepMotorClass::runTimeImplement() { if (resetSensorGpioHandle nullptr) return; if (HAL_GPIO_ReadPin(resetSensorGpioHandle, resetSensorGpioNumber) resetSensorTriggerLv) //检查复位开关 { if (resetFlag true) { stop(); currentAbsolutePosition 0; //绝对位置置0 } else if (currentNeedRunStep 0) //不能继续向复位的方向走但可以向复位的反方向走 stop(); } } void ivesStepMotorClass::loop() { if (runState motorStopState) return; if (overflowFlag) //如果产生了高电平 HAL_GPIO_WritePin(stepGpioHandle, stepGpioNumber, GPIO_PIN_RESET); //拉低脉冲信号 overflowFlag false; speedAccumulator currentSpeed; //叠加速度 if (speedAccumulator maxSpeedThreshold) //阈值如果当前速度达到阈值发送脉冲的速度就达到最快了这个阈值就是算法的最大速度 { overflowFlag true; speedAccumulator - maxSpeedThreshold; } if (overflowFlag) //如果溢出 { temporaryStepCounter; HAL_GPIO_WritePin(stepGpioHandle, stepGpioNumber, GPIO_PIN_SET); //拉高脉冲信号产生 if (currentNeedRunStep 0) currentAbsolutePosition 0x7fffffff ? currentAbsolutePosition : currentAbsolutePosition; else currentAbsolutePosition (int32_t)0x80000000 ? currentAbsolutePosition-- : currentAbsolutePosition; runTimeImplement(); //额外的操作比如检测复位开关 } //根据电机的状态进行工作 switch (runState) { case motorAccelerateState: //记录加速的步数 if (overflowFlag) accelerationStep; if (interruptCounterFlag) { interruptCounter; if (interruptCounter accelerationInterval) { interruptCounter 0; accelerationNumberCounter; //计录加速的次数 currentSpeed sCurveTable[accelerationNumberCounter] * targetSpeed; //计算当前速度 //如果加速次数达到最高次数就停止加速并让速度等于最大速度再切换状态 if (accelerationNumberCounter sCurvePointNumber) { interruptCounterFlag false; currentSpeed targetSpeed; runState motorMaxState; } } } //如果总步数大于1步 if ((uint32_t)fabs(currentNeedRunStep) 1) { if (temporaryStepCounter (uint32_t)fabs(currentNeedRunStep) / 2) //如果已走步数大于最大步数的一半 { //加速时中断计数被断所以要调整最后一次加速多长时间减速的第一次的速度就维持多长时间 interruptCounter accelerationInterval - interruptCounter; runState motorDecelerateState; } } else if (temporaryStepCounter 0) //只有1步就至少走1步 runState motorDecelerateState; break; case motorMaxState: if (keepMoveFlag overflowFlag true) //当处于保持转动的状态时 temporaryStepCounter--; //加一次就减一次防止步数达到进入减速的要求 if ((uint32_t)fabs(currentNeedRunStep) - temporaryStepCounter accelerationStep) { interruptCounterFlag true; //进入减速状态就要切换为减速状态时的最大速度 accelerationNumberCounter--; currentSpeed sCurveTable[accelerationNumberCounter] * targetSpeed; //计算当前速度 runState motorDecelerateState; } break; case motorDecelerateState: if (interruptCounterFlag) { interruptCounter; if (interruptCounter accelerationInterval) //如果中断次数达到设定次数 { interruptCounter 0; accelerationNumberCounter--; currentSpeed sCurveTable[accelerationNumberCounter] * targetSpeed; //计算当前速度 if (accelerationNumberCounter 1) interruptCounterFlag false; } } if (temporaryStepCounter (uint32_t)fabs(currentNeedRunStep)) stop(); break; default: break; } } void ivesStepMotorClass::reset(uint16_t targetSpeed, uint16_t accelerationInterval) { resetFlag true; keepMove(false, targetSpeed, accelerationInterval); //向复位方向一直走 } void ivesStepMotorClass::stop() { runState motorStopState; stopFlag true; keepMoveFlag false; resetFlag false; } void ivesStepMotorClass::keepMove(bool dir, uint16_t targetSpeed, uint16_t accelerationInterval) { keepMoveFlag true; move(aCircleStep * 100 * (dir false ? -1 : 1), targetSpeed, accelerationInterval); //步数至少让加速能加满 } } // namespace ivesStepMotorNamespace } // namespace motorNamespace } // namespace xiaguangbo
延伸阅读

更多相关文章

2026/10/27 4:18:09

C++ String与STL核心解析:从基础概念到工程实践

1. 从“Hello World”到“Hello STL”:为什么C程序员绕不开String和STL刚学C那会儿,觉得这语言真麻烦,连个字符串都得用字符数组char str[],还得操心结尾的\0。后来老师扔过来一句std::string s “Hello World”;,世界…

2026/10/24 20:18:00

前端八股文是什么?高频面试考点与实战答法全解析

“前端八股文”这个词,圈里人听了都会心一笑。说白了,就是面试里翻来覆去问的那批固定知识点:闭包、原型链、事件循环、this 指向、Vue 响应式原理、浏览器从输入 URL 到页面展示发生了什么。背的人觉得枯燥,问的人也觉得套路&…

2026/10/27 13:48:54

4K MV制作全流程:从拍摄参数到编码压制的实用要点

4K 画质的音乐 MV,例如派伟俊《别恋》官方 MV,观众在屏幕上看到的是几分钟的歌曲影像,但创作端面对的是拍摄、剪辑、调色、音频处理、编码压制和平台发布一整条技术链路。很多刚开始接触视频制作的人会把注意力放在镜头构图和转场特效上&…

2026/10/28 3:47:53

OFDM系统中深度学习与传统信道估计算法对比研究

1. 项目概述在无线通信系统中,OFDM(正交频分复用)技术因其高频谱效率和抗多径干扰能力而被广泛应用。然而,实际信道环境中的多径效应会导致符号间干扰(ISI),严重影响系统性能。本项目基于深度学习技术,针对…

2026/10/28 3:47:53

微信OAuth2.0登录在餐饮外卖平台的实践与优化

1. 项目背景与需求分析"苍穹外卖"作为一款餐饮外卖服务平台,用户登录环节的便捷性直接影响着转化率和用户体验。传统的账号密码登录方式存在注册流程繁琐、密码记忆负担等问题,而微信登录凭借其庞大的用户基础和便捷的授权机制,成为…

2026/10/28 3:47:53

Linux源码编译安装:核心价值、工具链与进阶实践

1. Linux源码编译安装的核心价值与场景定位在Linux系统管理中,源码编译安装就像亲手组装一台精密仪器,相比直接使用预编译的二进制包,它能带来三个维度的显著优势:第一是版本控制的灵活性。当我们需要最新版的Nginx体验QUIC协议支…

2026/10/28 3:47:53

娇兰impeccable:蜂巢、蜂胶与蜂蜜调香的工艺秘密

如果把市面上所有以蜜蜂为灵感的香水排成一排,让一个不懂香水的人来挑,大多数人会挑最甜的那支。甜,几乎成了蜂蜜香气的刻板印象。可有一次我在试香纸上闻到一支名为impeccable的作品时,旁边的人反复确认了三遍它到底是不是一款蜂…

2026/10/28 3:47:53

impeccable用法全解析:发音、搭配与写作指南

1. 为什么这个看似简单的词突然成了热搜常客1.1 先厘清"impeccable"的确切含义如果你只在词典里查过这个词,大概率看到的是"无懈可击的、完美无缺的"这串中文释义。这样理解没大问题,但放到真实使用场景里,你会发现它的语…

2026/10/28 3:42:50

神通数据库OSCAR安装部署全流程:从环境准备到实例初始化

国产数据库这两年在信创项目里落地得越来越密集,神通数据库(OSCAR)作为其中比较有代表性的一款,很多团队第一次接触它,往往卡在最不起眼的环节——安装包部署。我前后在几套不同的服务器环境里做过神通数据库的部署&am…

2026/10/27 14:24:03

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/27 9:34:54

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/28 0:02:13

Python股票自动交易系统源码解析:从行情获取到风控的完整链路

简介:这份资源是面向高校计算机、金融相关专业毕业设计场景的完整项目源码包,主题为基于Python的股票自动交易系统,适合需要完成毕设选题、课程设计或自学金融量化交易开发的学生与开发者参考。压缩包共收录687个文件,整体约14.93…

2026/10/28 0:02:13

视酷酷信IM源码部署指南:Java 17+MySQL 8.0.33实战配置

简介:这是一套2024年亲测可部署的视酷酷信IM即时通讯系统源码,面向具备Java、OC、C#及Vue开发能力的技术人员,解决私有化即时通讯平台快速搭建与二次定制需求,尤其适用于需集成商城、保障数据主权的中小型企业或独立开发者。资源共…

2026/10/28 0:02:13

Python爬取百度迁徙数据:接口解析、批量采集与避坑指南

简介:这份资源是一个用Python爬取百度迁徙数据的脚本压缩包,面向具备Python基础、希望入门网络爬虫与数据解析的开发者,解决按日期获取全国热门城市人口迁徙数据的问题。包内共1个文件,为单个py脚本,压缩包约1KB&#…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑