直流减速电机 + Arduino 接线方案
总体接线图12V电源 ├── BTS7960 → 37GB545D电机动力线 └── LM2596 → 5V → Arduino / 编码器 / 传感器 Arduino ├── D5 → BTS7960 RPWM ├── D6 → BTS7960 LPWM ├── D2 ← 编码器A相 ├── D3 ← 编码器B相 ├── D7 ← HOME限位 └── A0 ← FSR压力传感器1.BTS7960 接电源和电机BTS7960连接B12V电源正极B-12V电源负极M电机动力线1M-电机动力线22.BTS7960 接 ArduinoBTS7960Arduino作用VCC5V逻辑电源GNDGND信号地RPWMD5正转PWMLPWMD6反转PWMR_END8右桥使能L_END9左桥使能3.37GB545D 编码器接 Arduino编码器线ArduinoVCC5VGNDGNDA相D2B相D34.LM2596降压模块接线12V电源 → LM2596 IN 12V电源 - → LM2596 IN- LM2596 OUT → Arduino 5V LM2596 OUT- → Arduino GND5.FSR402压力传感器接线Arduino 5V │ FSR402 │ Arduino A0 │ 10kΩ电阻 │ GND完整功能代码/* * 37GB545D BTS7960 AB编码器 FSR402 * 控制流程 * 上电回零 - FSR待机检测 - 闭合剪切 * - 保持 - 张开 - 等待压力释放 - 再次待机 * * Arduino Uno 引脚 * D2 - 编码器A * D3 - 编码器B * D5 - BTS7960 RPWM * D6 - BTS7960 LPWM * D7 - HOME限位开关可以通过宏关闭 * D8 - BTS7960 R_EN * D9 - BTS7960 L_EN * A0 - FSR402分压输出 */ // // 1. 功能选择 // // 使用HOME限位开关1 // 不使用HOME限位开关0 #define USE_HOME_SWITCH 1 // 编码器方向修正 // 正向运行时计数增加设为1 // 正向运行时计数减少改为-1 #define ENCODER_SIGN 1 // // 2. 引脚定义 // #define ENC_A_PIN 2 #define ENC_B_PIN 3 #define RPWM_PIN 5 #define LPWM_PIN 6 #define HOME_PIN 7 #define R_EN_PIN 8 #define L_EN_PIN 9 #define FSR_PIN A0 // // 3. 位置参数 // // 回零后张开位置为0 #define OPEN_POSITION 0L // 闭合位置必须根据你的机构重新标定 // 第一次测试先设置小一点例如100200 #define CLOSE_POSITION 200L // 位置允许误差 #define DEAD_ZONE 6L // // 4. 电机控制参数 // // 位置P控制增益 #define KP 0.8f // 电机克服减速箱静摩擦所需的最低PWM #define MIN_PWM 50 // 限制最大PWM避免一开始速度过快 #define MAX_PWM 160 // 回零速度 // 如果回零方向反了把-70改成70 #define HOME_PWM -70 // // 5. 时间参数 // #define HOME_TIMEOUT 6000UL #define CLOSE_TIMEOUT 4000UL #define OPEN_TIMEOUT 4000UL #define HOLD_TIME 300UL // FSR上电空载校准时间 #define FSR_CALIBRATION_TIME 2000UL // FSR采样周期 #define FSR_SAMPLE_INTERVAL 20UL // // 6. FSR参数 // // 触发阈值 空载基准 触发偏移 #define FSR_TRIGGER_OFFSET 100 // 释放阈值必须小于触发偏移形成滞回 #define FSR_RELEASE_OFFSET 40 // 连续多少次超过阈值才触发 #define TRIGGER_CONFIRM_COUNT 5 // 连续多少次低于释放阈值才认为果梗已离开 #define RELEASE_CONFIRM_COUNT 10 // // 7. 编码器变量 // volatile long encoderCount 0; volatile uint8_t previousEncoderState 0; /* * 正交编码器状态转换表。 * 前两位为上一次AB状态后两位为当前AB状态。 */ const int8_t QUADRATURE_TABLE[16] { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 }; // // 8. 状态机 // enum CutterState { STATE_HOMING, STATE_IDLE, STATE_CLOSING, STATE_HOLDING, STATE_OPENING, STATE_WAIT_RELEASE, STATE_ERROR }; CutterState currentState STATE_IDLE; unsigned long stateStartTime 0; unsigned long lastFSRSampleTime 0; unsigned long lastPrintTime 0; int basePressure 0; int currentPressure 0; uint8_t triggerConfirmCounter 0; uint8_t releaseConfirmCounter 0; bool manualCutRequest false; // // 9. 初始化 // void setup() { Serial.begin(115200); // BTS7960 pinMode(RPWM_PIN, OUTPUT); pinMode(LPWM_PIN, OUTPUT); pinMode(R_EN_PIN, OUTPUT); pinMode(L_EN_PIN, OUTPUT); setMotor(0); enableMotorDriver(true); // 编码器 pinMode(ENC_A_PIN, INPUT_PULLUP); pinMode(ENC_B_PIN, INPUT_PULLUP); previousEncoderState (digitalRead(ENC_A_PIN) 1) | digitalRead(ENC_B_PIN); attachInterrupt( digitalPinToInterrupt(ENC_A_PIN), encoderISR, CHANGE ); attachInterrupt( digitalPinToInterrupt(ENC_B_PIN), encoderISR, CHANGE ); // HOME开关 pinMode(HOME_PIN, INPUT_PULLUP); Serial.println(F()); Serial.println(F(37GB545D剪切控制系统启动)); Serial.println(F(请确保FSR上没有果梗或其他物体)); Serial.println(F()); calibrateFSR(); #if USE_HOME_SWITCH enterState(STATE_HOMING); Serial.println(F(开始自动回零)); #else /* * 不使用HOME开关时 * 上电前必须人工把刀片放到完全张开位置。 */ writeEncoderCount(0); enterState(STATE_IDLE); Serial.println(F(未启用HOME开关)); Serial.println(F(已将上电位置设为张开零点)); #endif Serial.println(F(串口命令)); Serial.println(F(C强制执行一次剪切)); Serial.println(F(S软件停止)); Serial.println(F(R故障复位)); Serial.println(F(Z将当前位置设为编码器零点)); } // // 10. 主循环 // void loop() { unsigned long now millis(); handleSerialCommand(); updatePressure(now); printStatus(now); switch (currentState) { // -------------------------------------------------- // 自动回零 // -------------------------------------------------- case STATE_HOMING: #if USE_HOME_SWITCH if (digitalRead(HOME_PIN) LOW) { setMotor(0); writeEncoderCount(0); Serial.println(F(回零完成编码器已清零)); enterState(STATE_IDLE); } else if (now - stateStartTime HOME_TIMEOUT) { enterError(F(故障回零超时)); } else { setMotor(HOME_PWM); } #else setMotor(0); writeEncoderCount(0); enterState(STATE_IDLE); #endif break; // -------------------------------------------------- // 等待FSR触发 // -------------------------------------------------- case STATE_IDLE: positionControl(OPEN_POSITION); if (manualCutRequest) { manualCutRequest false; triggerConfirmCounter 0; Serial.println(F(串口命令触发剪切)); enterState(STATE_CLOSING); } else if (pressureTriggerConfirmed()) { Serial.println(F(FSR检测到果梗开始剪切)); enterState(STATE_CLOSING); } break; // -------------------------------------------------- // 闭合剪切 // -------------------------------------------------- case STATE_CLOSING: if (positionControl(CLOSE_POSITION)) { setMotor(0); Serial.println(F(到达闭合位置)); enterState(STATE_HOLDING); } else if (now - stateStartTime CLOSE_TIMEOUT) { enterError(F(故障闭合动作超时)); } break; // -------------------------------------------------- // 剪切保持 // -------------------------------------------------- case STATE_HOLDING: positionControl(CLOSE_POSITION); if (now - stateStartTime HOLD_TIME) { Serial.println(F(保持完成开始张开)); enterState(STATE_OPENING); } break; // -------------------------------------------------- // 张开复位 // -------------------------------------------------- case STATE_OPENING: #if USE_HOME_SWITCH /* * 如果张开过程中先碰到HOME开关 * 立即停止并重新清零。 */ if (digitalRead(HOME_PIN) LOW) { setMotor(0); writeEncoderCount(0); Serial.println(F(HOME开关触发张开完成)); enterState(STATE_WAIT_RELEASE); break; } #endif if (positionControl(OPEN_POSITION)) { setMotor(0); Serial.println(F(张开位置到达)); enterState(STATE_WAIT_RELEASE); } else if (now - stateStartTime OPEN_TIMEOUT) { enterError(F(故障张开动作超时)); } break; // -------------------------------------------------- // 等待果梗或物体离开FSR // -------------------------------------------------- case STATE_WAIT_RELEASE: positionControl(OPEN_POSITION); if (pressureReleaseConfirmed()) { Serial.println(F(压力已释放重新进入待机)); enterState(STATE_IDLE); } break; // -------------------------------------------------- // 故障状态 // -------------------------------------------------- case STATE_ERROR: setMotor(0); break; } } // // 11. 电机驱动函数 // void enableMotorDriver(bool enable) { digitalWrite(R_EN_PIN, enable ? HIGH : LOW); digitalWrite(L_EN_PIN, enable ? HIGH : LOW); } /* * speed范围-255255 * 正数一个方向 * 负数相反方向 * 0停止 */ void setMotor(int speedValue) { speedValue constrain(speedValue, -255, 255); if (speedValue 0) { analogWrite(RPWM_PIN, speedValue); analogWrite(LPWM_PIN, 0); } else if (speedValue 0) { analogWrite(RPWM_PIN, 0); analogWrite(LPWM_PIN, -speedValue); } else { analogWrite(RPWM_PIN, 0); analogWrite(LPWM_PIN, 0); } } // // 12. 位置闭环控制 // /* * 返回true到达目标位置 * 返回false尚未到达 */ bool positionControl(long targetPosition) { long currentPosition readEncoderCount(); long error targetPosition - currentPosition; if (labs(error) DEAD_ZONE) { setMotor(0); return true; } int controlOutput (int)(KP * error); controlOutput constrain( controlOutput, -MAX_PWM, MAX_PWM ); /* * 小误差时P控制输出可能太小 * 电机无法克服减速箱摩擦因此设置最低PWM。 */ if (controlOutput 0 controlOutput MIN_PWM) { controlOutput MIN_PWM; } if (controlOutput 0 controlOutput -MIN_PWM) { controlOutput -MIN_PWM; } setMotor(controlOutput); return false; } // // 13. 编码器中断 // void encoderISR() { uint8_t currentEncoderState (digitalRead(ENC_A_PIN) 1) | digitalRead(ENC_B_PIN); uint8_t tableIndex (previousEncoderState 2) | currentEncoderState; encoderCount ENCODER_SIGN * QUADRATURE_TABLE[tableIndex]; previousEncoderState currentEncoderState; } /* * Arduino Uno是8位单片机 * 读取4字节long变量时需要临时关闭中断。 */ long readEncoderCount() { noInterrupts(); long value encoderCount; interrupts(); return value; } void writeEncoderCount(long value) { noInterrupts(); encoderCount value; interrupts(); } // // 14. FSR空载校准 // void calibrateFSR() { unsigned long calibrationStart millis(); long pressureSum 0; unsigned int sampleCount 0; while (millis() - calibrationStart FSR_CALIBRATION_TIME) { pressureSum analogRead(FSR_PIN); sampleCount; delay(10); } if (sampleCount 0) { basePressure pressureSum / sampleCount; } currentPressure basePressure; Serial.print(F(FSR空载基准值)); Serial.println(basePressure); Serial.print(F(FSR触发阈值)); Serial.println( basePressure FSR_TRIGGER_OFFSET ); Serial.print(F(FSR释放阈值)); Serial.println( basePressure FSR_RELEASE_OFFSET ); } // // 15. FSR读取和触发判断 // void updatePressure(unsigned long now) { if (now - lastFSRSampleTime FSR_SAMPLE_INTERVAL) { return; } lastFSRSampleTime now; currentPressure analogRead(FSR_PIN); } bool pressureTriggerConfirmed() { int triggerThreshold basePressure FSR_TRIGGER_OFFSET; if (currentPressure triggerThreshold) { if (triggerConfirmCounter TRIGGER_CONFIRM_COUNT) { triggerConfirmCounter; } } else { triggerConfirmCounter 0; } if (triggerConfirmCounter TRIGGER_CONFIRM_COUNT) { triggerConfirmCounter 0; return true; } return false; } bool pressureReleaseConfirmed() { int releaseThreshold basePressure FSR_RELEASE_OFFSET; if (currentPressure releaseThreshold) { if (releaseConfirmCounter RELEASE_CONFIRM_COUNT) { releaseConfirmCounter; } } else { releaseConfirmCounter 0; } if (releaseConfirmCounter RELEASE_CONFIRM_COUNT) { releaseConfirmCounter 0; return true; } return false; } // // 16. 状态切换和故障处理 // void enterState(CutterState newState) { currentState newState; stateStartTime millis(); triggerConfirmCounter 0; releaseConfirmCounter 0; } void enterError(const __FlashStringHelper *message) { setMotor(0); enableMotorDriver(false); currentState STATE_ERROR; Serial.println(message); Serial.println(F(发送R命令进行复位)); } // // 17. 串口调试命令 // void handleSerialCommand() { while (Serial.available() 0) { char command Serial.read(); if (command a command z) { command command - a A; } switch (command) { case C: if (currentState STATE_IDLE) { manualCutRequest true; } break; case S: enterError(F(软件停止命令触发)); break; case R: setMotor(0); enableMotorDriver(true); #if USE_HOME_SWITCH Serial.println(F(故障复位重新回零)); enterState(STATE_HOMING); #else Serial.println(F(故障复位当前位置设为零)); writeEncoderCount(0); enterState(STATE_IDLE); #endif break; case Z: if (currentState STATE_IDLE || currentState STATE_ERROR) { setMotor(0); writeEncoderCount(0); Serial.println(F(当前位置已设为编码器零点)); } break; } } } // // 18. 串口状态打印 // void printStatus(unsigned long now) { if (now - lastPrintTime 250) { return; } lastPrintTime now; Serial.print(F(状态)); Serial.print((int)currentState); Serial.print(F( 编码器)); Serial.print(readEncoderCount()); Serial.print(F( FSR)); Serial.println(currentPressure); }调试校准步骤第一步基础硬件验证不通单片机先测电源给 12V 上电测 LM2596 输出是否为 5V确认无误后再接 Arduino。电机空载测试注释掉回零逻辑直接用setMotor(100)和setMotor(-100)测试正反转确认电机转向、接线无错误。编码器验证打开串口监视器手动转动电机轴观察脉冲数是否连续增减无跳数、无反向。第二步机械参数校准脉冲数标定让电机转一整圈记录encoderCount变化值替换PULSES_PER_REV。行程标定手动让剪刀完全张开记录脉冲数 → 设为OPEN_POSITION手动让剪刀完全闭合刀刃交错到位记录脉冲数 → 设为CLOSE_POSITION压力阈值校准参考你之前的校准方法修改FSR_THRESHOLD。第三步闭环与回零调试先测试回零功能确认触发限位后电机立即停止计数清零。再测试位置闭环发送指令让电机走到指定位置观察是否平稳到位、无振荡。最后联调 FSR 触发验证完整剪切流程。

