MSPM0开发学习笔记:二维云台画图(2025电赛 附源代码及引脚配置)

为备战 2025 电赛云台类题目,作者用 MSPM0G3507 搭配 D36A 双通道驱动两颗 42 步进电机,自研三维打印桁架组装二维云台,并分享了当前只做轨迹跟随、尚未挂载激光绘图头时的软硬件连线、引脚映射与画图控制思路。


前言

今年的电赛(2025),很多题都与云台相关,因此为备战电赛,博主这边也是准备了一个由两个42步进电机驱动的云台并提前进行调试,避免赛题出来之后手忙脚乱的,这边的两个42步进电机采用同一个驱动模块进行驱动(D36A),主控肯定采用MSPM0G3507。然后3D打印了一个二维云台的结构并进行组装。本章博客主要是讲这个云台进行绘图的思路以及代码。激光还没有安装上去,目前只是云台的循迹代码


如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线

一、硬件选择

主控:MSPM0G3507
驱动:D36A双路步进电机驱动
电机:42步进电机*2

二、硬件连线

硬件连线部分在上一篇博客里面已经说过了,可以直接去上一篇里面看,这边附上链接
MSPM0开发学习笔记:D36A驱动的42步进电机二维云台(2025电赛 附源代码及引脚配置)

三、软件代码

软件部分采用C语言实现,IDE采用keil,基于逐飞库进行编写
这边先进行一下简单的参数说明,便于理解后面的思路

参数含义以及作用
current_x目前的X坐标
current_y目前的Y坐标
target_x需要移动到的X坐标
target_y需要移动到的Y坐标
move_x需要移动的X距离
move_y需要移动的Y距离
point_count绘制圆形时候的点数

代码实现如下:

函数一:限幅函数

#define MAX_ANGLE_X  1000.0f   
#define MIN_ANGLE_X -1000.0f   
#define MAX_ANGLE_Y  1000.0f  
#define MIN_ANGLE_Y -1000.0f  


float limit_angle(float angle, float min, float max) {
    if (angle < min) return min;
    if (angle > max) return max;
    return angle;
}

函数二:激光云台绘制正方形

// Function to draw a square trajectory
// Parameters:
//   x_len - length of the square's X-axis dimension
//   y_len - length of the square's Y-axis dimension
//   MOVE_SPEED - speed of movement between points
void draw_square(int x_len, int y_len, int MOVE_SPEED) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;
    
    // Invert Y-axis length (likely for coordinate system adjustment)
    y_len = y_len * -1;
    
    // Calculate half-lengths for easier coordinate calculation
    float x_len_2 = x_len / 2;
    float y_len_2 = y_len / 2;
    
    // Define square vertices relative to origin (0,0)
    // Coordinates form a square shape when connected sequentially
    Point square_points[] = {
        {-1 * x_len_2, y_len_2},    // Top-left corner
        {x_len_2, y_len_2},         // Top-right corner
        {x_len_2, -1 * y_len_2},    // Bottom-right corner
        {-1 * x_len_2, -1 * y_len_2},// Bottom-left corner
        {-1 * x_len_2, y_len_2},    // Back to top-left to close the square
        {0.0f, 0.0f}                // Final point: return to origin
    };
    
    // Calculate total number of points in the square trajectory
    uint8 point_count = sizeof(square_points) / sizeof(Point);  
    
    // Initialize current position at origin (0,0)
    float current_x = 0;
    float current_y = 0;
    
    // Move to each defined point in sequence
    for (uint8 i = 0; i < point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(square_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(square_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);

        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;
        
        // Update current position to target coordinates
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);
        
        // Pause 300ms after reaching each point
        system_delay_ms(300);
    }
    
    // Pause 2 seconds after completing the square
    system_delay_ms(2000);
}

