Shop OBEX P1 Docs P2 Docs Learn Events
Well it's plywood - Page 2 — Parallax Forums

Well it's plywood

2

Comments

  • ercoerco Posts: 20,255
    edited 2013-01-28 09:30
    I shared this thread and another with the seller, and he didn't waste any time updating his ebay listing with those links... :)

    http://www.ebay.com/itm/Robotic-arm-DIY-kit-Lasercut-Plywood-Structure-6servos-AT89S51-MCU-USB-ISP-tool-/330864397767?pt=LH_DefaultDomain_0&hash=item4d090e0dc7
  • Martin_HMartin_H Posts: 4,051
    edited 2013-01-28 11:40
    Mine is all wired up and ready to go. Last night I spent some time calibrating my inverse kinematics code which takes a bit of time to get right. Hopefully I'll have a video in a week or so.
  • ercoerco Posts: 20,255
    edited 2013-01-28 13:41
    A week?

    A WEEK?

    The race is on, Pal!
  • ercoerco Posts: 20,255
    edited 2013-01-29 13:35
    A 6-axis mod might work pretty well if the gripper was made shorter, and 2 smaller servos were used for the gripper and wrist rotate. The working payload of the robot is pretty small, IMO the standard-sized base rotate servo is the weak link. A quarter-scale servo would stabilize the arm and reduce flexing. And a helper spring (or rubber band) from the slotted base up to the forearm helps counterbalance the load and reduce shoulder/elbow servo buzzing.
  • Martin_HMartin_H Posts: 4,051
    edited 2013-01-31 08:14
    erco wrote: »
    A 6-axis mod might work pretty well if the gripper was made shorter, and 2 smaller servos were used for the gripper and wrist rotate. The working payload of the robot is pretty small, IMO the standard-sized base rotate servo is the weak link. A quarter-scale servo would stabilize the arm and reduce flexing. And a helper spring (or rubber band) from the slotted base up to the forearm helps counterbalance the load and reduce shoulder/elbow servo buzzing.

    I've been working on this an hour or so each night this week. I've found that there's a fair bit of oscillation in the azimuth axis and it could use some form of damping. I think that would also help the front to back rocking when you change the shoulder joint's altitude.

    My six axis mod retained the ability to use the arm in its stock 5 axis configuration. With six axis the wrist mechanism is a bit too long and I'm thinking of ways to try to make it more compact. So for now I'm using it without a wrist rotate servo. My modification is still worth doing because not gluing the gripper allows using different end effectors with this arm or tweaking the wrist servos calibration.
  • ercoerco Posts: 20,255
    edited 2013-02-01 23:31
    I've got all 5 axes moving smoothly under Picaxe control now. I'll have to stick this down on a base and get something real done pronto. My time is divided between this and a mecanum robot on the bench. Busy weekend!
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-03 17:49
    Cool beans Erco.

    I ported my Spin inverse kinematics software to C++ and made it more configurable as well. The reason is this arm has different dimensions from my old arm. So I needed to tweak the code anyway. So I bit the bullet and rewrote it so it could drive both arms. Plus now that C++ is supported on the Propeller I have code that's portable, so I won't have to write it again. This code could probably be insect leg kinematics if you cut back on the number of joints.
    #define ftl(x) ((x)>=0 ? (long)((x)+0.5) : (long)((x)-0.5))  // float to long conversion with rounding
    
    // Angular values for common angles
    const float STEP_ANGLE      = PI/360;
    const float RIGHT_ANGLE     = HALF_PI;
    const float STRAIGHT_ANGLE  = PI;
    const float FULL_ROTATION   = 2.0 * PI;
    
    #ifndef RobotArm_h
    #define RobotArm_h
    
    // Defines and manages a single joint of the arm.
    class Joint
    {
      public:
        /*
          setParameters : configures the joint with a number of measure constants used to position the joint.
          Parameters:
            pin       the numeric value of the pin.
            minWidth  the minimum pulse with for this joint.
            maxWidth  the maximum pulse with for this joint.
            center    the pulse width which centers the joint.
            widthPerRadian    the pulse width to radian ratio which is signed to handle inverted servos.
         */
        void setParameters(int pin, int minWidth, int maxWidth, int center, float widthPerRadian)
        {
          _center = center;
          _widthPerRadian = widthPerRadian;
          _servo.attach(pin, minWidth, maxWidth);
        }
    
        /*
          setPosition: Computes the pulse width that matches the desired joint angle
          using the constraints of known angle to pulse values.  Servo center is zero,
          counter clockwise a quarter circle is Pi/2, while clockwise is -Pi/2.
    
          Parameters:
            angle    32-bit floating point radian value
        */
        void setPosition(float angle)
        {
          int pulseWidth = _center + ftl(_widthPerRadian * angle);
    
          _servo.writeMicroseconds(pulseWidth);
        }
    
        private:
          int          _center;
          float        _widthPerRadian;
          Servo        _servo;
    };
        
    class RobotArm
    {
      public:
        enum JointIndex { AZIMUTH=0, ALTITUDE=1, ELBOW=3, WRIST=4, ROTATE=5, GRIP=6, COUNT=7 };
    
        /* Constructor used to initialize arm parameters.
           Parameters:
            baseHeight   height of the altitude joint in mm.
            humerous   shoulder-to-elbow "bone" in mm
            ulna       elbow-to-wrist "bone" in mm
            gripper    gripper (incl. wrist rotate mechanism) in mm.
         */
        RobotArm(float baseHeight, float humerous, float ulna, float gripper)
        {
          _baseHeight = baseHeight;
          _humerous = humerous;
          _ulna = ulna;
          _gripper = gripper;
    
          /* pre-calculations */
          _humerousSq = _humerous * _humerous;
          _ulnaSq = _ulna * _ulna;
        }
        
        /*
          setters to inject the servo parameters for each joint.
          Parameters
            jointIdx - the enumerated name of the joint.
            pin      - the numeric value of the pin.
            minWidth - the minimum pulse with for this joint.
            maxWidth - the maximum pulse with for this joint.
            center   - pulse width that centers the joint
            widthPerRadian - the pulse width to deflect the servo one radian from center
          */
        void setJointParameters(JointIndex jointIdx, int pin, int minWidth, int maxWidth, int center, float widthPerRadian)
        {
          joints[jointIdx].setParameters( pin, minWidth, maxWidth, center, widthPerRadian);
          if (jointIdx == ALTITUDE)
          {
             joints[jointIdx+1].setParameters( pin-1, minWidth, maxWidth, center, widthPerRadian);
          }
        }
    
        /*
         Park - Move the arm servos to the parked position.  Called during start and stop.
          By parking the arm the servos will start from a known position and minimize startup
          lurch.
        */
        void park()
        {
          setAzimuth( 0.0 );
          setAltitude( PI/2 );
          setElbow( PI/2 );
          setWristAngle( 0 );
          setWristRotate( 0.0 );
          setGripper( 80 );
        }
    
        /*
          SetPosition : Arm positioning routine utilizing inverse kinematics.  Since the arm is
            resting on a surface Z can only be positive.  Servo movement contraints prevent y
            from being negative, But X can be a signed value.
            Note: This must be called before and of the move routines to initialize arm state.
          Parameters:
            x   the side to side displacement.
            y   the distance out from the base center.
            z   the height above the table
            wristAngle    wrist ground angle of the gripper in radians.
         */
        void setPosition( float x, float y, float z, float wristAngle )
        {
          // Save the cartesian space coordinates.
          _x = x;
          _y = y;
          _z = z;
    
          // grip angle in radians for use in calculations
          _wristGroundAngleRads = wristAngle;
          
          // Compute base angle using the x,y coordinates
          setAzimuth(atan2( x, y ));
    
          // Compute the vector magnitude and hold in the y coordinate.
          y = sqrt(( x * x ) + ( y * y ));
    
          // Calculate Gripper offsets based on grip angle and length.
          float grip_off_z = sin( _wristGroundAngleRads ) * _gripper;
          float grip_off_y = cos( _wristGroundAngleRads ) * _gripper;
    
          // Wrist position
          float wrist_z = z - grip_off_z - _baseHeight;
          float wrist_y = y - grip_off_y;
    
          // Use Pythagorean theorem to calculate shoulder to wrist distance.
          float s_w = ( wrist_z * wrist_z ) + ( wrist_y * wrist_y );
          float s_w_sqrt = sqrt( s_w );
    
          // s_w angle to ground
          float a1 = atan2( wrist_z, wrist_y );
    
          // s_w angle to humerus, note the need to clamp this value between -1 to 1!
          float q = ((_humerousSq - _ulnaSq) + s_w) / (2 * _humerous * s_w_sqrt);
          if (q > 1)
            q = 1;
          else if (q < -1)
            q = -1;
    
          float a2 = acos(q);
    
          // shoulder angle
          float altitudeRads = a1 + a2;
          setAltitude(altitudeRads);
    
          // elbow angle
          float elb_angle_r = acos(( _humerousSq + _ulnaSq - s_w ) / ( 2 * _humerous * _ulna ));
          float elb_angle_rn = -( STRAIGHT_ANGLE - elb_angle_r );
    
          // wrist angle
          float wri_angle_r = _wristGroundAngleRads - elb_angle_rn - altitudeRads;
    
          // Set the joints
          setElbow(elb_angle_r);
          setWristAngle(wri_angle_r);
        }
      
        /*
          SetX : Arm positioning routine utilizing inverse kinematics.  Moves the arm from
          the current X coordinate to the newX passed in.  It maintains all other position
          state.
          Parameters:
            newX   the new left right displacement
         */
        void setX(float newX)
        {
          setPosition(newX, _y, _z, _wristGroundAngleRads);
        }
    
        /*
          SetY : Arm positioning routine utilizing inverse kinematics.  Moves the arm from
          the current Y coordinate to the newY passed in.  It maintains all other position
          state.
          Parameters:
            newY   the new forward/back displacement
         */
        void setY(float newY)
        {
          setPosition(_x, newY, _z, _wristGroundAngleRads);
        }
    
        /*
          SetZ : Arm positioning routine utilizing inverse kinematics.  Moves the arm from
          the current Z coordinate to the newZ passed in.  It maintains all other position
          state.
          Parameters:
            z   the new height above the table
         */
        void setZ(float newZ)
        {
          setPosition(_x, _y, newZ, _wristGroundAngleRads);
        }
    
       /*
        setAzimuth: Sets base angle data member, computes the servo pulse width for that angle,
          and sets the associated servo.
        Parameters:
          baseAngle    floating point radian value
       */
       void setAzimuth(float baseAngle)
       {   
         _azimuthAngleRads = baseAngle;
         joints[AZIMUTH].setPosition(_azimuthAngleRads);
       }
    
      /*
        setAltitude: Sets the should angle member, computes the servo pulse width for that angle,
          and sets the associated servo.
        Parameters:
          shoulderAngle   32-bit floating point radian value
       */
      void setAltitude(float altitudeAngleRads)
      {
        // Rotate the coordinate system by a right angle so that a straight angle is
        // a full extension of the joint.
        _altitudeAngleRads = altitudeAngleRads - RIGHT_ANGLE;
    
        joints[ALTITUDE].setPosition(_altitudeAngleRads);
        joints[ALTITUDE+1].setPosition(_altitudeAngleRads);
      }
    
      /*
        SetElbow: Sets elbow angle data member, computes the servo pulse width for that angle,
          and sets the associated servo.
        Parameters:
          elbowAngle   floating point radian value
       */
      void setElbow(float elbowAngle)
      {
        // Rotate the coordinate system by a right angle so that a straight angle is
        // a full extension of the joint.
        _elbowAngleRads = elbowAngle - STRAIGHT_ANGLE; // RIGHT_ANGLE;
        joints[ELBOW].setPosition(_elbowAngleRads);
      }
    
      /*
        SetWristAngle: Sets the wrist angle member, computes the servo pulse width for that angle,
          and sets the associated servo.
        Parameters:
          wristAngle   32-bit floating point radian value
       */
      void setWristAngle(float wristAngle)
      {
        joints[WRIST].setPosition(wristAngle);
      }
      
      /*
        setWristRotate: Sets the gripper angle of rotation member, computes the servo pulse width,
          and sets the associated servo.
        Parameters:
          gripAngle   32-bit floating point radian value
       */
      void setWristRotate(float wristRotateAngle)
      {
        _wristRotAngleRads = wristRotateAngle;
        joints[ROTATE].setPosition(_wristRotAngleRads);
      }
    
      /*
        setGripper: Sets the gripper distance of the fingers.
        Parameters:
          distance   32-bit floating point radian value
       */
      void setGripper(float distance)
      {
        _fingerAngleRads = distance;
        joints[GRIP].setPosition(distance);
      }
    
      private:
        float _baseHeight;
        float _humerous;
        float _ulna;
        float _gripper;
        float _humerousSq;
        float _ulnaSq;
    
        // Coordinate of gripper tip in cartesian space.
        float _x, _y, _z;
        float _wristGroundAngleRads;
    
        // Coordinates in arm revolute polar space.
        float _azimuthAngleRads;
        float _altitudeAngleRads;
        float _elbowAngleRads;
        float _wristRotAngleRads;
        float _fingerAngleRads;
    
        Joint joints[COUNT];
    };
    #endif
    
    /* Arm dimensions( mm ) */
    #define BASE_HGT 75     
    #define HUMERUS 94       // shoulder-to-elbow "bone" 5.75"
    #define ULNA 78          // elbow-to-wrist "bone" 7.375"
    #define GRIPPER 150.00   // gripper (incl.heavy duty wrist rotate mechanism) length 3.94"
    
    RobotArm robotArm(BASE_HGT, HUMERUS, ULNA, GRIPPER);
    

    The only external dependency is a class called Servo which abstracts the details of using a servo. The Arduino servo library or Beau Schwabe's code (with some tweaking) could fit the bill for this depending upon the MCU you are using.

    I'm thinking of buying something to line the jaws of the gripper so they'll have a little more grip too.
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-03 18:40
    Here's a video of some inverse kinematics goodness.
  • ercoerco Posts: 20,255
    edited 2013-02-03 19:14
    True goodness, Martin_H! I'm pretty pleased with mine as well. Still soldering up my PCB...

    Meanwhile, check out another interesting 5-axis arm. Code those velocity profiles in C, toughguy! :) I like the camera on the gripper at 4:00. Try that with your IK routine and see how smooth it is.
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-04 06:54
    That's impressive. I wish those fancy pants hacked servos were available at a reasonable price because I could thing of some great uses for them. As far as duplicating that effort, there's no way I can pull it off while I'm still working full time.
  • ercoerco Posts: 20,255
    edited 2013-02-04 10:24
    I really like the hand-guided "puppeteering" aspect to teach it a path. With some extra hardware (and lots of extra time), it might be possible to use Beau's "master/slave" servo read routine and pull of something like that.
  • ercoerco Posts: 20,255
    edited 2013-02-05 23:15
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-06 06:15
    The idea of hand guided teaching has a lot of appeal.
    erco wrote: »

    It's robot performance art! I've toyed with the idea of a mobile arm, but it's hard enough to get them to do anything repeatable when they are fixed relative to the objects they manipulate.
  • ercoerco Posts: 20,255
    edited 2013-02-06 07:26
    One more arm and it can juggle!
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-14 18:57
    It's inverse kinematics, but I have a way to go before I am ready for chainsaws.

    BTW this was much harder to do than it looks. Hours of calibration and testing and it's still not flawlessly.
  • ercoerco Posts: 20,255
    edited 2013-02-14 21:32
    Fantastic job! The IK reads as the disk stays centered on the post as it rises. Well done! Try some rubber or sandpaper inside your gripper pads for a firmer grasp. Mine made a big difference. Obviously you can grip the disk tighter by varying the servo pulsewidth...

    Are you using a Prop?

    I did Towers of Hanoi a while back with my first arm, it's "required programming" IMO.

    Today I found a COOL new group of objects to manipulate. Big surprise, but it suits me fine. Will make a video, probably take a week. Too many Valentine's honeydos.

    Too bad wives don't put robot arms on honeydo lists, huh?
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-15 06:45
    erco wrote: »
    Are you using a Prop?
    ...
    Today I found a COOL new group of objects to manipulate. Big surprise, but it suits me fine. Will make a video, probably take a week.
    ...
    Too bad wives don't put robot arms on honeydo lists, huh?

    Erco, thanks for the Kudos. You've really piqued my curiosity about these cool new objects. My next project with either be drawing something with a pen to test IK accuracy, or object sorting based upon color. I'm not sure what I'll tackle next, although I'm leaning towards color sorting as a classic robot arm problem.

    A Valentines honeydo was part of the reason I had to post a video with it dropping the disk. No more time to get it perfect.

    With regards to the MCU. I am not using a prop because none of my boards were small enough to fit on the small platform. So I'm using a Pololu Wixel shield with an el cheapo Arduino compatible board that I built. That's a pretty flexible combination because I can use it as a wireless serial servo controller, or local brains doing IK. All my code was originally written in Spin and ported to platform neutral C++, so I could probably compile it for a Propeller chip using the new GCC and simple IDE. A Propeller chip would probably make servo ramping a tad smoother because Beau Schwabe's servo library is really top notch compared to the Arduino servo object.
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-18 13:27
    This weekend I modified the azimuth joint to reduce play. First I fabricated a plywood doughnut and drilled three holes in an equilateral triangle in it. I placed it on top of the base and drilled matching holes through it. Next I put 4-40 bolts through three furniture glides, and put the bolts through the disk into the base. Finally I tightened up the nuts to hold the glides about a thickness of a piece of paper from the top of the azimuth joint. This creates a bearing surface to push back on the joint when it moves and should reduce wobble considerably as well as adding damping to the joint.

    Here's a picture :

    IMG_20130218_161629.jpg
    1024 x 768 - 83K
  • garyggaryg Posts: 420
    edited 2013-02-18 15:38
    I really like your furniture glides used as bearings.
    In my past projects, I've used countertop laminate, glued to a plywood disk and waxed to serve the same purpose.
    Using furniture glides as you have them configured, would allow for very good adjustment to the servo drive shaft.
    These seemingly simple details are an important aspect of projects similar to yours.

    Thanks
  • Martin_HMartin_H Posts: 4,051
    edited 2013-02-19 05:00
    garyg wrote: »
    I really like your furniture glides used as bearings.
    In my past projects, I've used countertop laminate, glued to a plywood disk and waxed to serve the same purpose.
    Using furniture glides as you have them configured, would allow for very good adjustment to the servo drive shaft.
    These seemingly simple details are an important aspect of projects similar to yours.

    Garyg, I'm glad you found it useful. Another advantage of furniture glides over bearings like a lazy susan is that there's no need for concentricity of the motor and bearing. I tried a lazy susan on a mini project a while back, and found it always seemed to bind no matter how carefully I measured before drilling.
  • ercoerco Posts: 20,255
    edited 2013-02-28 10:21
    Ouch. This guy paid $224 for this arm. PLUS the $8 shipping. It pays to check the Parallax forum (or the seller's history)! The $124 he paid would have bought half a new Walmart laptop...

    http://www.ebay.com/itm/Robotic-arm-DIY-kit-Lasercut-Plywood-Structure-6servos-AT89S51-MCU-USB-ISP-tool-/330871657564
  • ercoerco Posts: 20,255
    edited 2013-03-01 10:07
    @Martin_H: You'll dig this guy's IK robot arm & remote controller (incl tilt). You can clearly see the IK in action to moving the gripper in a straight line per controller input. I speak from experience when I say the IK surely simplifies remote control path planning by eyeball. Nicely done!
  • ercoerco Posts: 20,255
    edited 2013-03-01 11:04
    Aluminum 6-DOF robot arm from China for $139. No controller/batteries/charger: http://www.ebay.com/itm/New-6-DOF-Manipulator-Aluminum-Robot-Arm-Kits-/160934066564
  • JasonDorieJasonDorie Posts: 1,930
    edited 2013-03-05 13:03
    You guys may like this. I'm hoping to copy an ST Robotics R12, more or less: http://www.strobotics.com/small-articulated-robot.htm

    My initial prototype is just wood, steppers, and some salvaged gears from dead printers. I use my CNC machine to modify the bore of the gears to fit the stepper shafts, and include the detent profile needed. Last night I got the shoulder done, so the elbow should be next.

    I'm working on the code to drive it, specifically the stepper control logic to handle proper acceleration and deceleration, path planning, etc. I have the basic logic done for running a stepper with proper acceleration and maximum speed, so I can do a linear move with an exact number of steps. It's done in real time using a Prop, almost completely in integer math. I compute a couple initial things in float whenever you change the acceleration value or top speed, but all the per-step work is done in fixed point.

    The video isn't much, but it shows acceleration and deceleration with an exact stop (4 turns):

    It's poorly built, but I figure once I get a feel for how the construction needs to be done I can build it better.

    I want to get it to the point where I can draw a shape on a flat surface, maintaining a particular speed, and respecting acceleration / deceleration limits. That implies IK, path planning, and motor limits all working together. The math makes my brain hurt. I'm envious of your IK progress, Martin - Nicely done.
    968 x 1296 - 649K
    968 x 1296 - 623K
  • ercoerco Posts: 20,255
    edited 2013-03-05 14:00
    JasonDorie wrote: »
    It's poorly built, but I figure once I get a feel for how the construction needs to be done I can build it better.

    Jason: I've seen your incredible craftmanship on various projects, furniture, etc. You don't need to apologize for anything! So very cool that you're getting into arms, we need more arm builders here. Arms are an art/science unto themselves. Please keep us in the loop with your progress.
  • Martin_HMartin_H Posts: 4,051
    edited 2013-03-05 19:12
    Erco, thanks thanks for posting IK video. I've thought about trying mouse control for something similar. The mouse controls the X, Y and the scroll wheel controls Z. The buttons would open or close the gripper.

    Jason, I've had my eye on the R12 as a potential amateur build for a while now. Keep us posted about your project as it has a lot of promise.
  • ercoerco Posts: 20,255
    edited 2013-03-06 11:04
    Martin_H wrote: »
    The mouse controls the X, Y and the scroll wheel controls Z. The buttons would open or close the gripper.

    That's a fabulous idea, and the perfect application for IK. Would make arm control fun and intuitive.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-03-12 09:26
    I decided to reply to Martin here instead of the figure 8 thread. I thought this thread would be a better place to discuss my attempt to get my robot arm moving again (even though it's made of metal).
    Martin_H wrote: »
    Thanks Duane. You leg IK code should give you a head start, but I have IK code in Spin and C++, so let me know if you hit any issues. I based mine on a spreadsheet I found on Lynx Motion:

    http://www.lynxmotion.com/images/html/proj057.htm

    But I've added primitives like moveX, MoveY, moveZ, and moveXY which allows smooth movement between Cartesian points.

    Thanks for the link Martin. I haven't looked at it yet. I want to see if I can figure this out on my own before I look the spreadsheet or ask to see your code. Thanks for the offer.

    I'll probably want to take a look at what you have eventually, either to compare it against my code (which I'd send you), or ,if I can't get my version to work, to use as a guide for fixing my code.

    I just found the last part to my arm yesterday and got the base rotating (the only servo connected so far).

    My plan it to initially just to get the arm working again and controlled with a RC remote. I've had it working through the remote in the past but I never did make a video of it in action.

    Once I have the arm moving again, I'll get to work on the IK equations (after posting a video of my progress).
  • ercoerco Posts: 20,255
    edited 2013-03-13 17:02
    Hey, I'm on vacation in NC, far from my robots. But all of a sudden, I want to see a robot arm tracing out a figure 8 pattern in the figure 8 thread. You jokers better get to it before I get home in a week!
  • Martin_HMartin_H Posts: 4,051
    edited 2013-03-13 17:16
    I'm on it! Well technically was on it.

    BTW I figured you were on vacation since there was a lack of erco posts.
Sign In or Register to comment.