相关新闻

FS800DTU 开箱分享 — 随手拍随手写的体验贴

FS800DTU 开箱分享 — 随手拍随手写的体验贴

目录 前言1 拆快递:这板子到底长啥样2 接线:全家福来一张3 上电:亮了就是成功了一半4 配置:填几个数就能连上5 跑起来:发个 hello 试试6 几句闲话 前言 手头刚好收到一块飞思创的 FS800DTU 核心板,想着反…

2026/10/13 17:40:17 阅读更多 →
UGEE UE12数位板评测:16K压感手绘屏的专业创作体验

UGEE UE12数位板评测:16K压感手绘屏的专业创作体验

最近在数字绘画和设计领域,很多专业美术生和插画师都在寻找性价比高、性能稳定的手绘设备。作为长期从事数字创作的技术博主,我经常被问到如何选择合适的手绘板或手绘屏。今天就来详细评测一款备受关注的设备——UGEE友基UE12数位板,看看它是…

2026/10/13 17:49:48 阅读更多 →
Web应用UGC内容安全处理:特殊字符过滤与XSS防护实战

Web应用UGC内容安全处理:特殊字符过滤与XSS防护实战

最近在开发一个社交类应用时,遇到了用户昵称和状态动态展示的需求,特别是需要处理一些包含特殊字符、网络流行语或特定组合的文本内容。这类文本往往需要后端进行有效的解析、存储和安全过滤,同时前端要能正确渲染。本文将以一个典型的用户状…

