Draw a simple robot arm in C#

[robot arm]

This is really an exercise in using graphical transformations. You can draw each segment and joint in the arm by using simple drawing methods and transformations to rotate and translate the parts to their correct positions.

The basic idea is to consider the stages of the arm one after another and use transformations to move from one to the next.

Suppose the three arm segments have lengths L1, L2, and L3 and the angles of rotation at the three joints are A1, A2, and A3.

The first joint rotates the arm by angle A1 at a fixed shoulder. The second joint is translated distance L1 away in the direction of the first arm segment.

The second joint rotates the arm by angle A2 at the “elbow.” The third joint is translated distance L2 away in the direction of the second arm segment.

The third joint rotates the arm by angle A3 at the “wrist.” The end of the arm is translated distance L3 away in the direction of the third arm segment.


This program uses the following code to draw the robot arm.

// Draw the robot arm.
private void DrawRobotArm(Graphics gr)
{
    const int UpperArmLength = 75;
    const int LowerArmLength = 50;
    const int WristLength = 20;

    gr.SmoothingMode = SmoothingMode.AntiAlias;
    gr.Clear(picCanvas.BackColor);

    // For each stage in the arm, draw and then *prepend* the
    // new transformation to represent the next arm in the sequence.

    // Translate to center of form.
    float cx = picCanvas.ClientSize.Width / 2;
    float cy = picCanvas.ClientSize.Height / 2;
    gr.TranslateTransform(cx, cy);

    // **************
    // Draw the arms.
    GraphicsState initial_state = gr.Save();

    // Make a rectangle to represent an arm.
    // Later we'll set its width for each arm.
    Rectangle rect = new Rectangle(0, -2, 100, 5);

    // Rotate at the shoulder.
    // (Negative to make the angle increase counter-clockwise).
    gr.RotateTransform(-scrJoint1.Value, MatrixOrder.Prepend);

    // Draw the first arm.
    rect.Width = UpperArmLength;
    gr.FillRectangle(Brushes.LightBlue, rect);
    gr.DrawRectangle(Pens.Blue, rect);

    // Translate to the end of the first arm.
    gr.TranslateTransform(UpperArmLength, 0, MatrixOrder.Prepend);

    // Rotate at the elbow.
    gr.RotateTransform(-scrJoint2.Value, MatrixOrder.Prepend);

    // Draw the second arm.
    rect.Width = LowerArmLength;
    gr.FillRectangle(Brushes.LightBlue, rect);
    gr.DrawRectangle(Pens.Blue, rect);

    // Translate to the end of the second arm.
    gr.TranslateTransform(LowerArmLength, 0, MatrixOrder.Prepend);

    // Rotate at the wrist.
    gr.RotateTransform(-scrJoint3.Value, MatrixOrder.Prepend);

    // Draw the third arm.
    rect.Width = WristLength;
    gr.FillRectangle(Brushes.LightBlue, rect);
    gr.DrawRectangle(Pens.Blue, rect);

    // ***********************************
    // Draw the joints on top of the arms.
    gr.Restore(initial_state);

    // Draw the shoulder centered at the origin.
    Rectangle joint_rect = new Rectangle(-4, -4, 9, 9);
    gr.FillEllipse(Brushes.Red, joint_rect);

    // Rotate at the shoulder.
    // (Negative to make the angle increase counter-clockwise).
    gr.RotateTransform(-scrJoint1.Value, MatrixOrder.Prepend);

    // Translate to the end of the first arm.
    gr.TranslateTransform(UpperArmLength, 0, MatrixOrder.Prepend);

    // Draw the elbow.
    gr.FillEllipse(Brushes.Red, joint_rect); 

    // Rotate at the elbow.
    gr.RotateTransform(-scrJoint2.Value, MatrixOrder.Prepend);

    // Translate to the end of the second arm.
    gr.TranslateTransform(LowerArmLength, 0, MatrixOrder.Prepend);

    // Draw the wrist.
    gr.FillEllipse(Brushes.Red, joint_rect);
}

The code starts with some initialization and then translates to center the origin in the middle of the PictureBox.

The program should draw the joints (shoulder, elbow, and wrist) on top of the arms, so the method draws the arm in two stages. First it draws the arms and then it draws the joints on top. It uses the same set of transformations for each.

The code applies the rotation A1 at the shoulder and draws a rectangle representing the first arm segment. There are is one tricky issue here.

The rotation is prepended to the previous translation so the rotation occurs before the translation that centers the shoulder. Imagine the arm in its own world coordinate space with the shoulder at the origin. The program draws a horizontal rectangle at the origin to represent the first arm segment. The transformations then rotate it to the correct angle and only then translate it so the shoulder is centered. This puts the first arm segment in the correct position.

Next the code must move to the location of the elbow joint. To do this, it prepends a translation of distance L1 in the X direction and 0 in the Y direction. Because this is followed by the previous rotation, this translation occurs in the direction that the first first arm segment points. That places the drawing origin at the location of the elbow. The drawing is translated, rotated, and translated again so it ends up in the correct position.

Again imagine the arm in world coordinate space and suppose you draw the elbow at the origin. The new translation moves the joint to (L1, 0). The rotation of angle A1 rotates the joint to match the rotation of the first arm segment. Finally the shoulder’s translation moves the whole thing so the base of the first arm segment is centered in the drawing area.

The code continues like this as many times as necessary to draw all of the segments in the robot arm. In each step, it prepends the new translations and rotations.

After is has drawn all of the arm segments, the code resets the transformation to the original one that centered the shoulder on the PictureBox. It then walks through the transformations again, this time drawing the joints.

That’s all there is to drawing a connected sequence of robotic segments. Draw a segment and then prepend a transformation to move to the end of that segment. You can continue as many times as you need to handle all of the segments in the robot arm.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, drawing, graphics and tagged , , , , , , , , , . Bookmark the permalink.

11 Responses to Draw a simple robot arm in C#

  1. me says:

    hi….

    i have try this “Draw a simple robot arm in C#” by using your code but why the picture i have copied did not appear in the “picCanvas” in C#

    begineer C#…

  2. Rod Stephens says:

    I’m not sure. My guess would be that the PictureBox’s Paint event isn’t hooked up to the code properly.

    Most of the examples on this web site only include the most interesting pieces of code and often there is other code that you need to include to make them work. To see all of the code, you should download the example program by clicking the Download button below the text. I suspect the example will work for you.

  3. Philip says:

    Hi,

    I tried to reuse your demo code but with 8 joints instead of 3. I’ve added the respective constants, I’ve also added in the new gr.RotateTransform() and gr.RotateTransform() with respect to the new joints that I’ve wanted.

    When I run the program, the arm is able to draw correctly with all 8 joints in the place.
    But when I try to rotate the joints, I can only rotate the first three joints. I’ve double check the name of each components and how they were called and they are call correctly. I don’t see why I can rotate the joints according to the scrolbar beyond the 3rd joint.

    Can you please advice how I could diagnose and solve this issue?

    thanks.

    • RodStephens says:

      I assume you created the scroll bars for the new segments? And be sure you gave the new scroll bars Scroll event handlers. Otherwise the program won’t redraw the sections when you adjust the scroll bars.

  4. Ashay says:

    Hi,
    Thank you for an amazing tutorial.I wanted to know if i can add separate labels with Robot Arm images instead of rectangular blocks to make the visualization more specific for a particular robot.

Leave a Reply

Your email address will not be published. Required fields are marked *