You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何保留原代码功能并限制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 &currentAngle) {
  if (target > currentAngle) {
    // 顺时针转动(数值越接近180转速越快)
    servo.write(110);
  } else if (target < currentAngle) {
    // 逆时针转动(数值越接近0转速越快)
    servo.write(70);
  } else {
    // 停止转动
    servo.write(90);
  }
}

// 更新角度并触发限位
void updateAngle(int target, int &currentAngle, 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 11:27:13