一、函数定义:draw_square函数接收三个参数,分别是正方形的 X 轴长度(x_len)、Y 轴长度(y_len)和移动速度(MOVE_SPEED)。
二、数据结构:定义了Point结构体用于存储坐标点的 X 和 Y 角度值。
三、坐标处理:
1、将 Y 轴长度取负值(为了调整坐标系方向)
2、计算半长(x_len_2, y_len_2),用于确定正方形顶点坐标
3、坐标定义:定义了正方形的 4 个顶点坐标和原点(0,0)坐标以中心点为原点,通过半长计算得出四个顶点位置
4、最后回到一个点 (0,0) 用于回到起点
四、绘制逻辑:
1、遍历所有定义的坐标点
2、对每个目标点进行角度限制(通过limit_angle函数确保在有效范围内)
3、计算当前位置到目标点的移动量
4、调用d36a_set_angle_both函数移动到目标点(同时设置 X 和 Y 方向角度)
5、每个点移动后延迟 300 毫秒,绘制完成后延迟 2000 毫秒

函数三:激光云台绘制圆形

// Function to draw a circle trajectory
// Parameters:
//   r - radius of the circle
//   MOVE_SPEED - speed of movement between points
//   point_count - number of points to use for drawing the circle (more = smoother)
void draw_circle(int r, int MOVE_SPEED, const uint8 point_count) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;

    // Array to store circle points (extra element to close the loop)
    Point circle_points[point_count + 1];  

    // Calculate coordinates for each point on the circle
    for (uint8 i = 0; i < point_count; i++) {
        // Convert angle from degrees to radians (full circle = 2π radians)
        float rad = 2 * 3.1415926f * i / point_count;

        // Calculate X and Y positions using trigonometric functions
        // cosine for X-axis, sine for Y-axis to form circular path
        circle_points[i].x_angle = r * cosf(rad); 
        circle_points[i].y_angle = r * sinf(rad);  
    }
    // Close the circle by duplicating the first point as the last point
    circle_points[point_count] = circle_points[0];

    // Initialize current position at origin (0,0)
    float current_x = 0.0f;  
    float current_y = 0.0f; 

    // Move to each calculated point in sequence
    for (uint8 i = 0; i <= point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(circle_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(circle_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);
        
        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;

        // Update current position to target
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

        // Optional delay between point movements
        // system_delay_ms(10);
    }
    
    // Return to origin (0,0) after completing the circle
    float target_x = 0;
    float target_y = 0;

    // Calculate movement from last circle point to origin
    float move_x = target_x - current_x;
    float move_y = target_y - current_y;

    // Update current position to origin
    current_x = target_x;
    current_y = target_y;

    // Send final movement command to return to origin
    d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

    // Pause for 2 seconds after completing the circle
    system_delay_ms(2000);  
}

通过三角函数(X 轴用余弦、Y 轴用正弦)计算圆周上指定数量的点的坐标,这些点基于圆周的等角度增量分布。函数会按顺序在这些点之间移动(带速度控制),同时确保角度在有效范围内;通过回到第一个点来闭合圆形轨迹,最后返回原点,结束时短暂暂停。点的数量越多,绘制的圆越平滑。
以下是函数中涉及的公式:

  1. 弧度计算(将圆周等分为指定数量的点)

    rad

    2 × π × i point_count \text{rad} = 2 \times \pi \times \frac{i}{\text{point\_count}} rad=2×π×point_counti​
    其中, i i i 为当前点的索引(0 到 point_count-1), point_count \text{point\_count} point_count 为圆周上的总点数, rad \text{rad} rad 为对应角度的弧度值。

  2. X轴坐标计算

    x _ a n g l e

    r × cos ⁡ ( rad ) x\_angle = r \times \cos(\text{rad}) x_angle=r×cos(rad)
    其中, r r r 为圆的半径, cos ⁡ ( rad ) \cos(\text{rad}) cos(rad) 为弧度对应的余弦值。

  3. Y轴坐标计算

    y _ a n g l e

    r × sin ⁡ ( rad ) y\_angle = r \times \sin(\text{rad}) y_angle=r×sin(rad)
    其中, sin ⁡ ( rad ) \sin(\text{rad}) sin(rad) 为弧度对应的正弦值。

四、总结

这边给的都是一些简单图形的绘制代码,但是思路都是通用的,复杂图形也可以复用这一套逻辑。大家参考参考就好

如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线

MSPM0開發學習筆記:二維雲臺畫圖(2025電賽 附源代碼及引腳配置)

為備戰 2025 電賽雲臺類題目,作者以 MSPM0G3507 搭配 D36A 雙通道驅動兩顆 42 步進電機,自製 3D 列印桁架組裝二維雲台,並說明目前僅完成軌跡跟隨、尚未安裝雷射頭時的連線、腳位與繪圖控制思路。

來源:https://blog.csdn.net/2403_87969572/article/details/149765767

抓取時間(ISO本地):2026-05-18 05:17:02


文章目錄


前言

今年的電賽(2025),很多題都與雲臺相關,因此為備戰電賽,博主這邊也是準備了一個由兩個42步進電機驅動的雲臺並提前進行調試,避免賽題出來之後手忙腳亂的,這邊的兩個42步進電機採用同一個驅動模塊進行驅動(D36A),主控肯定採用MSPM0G3507。然後3D打印了一個二維雲臺的結構並進行組裝。本章博客主要是講這個雲臺進行繪圖的思路以及代碼。激光還沒有安裝上去,目前只是雲臺的循跡代碼


如果無法很好的復現博客裡的代碼,可以私信作者博取源代碼,電賽期間都在線

一、硬件選擇

主控:MSPM0G3507
驅動:D36A雙路步進電機驅動
電機:42步進電機*2

二、硬件連線

硬件連線部分在上一篇博客裡面已經說過了,可以直接去上一篇裡面看,這邊附上鍊接
MSPM0開發學習筆記:D36A驅動的42步進電機二維雲臺(2025電賽 附源代碼及引腳配置)

三、軟件代碼

軟件部分採用C語言實現,IDE採用keil,基於逐飛庫進行編寫
這邊先進行一下簡單的參數說明,便於理解後面的思路

參數含義以及作用
current_x目前的X座標
current_y目前的Y座標
target_x需要移動到的X座標
target_y需要移動到的Y座標
move_x需要移動的X距離
move_y需要移動的Y距離
point_count繪製圓形時候的點數

代碼實現如下:

函數一:限幅函數

#define MAX_ANGLE_X  1000.0f   
#define MIN_ANGLE_X -1000.0f   
#define MAX_ANGLE_Y  1000.0f  
#define MIN_ANGLE_Y -1000.0f  


float limit_angle(float angle, float min, float max) {
    if (angle < min) return min;
    if (angle > max) return max;
    return angle;
}

函數二:激光雲臺繪製正方形

// Function to draw a square trajectory
// Parameters:
//   x_len - length of the square's X-axis dimension
//   y_len - length of the square's Y-axis dimension
//   MOVE_SPEED - speed of movement between points
void draw_square(int x_len, int y_len, int MOVE_SPEED) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;
    
    // Invert Y-axis length (likely for coordinate system adjustment)
    y_len = y_len * -1;
    
    // Calculate half-lengths for easier coordinate calculation
    float x_len_2 = x_len / 2;
    float y_len_2 = y_len / 2;
    
    // Define square vertices relative to origin (0,0)
    // Coordinates form a square shape when connected sequentially
    Point square_points[] = {
        {-1 * x_len_2, y_len_2},    // Top-left corner
        {x_len_2, y_len_2},         // Top-right corner
        {x_len_2, -1 * y_len_2},    // Bottom-right corner
        {-1 * x_len_2, -1 * y_len_2},// Bottom-left corner
        {-1 * x_len_2, y_len_2},    // Back to top-left to close the square
        {0.0f, 0.0f}                // Final point: return to origin
    };
    
    // Calculate total number of points in the square trajectory
    uint8 point_count = sizeof(square_points) / sizeof(Point);  
    
    // Initialize current position at origin (0,0)
    float current_x = 0;
    float current_y = 0;
    
    // Move to each defined point in sequence
    for (uint8 i = 0; i < point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(square_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(square_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);

        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;
        
        // Update current position to target coordinates
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);
        
        // Pause 300ms after reaching each point
        system_delay_ms(300);
    }
    
    // Pause 2 seconds after completing the square
    system_delay_ms(2000);
}

一、函數定義:draw_square函數接收三個參數,分別是正方形的 X 軸長度(x_len)、Y 軸長度(y_len)和移動速度(MOVE_SPEED)。
二、數據結構:定義了Point結構體用於存儲座標點的 X 和 Y 角度值。
三、座標處理:
1、將 Y 軸長度取負值(為了調整座標系方向)
2、計算半長(x_len_2, y_len_2),用於確定正方形頂點座標
3、座標定義:定義了正方形的 4 個頂點座標和原點(0,0)座標以中心點為原點,通過半長計算得出四個頂點位置
4、最後回到一個點 (0,0) 用於回到起點
四、繪製邏輯:
1、遍歷所有定義的座標點
2、對每個目標點進行角度限制(通過limit_angle函數確保在有效範圍內)
3、計算當前位置到目標點的移動量
4、調用d36a_set_angle_both函數移動到目標點(同時設置 X 和 Y 方向角度)
5、每個點移動後延遲 300 毫秒,繪製完成後延遲 2000 毫秒

函數三:激光雲臺繪製圓形

// Function to draw a circle trajectory
// Parameters:
//   r - radius of the circle
//   MOVE_SPEED - speed of movement between points
//   point_count - number of points to use for drawing the circle (more = smoother)
void draw_circle(int r, int MOVE_SPEED, const uint8 point_count) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;

    // Array to store circle points (extra element to close the loop)
    Point circle_points[point_count + 1];  

    // Calculate coordinates for each point on the circle
    for (uint8 i = 0; i < point_count; i++) {
        // Convert angle from degrees to radians (full circle = 2π radians)
        float rad = 2 * 3.1415926f * i / point_count;

        // Calculate X and Y positions using trigonometric functions
        // cosine for X-axis, sine for Y-axis to form circular path
        circle_points[i].x_angle = r * cosf(rad); 
        circle_points[i].y_angle = r * sinf(rad);  
    }
    // Close the circle by duplicating the first point as the last point
    circle_points[point_count] = circle_points[0];

    // Initialize current position at origin (0,0)
    float current_x = 0.0f;  
    float current_y = 0.0f; 

    // Move to each calculated point in sequence
    for (uint8 i = 0; i <= point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(circle_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(circle_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);
        
        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;

        // Update current position to target
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

        // Optional delay between point movements
        // system_delay_ms(10);
    }
    
    // Return to origin (0,0) after completing the circle
    float target_x = 0;
    float target_y = 0;

    // Calculate movement from last circle point to origin
    float move_x = target_x - current_x;
    float move_y = target_y - current_y;

    // Update current position to origin
    current_x = target_x;
    current_y = target_y;

    // Send final movement command to return to origin
    d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

    // Pause for 2 seconds after completing the circle
    system_delay_ms(2000);  
}

通過三角函數(X 軸用餘弦、Y 軸用正弦)計算圓周上指定數量的點的座標,這些點基於圓周的等角度增量分佈。函數會按順序在這些點之間移動(帶速度控制),同時確保角度在有效範圍內;通過回到第一個點來閉合圓形軌跡,最後返回原點,結束時短暫暫停。點的數量越多,繪製的圓越平滑。
以下是函數中涉及的公式:

  1. 弧度計算(將圓周等分為指定數量的點)

    rad

    2 × π × i point_count \text{rad} = 2 \times \pi \times \frac{i}{\text{point\_count}} rad=2×π×point_counti​
    其中, i i i 為當前點的索引(0 到 point_count-1), point_count \text{point\_count} point_count 為圓周上的總點數, rad \text{rad} rad 為對應角度的弧度值。

  2. X軸座標計算

    x _ a n g l e

    r × cos ⁡ ( rad ) x\_angle = r \times \cos(\text{rad}) x_angle=r×cos(rad)
    其中, r r r 為圓的半徑, cos ⁡ ( rad ) \cos(\text{rad}) cos(rad) 為弧度對應的餘弦值。

  3. Y軸座標計算

    y _ a n g l e

    r × sin ⁡ ( rad ) y\_angle = r \times \sin(\text{rad}) y_angle=r×sin(rad)
    其中, sin ⁡ ( rad ) \sin(\text{rad}) sin(rad) 為弧度對應的正弦值。

四、總結

這邊給的都是一些簡單圖形的繪製代碼,但是思路都是通用的,複雜圖形也可以複用這一套邏輯。大家參考參考就好

如果無法很好的復現博客裡的代碼,可以私信作者博取源代碼,電賽期間都在線

MSPM0开发学习笔记:二维云台画图(2025电赛 附源代码及引脚配置)

The build pairs an MSPM0G3507 MCU with one D36A dual stepper driver powering two NEMA‑17 motors on a printable two‑axis cradle meant for electronics‑contest gimbals; wiring, pinmux, mechanics, and the current firmware that traces paths—even before attaching a laser head—are explained so readers can replicate the rig quickly.