2026/10/14 8:42:09 阅读更多 →

最新新闻

Java高并发编程核心原理,初学者如何一周速通?

Java高并发编程核心原理,初学者如何一周速通?

不知道大家最近去面试过没有?有去面试过的小伙伴应该会知道现在互联网企业招聘对于“高并发”这块的考察可以说是越来越注重了。基本上你简历上有高并发相关经验,就能成为企业优先考虑的候选人。其原因在于,企业真正需要的是能独立解决问题的…

2026/10/15 0:24:18 阅读更多 →
让智能体在“历史回放”中做梦:破解递归自我改进的元探索困境——读马里兰大学郑童NICE学术前沿分享

让智能体在“历史回放”中做梦:破解递归自我改进的元探索困境——读马里兰大学郑童NICE学术前沿分享

NICE学术 前沿对话 | 演讲人:郑童(马里兰大学帕克分校博士生、Dream-RSI 第一作者) | 议题:Dream-RSI:让智能体通过“做梦”实现递归自我改进核心洞察:长程自主智能体实现递归自我改进(RSI&…

2026/10/15 0:24:18 阅读更多 →
把剪辑写成代码:用Opus 5.5重新定义视频工作流

把剪辑写成代码:用Opus 5.5重新定义视频工作流

以前剪视频的时候,我手上全是鼠标:时间轴上一点一点拖素材、剃刀切来切去、关键帧一个个打,一个30秒的口播片子能磨一下午。后来我换了思路,用 Opus 5.5 做视频,整个工作流彻底变了样——我不再对着画面戳来戳去&#…

2026/10/15 0:23:18 阅读更多 →
Java 转 Rust 异步篇:3 个任务从 3.015s 到 1.006s,CompletableFuture/虚拟线程 vs tokio 一次讲透(附真实报错)

Java 转 Rust 异步篇:3 个任务从 3.015s 到 1.006s,CompletableFuture/虚拟线程 vs tokio 一次讲透(附真实报错)

Java 转 Rust 异步篇:3 个任务从 3.015s 到 1.006s,CompletableFuture/虚拟线程 vs tokio 一次讲透(附真实报错) 一句话结论:Java 处理异步有三套方案(线程池、CompletableFuture、虚拟线程)&am…

2026/10/15 0:23:18 阅读更多 →
汉字拓扑密码:克莱因瓶与部首粒子的字族演化

汉字拓扑密码:克莱因瓶与部首粒子的字族演化

很多人觉得“汉字是二维的”这句话不用论证——方块字嘛,一笔一画落在纸面上,看起来就是一个标准的平面符号系统。但你要是真的拆过字、抠过字理,就会开始怀疑这个结论。“回”字里面套着一个口,外面再套一个口,纸面上…

2026/10/15 0:22:17 阅读更多 →
手写代码结束,做东西的人刚开始:从编码者到构建者的能力跃迁

手写代码结束,做东西的人刚开始:从编码者到构建者的能力跃迁

1. 这句话到底在说什么“手写代码结束了,做东西的人刚开始”——第一次看到这句话,我盯着屏幕愣了好几秒。不是因为它多深奥,恰恰相反,它太直白了,直白到像一盆冷水浇在每一个还在键盘上敲敲打打的人头上。我做了十多年…

2026/10/15 0:22:17 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →