如何保留原代码功能并限制360°MG996R舵机旋转至180°
解决MG996R舵机360°转180°限制及鼠标控制问题
核心问题说明
你用的MG996R如果是改成了连续旋转模式(和Michael Reeves视频里的炮塔配置一致),这类舵机本身没有内置位置反馈,单纯在C#端发送0-180的数值只能控制转速和方向,没法直接限制旋转范围。之前尝试的if语句没生效,本质原因是缺少Arduino端的位置追踪与限位逻辑。
完整解决方案
1. C#代码优化(鼠标控制逻辑)
调整代码,确保只发送有效的目标角度,减少冗余串口通信:
public Stopwatch watch { get; set; } private Point previousMousePosition; private bool servoMoving; // 记录当前舵机目标角度,避免重复发送指令 private int currentTargetX = 90; private int currentTargetY = 90; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { watch = Stopwatch.StartNew(); port.Open(); previousMousePosition = Cursor.Position; servoMoving = false; // 初始化舵机到中间位置(90°) port.Write("X90Y90"); } private void Form1_MouseMove(object sender, MouseEventArgs e) { writeToPort(new Point(e.X, e.Y)); } public void writeToPort(Point coordinates) { if (watch.ElapsedMilliseconds > 15) { watch = Stopwatch.StartNew(); // 将鼠标坐标映射为0-180的目标角度 int targetX = (int)(coordinates.X * 180.0 / Size.Width); int targetY = (int)(coordinates.Y * 180.0 / Size.Height); // 强制限制在0-180范围内 targetX = Math.Max(0, Math.Min(180, targetX)); targetY = Math.Max(0, Math.Min(180, targetY)); // 仅当目标角度变化时发送指令 if (targetX != currentTargetX || targetY != currentTargetY) { port.Write(String.Format("X{0}Y{1}", targetX, targetY)); currentTargetX = targetX; currentTargetY = targetY; servoMoving = true; } else { servoMoving = false; } } else { servoMoving = false; } }
2. Arduino代码(位置追踪与限位)
这是实现180°限制的关键,通过计时估算舵机当前角度,到达边界时强制停止:
#include <Servo.h> Servo servoX; Servo servoY; // 舵机引脚定义 const int servoXPin = 9; const int servoYPin = 10; // 角度边界限制 const int minAngle = 0; const int maxAngle = 180; // 当前舵机角度(初始为中间位置) int currentXAngle = 90; int currentYAngle = 90; // 舵机转速参数:每毫秒转动的角度(需根据实际测试调整) const float speedPerMs = 0.1; unsigned long lastMoveTime = 0; int lastTargetX = 90; int lastTargetY = 90; void setup() { Serial.begin(9600); servoX.attach(servoXPin); servoY.attach(servoYPin); // 初始化舵机到中间位置 servoX.write(90); servoY.write(90); } void loop() { if (Serial.available() > 0) { String input = Serial.readStringUntil('\n'); // 解析C#发送的指令格式:X[目标角度]Y[目标角度] int xIndex = input.indexOf('X'); int yIndex = input.indexOf('Y'); if (xIndex != -1 && yIndex != -1) { int targetX = input.substring(xIndex+1, yIndex).toInt(); int targetY = input.substring(yIndex+1).toInt(); // 控制X轴舵机移动 moveServo(servoX, targetX, currentXAngle); // 控制Y轴舵机移动 moveServo(servoY, targetY, currentYAngle); lastTargetX = targetX; lastTargetY = targetY; lastMoveTime = millis(); } } // 持续更新舵机角度,直到到达目标或边界 unsigned long currentTime = millis(); unsigned long elapsed = currentTime - lastMoveTime; if (elapsed > 0) { updateAngle(lastTargetX, currentXAngle, elapsed); updateAngle(lastTargetY, currentYAngle, elapsed); lastMoveTime = currentTime; } } // 控制舵机转向 void moveServo(Servo &servo, int target, int ¤tAngle) { if (target > currentAngle) { // 顺时针转动(数值越接近180转速越快) servo.write(110); } else if (target < currentAngle) { // 逆时针转动(数值越接近0转速越快) servo.write(70); } else { // 停止转动 servo.write(90); } } // 更新角度并触发限位 void updateAngle(int target, int ¤tAngle, unsigned long elapsed) { if (target > currentAngle) { int delta = speedPerMs * elapsed; currentAngle += delta; // 到达目标或上限时停止 if (currentAngle >= target || currentAngle >= maxAngle) { currentAngle = min(target, maxAngle); stopServo(currentAngle == currentXAngle ? servoX : servoY); } } else if (target < currentAngle) { int delta = speedPerMs * elapsed; currentAngle -= delta; // 到达目标或下限时停止 if (currentAngle <= target || currentAngle <= minAngle) { currentAngle = max(target, minAngle); stopServo(currentAngle == currentXAngle ? servoX : servoY); } } } // 停止舵机 void stopServo(Servo &servo) { servo.write(90); }
3. 调试注意事项
- 转速参数调整:
speedPerMs需要根据你的舵机实际转速测试修改,比如让舵机转90度计时,计算每毫秒转动的角度。 - 转速控制:
moveServo函数中的110和70是转速调节值,数值越接近90转速越慢,远离则越快,可根据需求微调。 - 限位逻辑:当舵机转到0°或180°时会自动停止,只有鼠标反向拖动时才会再次移动,避免线缆缠绕。
内容的提问来源于stack exchange,提问作者havi hugot
相关产品推荐
相关产品推荐