前言

今年的电赛(2025),很多题都与云台相关,因此为备战电赛,博主这边也是准备了一个由两个42步进电机驱动的云台并提前进行调试,避免赛题出来之后手忙脚乱的,这边的两个42步进电机采用同一个驱动模块进行驱动(D36A),主控肯定采用MSPM0G3507。然后3D打印了一个二维云台的结构并进行组装。本章博客主要是讲这个云台进行绘图的思路以及代码。激光还没有安装上去,目前只是云台的循迹代码


如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线

一、硬件选择

主控:MSPM0G3507
驱动:D36A双路步进电机驱动
电机:42步进电机*2

二、硬件连线

硬件连线部分在上一篇博客里面已经说过了,可以直接去上一篇里面看,这边附上链接
MSPM0开发学习笔记:D36A驱动的42步进电机二维云台(2025电赛 附源代码及引脚配置)

三、软件代码

软件部分采用C语言实现,IDE采用keil,基于逐飞库进行编写
这边先进行一下简单的参数说明,便于理解后面的思路

参数含义以及作用
current_x目前的X坐标
current_y目前的Y坐标
target_x需要移动到的X坐标
target_y需要移动到的Y坐标
move_x需要移动的X距离
move_y需要移动的Y距离
point_count绘制圆形时候的点数

代码实现如下:

函数一:限幅函数

#define MAX_ANGLE_X  1000.0f   
#define MIN_ANGLE_X -1000.0f   
#define MAX_ANGLE_Y  1000.0f  
#define MIN_ANGLE_Y -1000.0f  


float limit_angle(float angle, float min, float max) {
    if (angle < min) return min;
    if (angle > max) return max;
    return angle;
}

函数二:激光云台绘制正方形

// Function to draw a square trajectory
// Parameters:
//   x_len - length of the square's X-axis dimension
//   y_len - length of the square's Y-axis dimension
//   MOVE_SPEED - speed of movement between points
void draw_square(int x_len, int y_len, int MOVE_SPEED) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;
    
    // Invert Y-axis length (likely for coordinate system adjustment)
    y_len = y_len * -1;
    
    // Calculate half-lengths for easier coordinate calculation
    float x_len_2 = x_len / 2;
    float y_len_2 = y_len / 2;
    
    // Define square vertices relative to origin (0,0)
    // Coordinates form a square shape when connected sequentially
    Point square_points[] = {
        {-1 * x_len_2, y_len_2},    // Top-left corner
        {x_len_2, y_len_2},         // Top-right corner
        {x_len_2, -1 * y_len_2},    // Bottom-right corner
        {-1 * x_len_2, -1 * y_len_2},// Bottom-left corner
        {-1 * x_len_2, y_len_2},    // Back to top-left to close the square
        {0.0f, 0.0f}                // Final point: return to origin
    };
    
    // Calculate total number of points in the square trajectory
    uint8 point_count = sizeof(square_points) / sizeof(Point);  
    
    // Initialize current position at origin (0,0)
    float current_x = 0;
    float current_y = 0;
    
    // Move to each defined point in sequence
    for (uint8 i = 0; i < point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(square_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(square_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);

        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;
        
        // Update current position to target coordinates
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);
        
        // Pause 300ms after reaching each point
        system_delay_ms(300);
    }
    
    // Pause 2 seconds after completing the square
    system_delay_ms(2000);
}

一、函数定义:draw_square函数接收三个参数,分别是正方形的 X 轴长度(x_len)、Y 轴长度(y_len)和移动速度(MOVE_SPEED)。
二、数据结构:定义了Point结构体用于存储坐标点的 X 和 Y 角度值。
三、坐标处理:
1、将 Y 轴长度取负值(为了调整坐标系方向)
2、计算半长(x_len_2, y_len_2),用于确定正方形顶点坐标
3、坐标定义:定义了正方形的 4 个顶点坐标和原点(0,0)坐标以中心点为原点,通过半长计算得出四个顶点位置
4、最后回到一个点 (0,0) 用于回到起点
四、绘制逻辑:
1、遍历所有定义的坐标点
2、对每个目标点进行角度限制(通过limit_angle函数确保在有效范围内)
3、计算当前位置到目标点的移动量
4、调用d36a_set_angle_both函数移动到目标点(同时设置 X 和 Y 方向角度)
5、每个点移动后延迟 300 毫秒,绘制完成后延迟 2000 毫秒

