基于stm32的光点运动轨迹控制

前言

步进电机画圆、直线。使用的简单的模拟脉冲方式快速实现的步进电机运动控制,如有不足的地方欢迎补充。

任务分析

1.任务
制作一个激光笔点二维控制装置,示意如图所示。在50cm*50cm靶纸上,用激光笔投射一光点,激光笔距离靶纸1米。要求能按指定的误差范围将光点定位在靶纸上任意一点,并在限定的条件下将光点按指定轨迹运动。
在这里插入图片描述
2.设备方案
主控采用stm32f411retx。控制装置为二自由度旋转式云台。使用42步进电机以及TB6600电机驱动器进行32细分。
3.基本原理
直线运动插补与圆弧运动插补采用的逐点比较法,这里只要注意一点就是激光笔是在云台上进行控制,控制x轴与y轴的电机转动时,需根据实际定位的坐标换算成电机当前相对初始状态要转动的角度。这里让激光笔初始垂直照射到原点位置,如下图。
在这里插入图片描述
tan(angle_x) = x / o p x / op x/op
tan(angle_y) = y / ( x 2 + o p 2 ) y / \sqrt(x^2+op^2) y/( x2+op2)
使用math.h里的atan可以算出angle值
angle_x = a t a n ( x / o p ) ∗ 180 / P I atan(x / op) * 180 / PI atan(x/op)180/PI;
angle_y = a t a n ( y / ( x 2 + o p 2 ) ) ∗ 180 / P I atan(y / \sqrt(x^2+op^2)) * 180 / PI atan(y/( x2+op2))180/PI

4.关键代码

#define Step_one 1 //单步步进值
static float now_x_step = 0, now_y_step = 0;  //记忆当前步数

/*
 * x
 * CCW对应x轴正方向
 */
void stepper_x_run(int tim,float step,float subdivide,uint8_t dir)
{
  int i;
  if(step < 0.5)
    return;
  if(dir == CW)
    MOTOR_X_DIR(CW);
  else if(dir == CCW)
    MOTOR_X_DIR(CCW);
  osDelay(2);
  for(i = 0; i < step; i++)
  {
    if(dir == CW)
      now_x_step--;
    else if(dir == CCW)
      now_x_step++;
    MOTOR_X_PUL(HIGH);
    osDelay(tim / 2);
    MOTOR_X_PUL(LOW);
    osDelay(tim / 2);
  }
}
/*
 * y
 * CW对应y轴正方向
 */
void stepper_y_run(int tim, float step, float subdivide, uint8_t dir)
{
  int i;
  if(step < 0.5)
    return;
  if(dir == CW)
    MOTOR_Y_DIR(CW);
  else if(dir == CCW)
    MOTOR_Y_DIR(CCW);
  osDelay(2);
  for(i = 0; i < step; i++)
  {
    if(dir == CW)
      now_y_step++;
    else if(dir == CCW)
      now_y_step--;

    MOTOR_Y_PUL(HIGH);
    osDelay(tim / 2);
    MOTOR_Y_PUL(LOW);
    osDelay(tim / 2);
  }
}
/**
*定点函数
*/
void turn_coordinate(float x, float y)
{
  float angle_x, angle_y;
  float step_x, step_y;
  float dx, dy;
  float sqx;

  arm_sqrt_f32(1050 * 1050 + x * x, &sqx);
  angle_x = atan(x / 1050) * 180 / PI;
  angle_y = atan(y / sqx) * 180 / PI;

  step_x = angle_x / 0.05625;//计算对应步数,与0相差
  step_y = angle_y / 0.05625;

  dx = step_x - now_x_step;
  dy = step_y - now_y_step;

  if(dx > 0)
    stepper_x_run(2, dx, 32, CCW);
  else if(dx < 0)
    stepper_x_run(2, -dx, 32, CW);

  if(dy > 0)
    stepper_y_run(2, dy, 32, CW);
  else if(dy < 0)
    stepper_y_run(2, -dy, 32, CCW);
}
/*
 * @brief:直线运动插补
 * @parameter:起点坐标(X0, Y0),终点坐标(Xe, Ye)
 * @return: 无
 * */
void drawline(float X0, float Y0, float Xe, float Ye)
{
  float NXY;              //总步数
  float Fm = 0;           //偏差
  float Xm = X0, Ym = Y0; //当前坐标
  uint8_t XOY;            //象限

  Xe = Xe - X0;
  Ye = Ye - Y0;
  NXY = (fabsf(Xe) + fabsf(Ye)) / Step_one;

  if(Xe > 0 && Ye >= 0) XOY = 1;
  else if(Xe <= 0 && Ye > 0) XOY = 2;
  else if(Xe < 0 && Ye <= 0) XOY = 3;
  else if(Xe >= 0 && Ye < 0) XOY = 4;

  while(NXY > 0)
  {
    switch (XOY)
    {
    case 1: (Fm >= 0) ? (Xm += Step_one) : (Ym += Step_one); break;
    case 2: (Fm <  0) ? (Xm -= Step_one) : (Ym += Step_one); break;
    case 3: (Fm >= 0) ? (Xm -= Step_one) : (Ym -= Step_one); break;
    case 4: (Fm <  0) ? (Xm += Step_one) : (Ym -= Step_one); break;
    default: break;
    }
    NXY -= 1;
    Fm = (Ym - Y0) * Xe - (Xm - X0) * Ye;
    turn_coordinate(Xm, Ym);
    osDelay(2);
  }
}
/*
 * @brief:圆运动插补
 * @parameter:圆心坐标(x0, y0),半径 R, 方向 SorN 1 顺时针 2 逆时针
 * @return: 无
 * */
void drawcircle(float x0, float y0, float R, uint8_t SorN)
{
  float X0, Y0, Xe, Ye;
  float step = 0;
  float Fm = 0;
  float Xm, Ym;
  uint8_t XOY;

  X0 = x0; Y0 = y0 + R;  //开始点
  Xe = x0; Ye = y0 + R;  //结束点
  Xm = X0; Ym = Y0;

  while (pow((Xm - Xe), 2) + pow((Ym - Ye), 2) > Step_one * Step_one / 2 || (step == 0))
  {
    if ((Xm - x0) > 0 && (Ym - y0) >= 0) XOY = 1;
    else if ((Xm - x0) <= 0 && (Ym - y0) > 0) XOY = 2;
    else if ((Xm - x0) < 0 && (Ym - y0) <= 0) XOY = 3;
    else if ((Xm - x0) >= 0 && (Ym - y0) < 0) XOY = 4;

    switch (XOY)
    {
    case 1:
      if(SorN == 1)
        (Fm >= 0) ? (Ym -= Step_one) : (Xm += Step_one);
      else
        (Fm <= 0) ? (Ym += Step_one) : (Xm -= Step_one);
      break;
    case 2:
      if(SorN == 1)
        (Fm >= 0) ? (Xm += Step_one) : (Ym += Step_one);
      else
        (Fm >  0) ? (Ym -= Step_one) : (Xm -= Step_one);
      break;
    case 3:
      if(SorN == 1)
        (Fm >= 0) ? (Ym += Step_one) : (Xm -= Step_one);
      else
        (Fm >  0) ? (Xm += Step_one) : (Ym -= Step_one);
      break;
    case 4:
      if(SorN == 1)
        (Fm >= 0) ? (Xm -= Step_one) : (Ym -= Step_one);
      else
        (Fm >  0) ? (Ym += Step_one) : (Xm += Step_one);
    default: break;
    }
    step = step + 1;
    Fm = pow((Xm - x0), 2) + pow((Ym - y0), 2) - pow(R, 2);
    turn_coordinate(Xm, Ym);
    osDelay(2);
  }
}

结论与现象

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
右边有一点点多出来是因为板子右边往后弯了些。思路大概为:写好一个定点函数,根据所给坐标更新步进电机的当前对应角度。然后在直线与圆弧插补里面不断调用定点函数来进行插补。如果使用PWM脉冲中断去控制电机步进。开环控制改为闭环控制,如带编码器的伺服电机,精度也许会更高。

附下工程:https://github.com/YAOSIYANAD/Light_spot_motion_control

  • 87
    点赞
  • 486
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 50
    评论
STM32是一款广泛应用于嵌入式系统开发的微控制器,它可以通过各种外设模块来实现对舵机的控制。舵机是一种能够转动特定角度的电动装置,通常用于机器人控制、遥控模型等领域。 要在STM32上实现舵机光点运动轨迹控制,我们首先需要连接舵机到STM32的GPIO口上,并选用适当的驱动模式。然后我们可以使用PWM(脉冲宽度调制)信号来控制舵机的转动角度。 为了实现运动轨迹控制,我们可以在程序中定义一组规定的坐标点,并通过计算相邻坐标点之间的差值来控制舵机的转动角度。假设我们需要舵机光点从起点移动到终点,我们可以先计算出x轴和y轴方向上的角度差值,然后分别发送PWM信号给舵机,使之先在x轴上转到目标点,然后在y轴上转到目标点。 在代码中,我们可以利用定时器来产生PWM信号,通过改变占空比来控制舵机的转动角度。可以根据具体的舵机型号和规格进行调试和参数设置,以保证转动角度的准确性和精度。 总结起来,使用STM32实现舵机光点运动轨迹控制需要连接舵机到GPIO口,并使用PWM信号来控制舵机的转动角度。通过定义一组规定的坐标点和计算角度差值,我们可以控制舵机按照指定的轨迹移动。同时,通过定时器生成PWM信号,我们可以精确地控制舵机的转动角度,实现精准的运动轨迹控制

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__蚩尤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值