函数三:激光云台绘制圆形

// Function to draw a circle trajectory
// Parameters:
//   r - radius of the circle
//   MOVE_SPEED - speed of movement between points
//   point_count - number of points to use for drawing the circle (more = smoother)
void draw_circle(int r, int MOVE_SPEED, const uint8 point_count) {

    // Structure to store X and Y angle coordinates
    typedef struct {
        float x_angle;  // X-axis angle position
        float y_angle;  // Y-axis angle position
    } Point;

    // Array to store circle points (extra element to close the loop)
    Point circle_points[point_count + 1];  

    // Calculate coordinates for each point on the circle
    for (uint8 i = 0; i < point_count; i++) {
        // Convert angle from degrees to radians (full circle = 2π radians)
        float rad = 2 * 3.1415926f * i / point_count;

        // Calculate X and Y positions using trigonometric functions
        // cosine for X-axis, sine for Y-axis to form circular path
        circle_points[i].x_angle = r * cosf(rad); 
        circle_points[i].y_angle = r * sinf(rad);  
    }
    // Close the circle by duplicating the first point as the last point
    circle_points[point_count] = circle_points[0];

    // Initialize current position at origin (0,0)
    float current_x = 0.0f;  
    float current_y = 0.0f; 

    // Move to each calculated point in sequence
    for (uint8 i = 0; i <= point_count; i++) {
        // Ensure target angles stay within allowed range
        float target_x = limit_angle(circle_points[i].x_angle, MIN_ANGLE_X, MAX_ANGLE_X);
        float target_y = limit_angle(circle_points[i].y_angle, MIN_ANGLE_Y, MAX_ANGLE_Y);
        
        // Calculate relative movement from current position to target
        float move_x = target_x - current_x;
        float move_y = target_y - current_y;

        // Update current position to target
        current_x = target_x;
        current_y = target_y;
        
        // Send movement command to both axes
        d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

        // Optional delay between point movements
        // system_delay_ms(10);
    }
    
    // Return to origin (0,0) after completing the circle
    float target_x = 0;
    float target_y = 0;

    // Calculate movement from last circle point to origin
    float move_x = target_x - current_x;
    float move_y = target_y - current_y;

// Update current position to origin
    current_x = target_x;
    current_y = target_y;

    // Send final movement command to return to origin
    d36a_set_angle_both(move_x, move_y, MOVE_SPEED);

    // Pause for 2 seconds after completing the circle
    system_delay_ms(2000);  
}

通过三角函数(X 轴用余弦、Y 轴用正弦)计算圆周上指定数量的点的坐标,这些点基于圆周的等角度增量分布。函数会按顺序在这些点之间移动(带速度控制),同时确保角度在有效范围内;通过回到第一个点来闭合圆形轨迹,最后返回原点,结束时短暂暂停。点的数量越多,绘制的圆越平滑。
以下是函数中涉及的公式:

  1. 弧度计算(将圆周等分为指定数量的点)

    rad

    2 × π × i point_count \text{rad} = 2 \times \pi \times \frac{i}{\text{point\_count}} rad=2×π×point_counti​
    其中, i i i 为当前点的索引(0 到 point_count-1), point_count \text{point\_count} point_count 为圆周上的总点数, rad \text{rad} rad 为对应角度的弧度值。

  2. X轴坐标计算

    x _ a n g l e

    r × cos ⁡ ( rad ) x\_angle = r \times \cos(\text{rad}) x_angle=r×cos(rad)
    其中, r r r 为圆的半径, cos ⁡ ( rad ) \cos(\text{rad}) cos(rad) 为弧度对应的余弦值。

  3. Y轴坐标计算

    y _ a n g l e

    r × sin ⁡ ( rad ) y\_angle = r \times \sin(\text{rad}) y_angle=r×sin(rad)
    其中, sin ⁡ ( rad ) \sin(\text{rad}) sin(rad) 为弧度对应的正弦值。

四、总结

这边给的都是一些简单图形的绘制代码,但是思路都是通用的,复杂图形也可以复用这一套逻辑。大家参考参考就好

如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线