Limbi Generation 1 Summary Blog Post
Author/s:
Julie Liner, MST
Daniel Trejo, E&C
Noah Roth, D&M and E&C
Ridel Samonte, PM and D&M
Table of Contents
Executive Summary
Program and Project Objectives
Program Objectives
The idea of Limbi is to be a crawling robotic space limb that would fix and/or maintain spacecraft to reduce (or eliminate) the need for astronauts to have tethered spacewalks to do such tasks. Another foreseeable future of Limbi is to act as a reconfigurable robots called limboids (smaller limbis) to build unique space structures.
JPL designed a prototype that uses electromagnets for the docking mechanism which draws huge amounts of current. Our team’s contribution to the advancement of Limbi is the development of a low-power momentumless mechanical docking mechanism that is also capable of power transfer.
Project Objectives
Limbi is a Robotic arm that has 4 degrees of freedom (joints), each capable of moving 180 deg on one plane, as well as an end-over-end mobility. Each end of the arm is androgynous (universal connector for both arm-to-module and module-to-module interconnects) connector capable of mating with an identical pair. Part of Limbi is the “spacecraft” module that has the same androgynous connector on its faces. Each module is capable of supplying power to the arm.
Mission Profile
The objective of Limbi is to connect two “spacecraft” modules into a specified position using the Arxterra App to demonstrate in-space assembly. At the start of phase 0 one end of the arm will start connected to Module 1 which will supply power to the arm. Module 1 is in a fixed position. For phase 1 the other end of the arm will grab module 2 through the docking mechanism. At this point, both modules are both supplying power to the arm (make-before-break circuitry). During phase 2 the arm will bring module 2 and connect it to module 1 through the module-to-module faces of both modules. During phase 3 the arm will undock from module 1 and module 2 will solely supply power to the arm for missions success. These phases can be found below in Figure 1.
A more detailed description can be found here.
Project Features
The goal of Limbi is to demonstrate in-space assembly that has a low-power momentumless mechanical docking mechanism with positive lock while having a continuous power supply from the module(s). Our team came up with a two layer docking design in the module side and a cross-shaped manipulator in the arm side that will initiate a 3 step sequence to dock: 45 degree turn, power transfer, and a actuate a positive lock.
Requirements
For the Limbi, requirements were an iterative process; they stemmed from the customer’s expectation, JPL’s model of the Limbi, and the conceptual operation of the final mission. Level 1 requirements are design independent requirements; for Limbi these were separated into requirements for the arm and the module. For the arm these then separated more into General, Mobility, Electronics and Control, and Special Features. These flowed down to Level 2 design dependent requirements which clarified the performance of separate systems and components. All requirements should be quantitative, verifiable, and realizable. To find more information on the creation of the requirements of the Limbi see: Creating Requirements and Servo Test.
Engineering Standards and Constraints
Applicable Engineering Standards and Environmental, Health, and Safety (EH&S) Standards
L 1.30 The Limbi shall employ a custom PCB to extend the functions of the arduino nano by allowing control of 6 servos, and logic levels compatible with the hm-11.
L2.1.19.1 The custom PCB will use surface mount technology including the Hm-11 and the SN74LV1T34 Logic Shifter
L 1.31 Disassemble and Reassemble of the robot shall be constrained to less than 20 minutes (10 minutes+10 minutes) (waived)
L 1.32 The Limbi shall be completed by the date of the final: May 14th 2019.
L 1.33 The robot shall be designed in such a way that there are no dangling or exposed wires.
L 1.34 The form factor of Limbi shall be constrained by the original JPL version (see requirement L1.2)
L 1.35 The usability of the Limbi shall be enhanced by use of the Arxterra phone and control panel application
L 2.1.35.1 The ArxRobot app shall allow control of all joint servos and docking servos (see requirement L 1.10)
L 1.36 Back of the envelope calculations and experiments shall be conducted to set the diameter of power carrying wires. Follow the American Wire Gauge (AWG) standard when defining the diameter of power carrying wires.
L 1.37 Manufacturability of 3D printed robots shall minimize the number of files to be printed when using the library’s Innovation Space to print the final robot (waived)
Out of the standards, codes, and regulations as defined in the “Engineering Standards and Constraints” Section, these are the ones that apply to Limbi
L 1.38 All Lithium (Li-ion, Li-polymer) batteries shall be stored, when not in use, in a fire and explosion proof battery bag.
L 1.39 Software shall be written in the Arduino De facto Standard scripting language and/or using the GCC C++ programming language, which is implements the ISO C++ standard (ISO/IEC 14882:1998) published in 1998, and the 2011 and 2014 revisions.
L 1.40 The Limbi shall be controlled via Bluetooth 4.0 in compliance with the Bluetooth Special Interest Group (SIG) Standard (supersedes IEEE 802.15.1).
Program and Project Level 1 Requirements
For Limbi the project unique functional requirements related to the form factor of the JPL Limbi and the fact that conceptually Limbi is meant to be used in space. This led to requirements related to end-over-end mobility, and docking mechanisms that could be implemented without pushing force.
Requirements (Arm):
General:
L1.1 Objective shall be demonstrated on a low friction surface
L 1.2 The Limbi project will be smaller than the JPL Limbi for cost and storage purposes.
Mobility:
L 1.3 The arm’s 4 joints shall move 4 of the 5 limbs (see Figure 2).
L 1.4 Each joint shall have 180 degrees of movement in one plane (x-y).
L 1.5 The arm shall be able to connect and disconnect with the modules
L 1.6 Docking mechanism shall keep the arm and module connected as the arm moves until it is meant to be disengaged.
L 1.7 The arm shall have a docking mechanism on each end to connect to two modules at once.
L 1.8 The arm shall be able to move module (in the same plane as the actuator planar scope).
L 1.9 The arm shall be able to link two modules together via permanent magnets.
Electronics and Control
L 1.10 The movement of the arm shall be controlled by the user with custom software.
L1.11 The Limbi shall be controlled with a microcontroller
Special Features
L 1.12 The arm should provide live video feed capability.
Requirements (Module):
L 1.13 Each module shall have the capability of providing power to the arm.
L 1.14 The arm shall only be powered with one power source (for extended amounts of time)
L 1.15 For demonstration purposes the module shall have docks on only 2 out of 6 faces
L 1.16 The module should indicate when secure connection is made between the Limbi and modules with an LED.
L 1.17 One module shall be defined as the base module and shall be stationary to represent a large, unmoving mass in space (such as the spacecraft). This requirement is based on Section 4 of “An Untethered Mobile Limb for Modular In-Space Assembly”.
System/Subsystem/Specifications Level 2 Requirements
Our Level 2 requirements flow from our Level 1 requirements. They expand on performance specifications that allow the Level 1 requirements to be accomplished. Limbi’s Level 2 requirements include values such as servo, battery, and magnet specifications, size constraints, and how specific components will interact with each other.
L2.1.1.1 The arm will be supported with metallic ball casters on Limb 0 and Limb 4 to simulate the conditions where the arm will not be affected by gravity.
L 2.1.2.1 The Limbi will follow the form factor of the JPL version; the lengths will be optimized in respect to inverse kinematics
L 2.1.3.1 Joint 1 shall control the motion between Limb 0 and Limb 1.
L 2.1.3.2 Joint 2 shall control the motion of between Limb 1 and Limb 2.
L 2.1.3.3 Joint 3 shall control the motion between Limb 2 and Limb 3.
L 2.1.3.4 Joint 4 shall control the movement between Limb 3 and Limb 4.
L 2.1.4.1 The Limbi will have 4 joints controlled by servos.
L 2.1.4.2 Each servo shall require no more than 7.4V and 500mA to run with a load
L 2.1.4.3 The servo shall be able to provide more than .237 Nm or 2.417kg/cm based on the force to move an object in a planar field
https://drive.google.com/file/d/1-dam0Q-eQ5knCC3htdMbFq1tB0Sh4aM8/view?usp=sharing
L 2.1.5.1 The docking mechanism shall consist of the interlocking mechanical device described in requirement L2.1.8.1 that allows the Limbi arm to successfully attach and detach to and from the module
L 2.1.5.2 The arm shall be able to attach and detach to and from a module without pushing the module away.
L 2.1.5.3 The module should have an androgynous connector.
L2.1.6.1 The slot for the cross shall be at least 1.52 mm larger than the cross based on the max vernier servo error of 1 degree.
https://drive.google.com/file/d/19Th-GjUsLZENH-va1Bqef4a42NR5o-Wc/view?usp=sharing
L2.1.6.2 Only 1 docking servo shall be in motion at once so the module does not un-dock
while the other module docks (this would cause power loss)
L2.1.7.1 The docking mechanisms on each end will be identical to each other
L2.1.8.1 The cross shall be between 7.19×7.19 cm and 8.89×8.89 cm in order to support the module-side dock and to fit within the module
https://drive.google.com/file/d/1Ozo8QtbmcpTTIGyFXx-LsOBe_Rc6u4hm/view?usp=sharing
L2.1.8.2 The depth of the cross shall be less than the depth of the module docking wall; the wall is based on the width of the modular contacts
L2.1.8.3 Copper tape will be used to conduct electricity between the cross and male modular contact
L 2.1.9.1 The magnets shall have between a .62lb pulling force and a 2.16lb pulling force. This will allow the magnets to be strong enough to hold together, but weak enough to not attract at further than 15mm away. https://docs.google.com/document/d/1DVFe5M6uZ-BYn5DWm9XuO3wccNZQ8Z0JOhozKRqyyIo/edit?usp=sharing
L2.1.10.1 The custom software will be implemented through the Arxterra App
L2.1.10.2 The user interface shall utilize wireless control of Limbi.
L2.1.10.3 The user interface shall have push buttons/toggles for pre-determined movements
L2.1.10.4 The user interface shall use simulated sliders for manual movements.
L2.1.11.1 The project will use an Arduino Nano mounted within the Limbi arm to allow the servos to be controlled directly by the microcontroller
L2.1.12.1 A TTL Serial Camera and TFT LCD display should be used to provide live video feed for alignment.
L2.1.13.1 The power provided from the module shall come from a battery via the docking mechanism
L2.1.13.2 The battery shall be capable of providing 1055 mA current (if needed) which will be used to power the MCU, 6 servos, Bluetooth Module, TFT LCD Display, and TTL Serial Camera.
https://docs.google.com/spreadsheets/d/14YAiEcqK4qWxwYl2tHo_f3VKfI4TdQ1mBcvUcpUTPt0/edit?usp=sharing (See power resource report)
L2.1.13.3 The battery used will be a Floureon LiPo rated at 7.4V, 1500mAh
L2.1.14.1 A make-before-break will be used to switch power
L2.1.15.1 One docking face shall be for module-to-module and the other shall be for arm-to-module connection.
L2.1.16.1 The LED on the module should be activated by one of the four power connections
L2.1.16.2 The LED should be on the corner so the person controlling the app can easily see that a secure connection has been made
Allocated Requirements / System Resource Reports
The mass allocation came from a rough estimate of the weight of our Limbi compared to the JPL version. We did not know the weight of the JPL version, but we did know that they required 80lb pull force magnets to connect the module to the arm; this gave us the idea that the arm weighed roughly 80lbs. Knowing that our Limbi would be half the size and made out of ABS plastic we allocated 5lbs to the project. The measured weights of Limbi were much less because the Limbi was printed in PLA. We largely overestimated the weight of the magnets, causing the docking mechanisms to weight more than the modules; this was corrected in the actual measurements and in reality the modules weight more than the docking mechanism. Our contingency is high to allow the Limbi arm and modules to be printed out of different materials.
Mass Allocations | ||||||
System | Resource | Total Expected Weight (g) | Measured Weight (g) | Uncertainty (%) | Margin (g) | (g) |
Limbi Arm | ||||||
3D Printed Plastic | 450 | 321 | 5% | ±22.5 | ||
HS-422 servos (4) | 184 | 172 | 5% | ±9.2 | ||
Arduino Nano | 7 | 6 | 5% | ±.35 | ||
Ball Casters (2) | 83.6 | 256 | 5% | ±4.18 | ||
Modules (2) | ||||||
3D Printed Plastic | 240 | 182 | 5% | ±12 | ||
Batteries | 176 | 179 | 5% | ±8.8 | ||
Docking Mechanism (2) | ||||||
3D Printed Plastic | 394 | 138 | 5% | ±19.7 | ||
Magnets (8) | 200 | 78 | 20% | ±40 | ||
HS-422 servos (2) | 28 | 86 | 5% | ±1.4 | ||
Miscellaneous
Components |
50 | 28 | 40% | ±20 | ||
Project Allocation: | 2267.96 | |||||
Total Margin: | 138.13 | |||||
Total Actual Weight: | 1446 | |||||
Total Expected Weight: | 1812.6 | |||||
Contingency: | 317.23 |
Table 1: Mass Allocation
The power allocation came from the estimate of the amount of current draw our servos would need. We knew that the servos would draw the most power and that we would have 6 servos. Estimating that each servo would need about 250A we decided on a battery that would provide 1500mAh. This provided the allocation for the battery which we used for the whole project. When we actually decided on parts we realized that the servos could draw a max of approximately 300mA each, but only 3 servos needed to be used at once since only one side of the arm would be used at once and one docking mechanism would be used at once. For our expected values we had large uncertainty because we didn’t know how the load would affect the current draw, this resulted in large margin. For contingency we were trying to plan for the worst case scenario and found that if all parts ended up being over the expected values that we would still have room left from our allocated amount of 1500mAh. This gave us leeway in how much we could add in terms of electronics and how long our battery would be able to run. For our actual measured values we ended up getting significantly lower current draw than expected from the servo, but we added an solenoid that draws significantly high current for a very short amount of time which caused us to use more than our expected current.
Power Allocations |
|||||
Resource | Expected Current Draw (mA) | Measured Current Draw (mA) | Uncertainty (%) | Margin (mA) | |
Joint Servos (4) | 600 | 40% | ±240 | ||
Vernier Servo (1) | 104.9 | ||||
Inner Joint Servo (2) | 222.3 | ||||
Inner Joint Servo (3) | 234.6 | ||||
Vernier Servo (4) | 112.2 | ||||
Docking Servos (2) | 300 | 40% | ±120 | ||
Dock 1 | 91 | ||||
Dock 2 | 96.7 | ||||
Hm-11 | 15 | 8.9 | 5% | ±.75 | |
Arduino Nano | 40 | 19.5 | 5% | ±2 | |
TFT LCD Display | 25 | N/A | 5% | ±1.25 | |
TTL Camera | 75 | N/A | 5% | ±3.75 | |
Logic Shifter | 7 | 7 | 0% | ±0 | |
Push-Pull Solenoid | 700 | 700 | 0% | ±0 | |
Project Allocation | 1500 mA | ||||
Total Margin | 367.75 mA | ||||
Total Actual Current | 1397.1mA | ||||
Total Expected Current | 1055 mA | ||||
Contingency |
77.25 mA |
Table 2: Power Allocation
Assembly | Print Hours (0.15 layer height and 50% fill) |
1 | 1230 min |
2 | 1200 min |
3 | 1000 min |
4 | 1440 min |
5 | 720 min |
Slicer Software used: Ultimaker Cura
3D Printer: Ultimaker S5
3D Printer Setting: 0.15 layer height, 50% fill, AA 0.4 print core
Material: PLA
Total: 5590 min (approximately 93 hours, 4 days)
Project Report
Project WBS and PBS
WBS:
Limbi’s Work Breakdown Structure is divided into three parts: Missions Systems and Test, Electronics and Control and Design and Manufacturing.
Missions Systems and Test is responsible for the System Requirement, Project Allocation, and Verification/Validation of tests performed by the E&C and D&M.
E&C is responsible for the electrical architecture of the arm and module, the power transfer design innovation and the PCB layout,
D&M is responsible for the arm, module mechanical design and the momentumless low-power mechanical docking mechanism design.
ADD DESCRIPTION
PBS:
The Limbi project breaks down into three separate components. It consists of the Limbi arm, the base module (Module 1), and the second module (Module 2). The arm further breaks down into 5 limb segments which are controlled by 4 joints, docking mechanisms on either end of the arm, and a power distribution system, communication system, and control system. The power control system, communication system, and control system are all implemented through the Arduino Nano, Hm-11, and a custom PCB and is located in the center of the Limbi arm. The docking mechanism is what interfaces the arm and the module. The docking mechanism on the arm connects to the docking mechanism on the module; this connection provides power from the battery in the modules to the power control system, communication system, and control system. Each module is identical and they both contain a battery and two docking faces; one face is a module-to-arm face (which provides power) and one is a module-to-module face (which connects the two modules to each other). This is shown below in Figure 6.
Cost
For the cost we did not have a project allocation of $250 because we are not a 3DoT project. Our allocation was determined at CDR based on how much we had spent, but even with our contingency and margin, we ended up going over budget due to final design changes and extra print versions that we did not account for. Our main suppliers were Amazon and Mouser Electronics. We went through many suppliers and compared the costs, but chose to use Amazon mainly because the lack of shipping cost we achieved through using a Prime account lowered the cost compared to buying from separate suppliers.
The total cost for the Limbi Spring ’19 project is about 400$.
Burndown Schedule
The project manager of the team have decided to use excel spreadsheet for the schedule burndown instead of targetprocess in which the whole team agreed on. The excel spreadsheet captures both action items and schedule breakdown in one whole matrix. Each task has 8 columns: description, priority (High, Medium, Low), assigned to (person responsible), status (open, percentage complete, pending, close), hours spent, expected and completed date, and comment. This way, we can easily keep track of what is holding the design process from moving forward.
Concept and Preliminary Design
Literature Review
We had three main literature resources for this project: JPL’s Limbi IEEE paper, and the previous Arxterra documentation for Research and the PDR.
The previous Arxterra PDR documentation consists of a project proposal for an interdepartmental project to improve the Limbi for use by JPL. It was proposed as a two semester senior design project, that would be a collaboration on work done by the MAE,CECS, and EE departments. The post laid out a potential schedule for the two semester course. The previous CSULB Mission Objective was to contribute to the advancement of the Limbi by adding mechanical interconnects for docking, and improving the power transfer.
The research overviewed the goal of the Limbi, the current design, and the power module design. Some key points it made were:
- The goal of the Limbi is to create a multi-jointed, autonomous robot that can reconfigure itself, and attach itself to other limbiods to construct spacecrafts or fix space structures.
- The current design is a JPL prototype that attaches itself to makeshift spacecraft modules and arranges them into a given position.
- This design uses cube-shaped power modules where one of the modules provides power to all other modules and to the detachable Limbi arm.
- The current docking mechanism utilizes electromagnets; however, this limits the rigidity of the modules.
The JPL Paper, “An Untethered Mobile Limbi for Modular In-Space Assembly” goes into detail about the current Limbi and the future of the Limbi. For the JPL mission objective it was to develop autonomous reconfigurable robots that can assemble and fix large space structures and spacecraft. The current JPL Limbi is not autonomous, but completes its mission using predefined movements. The JPL Limbi acts only on one plane and uses rotary actuators to control its movements. Other key factors that were discussed in this paper were the limb design dimensions, the symmetry of the arm, and the proportions of the limb segments which had small vernier segments and a large center segment. Unfortunately, JPL used electromagnets as the primary docking mechanism which was incredibly inefficient in terms of power use, and also the electromagnet strength drops rapidly with small distances, making the mechanism prone to failure from high moments.
We used information from these three sources to imitate the Limbi design and improve it. Based on the information gathered from our literature review we decided against the use of electromagnets (at the cost of no longer having androgynous docking), replaced the docking mechanism with a mechanical interlocking, low momentum option, and kept the form factor of the Limbi only modifying the arm proportions for the use of inverse kinematics.
Design Innovation
- Docking Mechanism – Momentumless docking mechanism for arm-to-module connection that is also compatible for module-to-module connection.
(see Mechanical Design Blog post for final version)
For the arm-to-module connection we used a momentumless docking mechanism. On the arm side, this mechanism includes a cross and a push-pull solenoid for auto lock. On the module side, the docking mechanism has two layers; the bottom layer is recessed and allows for the cross to be turned and the top layer covers the recessed part of the bottom layer so the cross can’t fall out. When the docking mechanism is in use the first stage includes the cross turning 45° and once in place the second stage of docking is the solenoid inserting into a fixed position (this is powerless). To undock, power must be applied to the solenoid to retract it and then the cross turns 45° counter-clockwise to complete undocking.
- Make-Before-Break Power Transfer – Continuous power supply to the arm pre, post and during module change.
To provide continuous power to the arm a make-before-break circuit was used. The cross represents the switch in the make-before-break circuit. In a make-before-break circuit it allows both batteries to power the arm before disconnecting from the original battery. This method was used to prevent the microcontroller from disconnecting between transfer from Module 1 to Module 2.
- Docking-Power Transfer Interface – Interface between the docking mechanism and power supply.
(see Mechanical Design Blog post for final version)
For power transfer between the modules and the arm male modular contacts and copper tape were used. On the module side the battery is connected to the male modular contacts. The copper tape is located on the cross and provides power to the circuitry located on the arm; when the cross is docked the copper tape touches the male modular contacts which allows power to transfer between the battery in the module and the circuitry on the arm.
Conceptual Design / Proposed Solution
Our team came up with a two layer docking design in the module side and a cross-shaped manipulator in the arm side that will initiate a 3 step sequence to dock: 45 degree turn for the cross, power transfer (or signal line for feedback) through the modular contact, and a actuate a normally open (lock position) push-pull solenoid valve for low power positive lock.
*picture of the actual design together
Figure 8: Exploded view of 3D Model
System Design / Final Design and Results
System Block Diagram
- System Block Diagram
The system block diagram shows how different components of the Limbi interface with each other. The power starts with the battery in Module 1 and powers the components within the Limbi arm through the docking mechanisms on the module and the arm. This power is then distributed to the Arduino and an external Buck Converter which distributes the power appropriately. The Custom PCB powers the servos and the Arduino sends commands to the servos using the Hm-11. The Hm-11 communicates with the Arxterra app and this then controls the servos which move the arm segments. A make before break system is set up so the arm connects to Module 2 and secures power before undocking from Module 1. This allows the Limbi arm to move from end to end while maintaining consistent power from one module or the other.
For previous iterations of the System Block Diagram see: System Block Diagram Iterations Blog Post
Interface Definition
For interface we have the interface matrix which shows connections between the Arduino Nano, Custom PBC, and components. This can be found in Figure below.
Below is the cable tree which shows how the wires will be routed through the Limbi. Power between the batteries within the modules and the cross will be transferred between aluminum and male modular contacts. (No wires will directly connect the module and arm). The cross will have grooves routed through the back to allow the wires to travel through, the wires will use a wire wrap around the connection of the cross to prevent them from tugging. The module wiring harness will consist of only the power and ground which will be connected to aluminum. The servo power wires will be connected to the custom PCB using JST connectors. The servos will implement keyed 3 pin molex connectors. The power wires will be connected to the arduino and the DC/DC buck converter. The buck converter will provide power to the PCB. The wires will be routed through the center of either side of the blue braces shown in the picture below.
Modeling and Experimental Results
The following are the experiments performed to tests design concepts:
- Magnet Test
Objective: To test if the magnets are strong enough to dock but weak enough to not cause stalled joint servos to rotate.
Magnets Test - Press Fit Test
Objective: To test tolerance to implement press fit design.
Press fit Test - Inverse Kinematics Test
Objective: To test code for a forward motion to align cross for docking.
Inverse Kinematics Test - Docking-Power Transfer Interface Test
Objective: To test continuity and resistance changes of the docking-power transfer interface design while the arm is moving the module.
Docking Video Continuity Test - Power Transfer Test
Objective: To test continuous power and power surges pre, post and during docking.
Power Transfer Breadboard Test Video - Servo Error Test
Objective: To the test the minimum servo rotation for our Vernier limb (L0 and L4) to calculate the slot tolerance for docking layer 2.
Servo Error Test
Mission Command and Control
The Limbi will be mainly controlled through the Arxterra App or Control Panel. All the control commands are custom commands, that are explained more in depth in the separate Firmware Blog Post. The custom commands that are used to control the Limbi are shown in the figure above. Below is a brief overview of the command, it’s address, and a general description.
- MOVE (0x01) – The move command has been modified to control the middle and end servos (on either side), and performs inverse kinematics to move the Limbi forwards, backwards, left or right.
- DOCK 1 (0x40) – The Dock 1 custom command is an ON/OFF button that docks and undocks the Limbi on side 1 of the Limbi.
- DOCK 2 (0x41) – The Dock 2 custom command is an ON/OFF button docks and undocks the Limbi on side 2 of the Limbi
- VERNIER CONTROL 1 (0x42) – The Vernier Control 1 custom command is a slider with values between 1 and 180, and it writes an angle to the vernier control on side 1 of the limbi
- VERNIER CONTROL 2 (0x43) – The Vernier Control 2 custom command is a slider with values between 1 and 180, and it writes an angle to the vernier control on side 2 of the limbi
- MIDDLE CONTROL 1 (0x44) – The Middle Control 1 custom command is a slider with values between 1 and 180, and it writes an angle to the middle joint on side 1 of the limbi
- MIDDLE CONTROL2 (0x45) – The Middle Control 2 custom command is a slider with values between 1 and 180, and it writes an angle to the middle joint on side 2 of the limbi
- INVERSE KINEMATICS (0x46) – The Inverse Kinematics custom command is a select with two options (END 1 and END 2). Selecting either end will determine what end the inverse kinematics will be performed on.
Electronic Design
Our E&C team went through quite a few design iteration for the PCB and the firmware to meet design requirement and specification.
PCB Design
We decided to make use of the Arduino Nano microcontroller due to its small scale and ease of integration with a custom PCB shield that would allow a better organization of wires in a small enclosed space located within the center limb of the arm.
The servos utilized were rated to operate at a maximum of 6V so we needed to implement a DC-DC buck down converter to drop the voltage down from the 7.4V Lipo Batteries we would be using. This means the board would have an input of 7.4V directly from the battery to power the MCU, a 6V to feed towards the servos, and naturally a GND. To serve as protection from potential short circuit dangers, 4.5A polyfuses were used on the power lines.
In addition to servo control, a solenoid valve would also need to be controlled as a positive locking mechanism at each end of the arm for docking. Since the solenoid pin was able to be compressed on its spring with very low amount of force the design was implemented in a way such that the solenoid would only need to be actively powered during the undocking phase in which it should retract thus allowing for free rotation of the docking servo. To achieve this a simple MOSFET switching circuit was implemented which allowed for the solenoid to be powered by the 6V line when the digital output D12 from the MCU applied voltage on the gate of the MOSFET. In addition to this a diode was connected between both solenoid lines which would prevent current discharging backwards when the solenoid coil disharged. Naturally this circuit was implemented symmetrically for both sides.
To facilitate wireless communication between the operating application and the arm an HM-11 BLE module was utilized. The bluetooth module requires a 3.3V power supply which is provided by the MCU 3.3V output. Since the MCU operated on a 5V logic and the HM-11 3.3V logic, translation between the devices was necessary. This was achieved through utilizing single buffer gate logic level shifters. These IC’s functioned to provide up and down translation based on the power supplied to them. So for instance the Tx pin on the MCU operates at a 5V logic level, by providing the buffer with a 3.3V power supply from the MCU, the logic is translated to a 3.3V level that can be properly interpreted by the HM-11 module, and vice-versa.
The schematic can be seen below:
The general design philosophy behind the board would be to have a symmetric output headers on the left and right side which would provide power and control signals to the 3 servos and locking solenoid on each side of the arm. At the top of the board would be the inputs for the direct battery, as well as the regulated 6V output from the buck converters. All components on the board were SMD with the exception of the diodes. Finally all four corners of the board contain screw holes to mount on the arm. The component list and board layout can be seen below, as well as a picture of the final assembled version and the buck converter used.
Firmware
Before I begin explaining the firmware, and how it controls each joint, I believe it is important for the reader to grasp how the Limbi looks, and the name of each joint. This will help the reader understand what I’m referring to in the rest of this section.
Limbi 2DAs seen in the Figure above, the Limbi has 4 joints. In this report, I will be referring to Joint 1 as Vernier Control 1, Joint 2 as Middle Control 1, Joint 3 as Middle Control 2, and Joint 4 as Vernier Control 2.
The firmware for our project contains 7 custom commands, and 1 command redefinition. These custom commands are: Move, Dock 1, Dock 2, Vernier Control 1, Vernier Control 2, Middle Control 1, Middle Control 2, and Inverse Kinematics Selector. These are briefly described in the Mission Command and Control Section above.
Below is the most recent code for Limbi.
#define DOCK1 0x40 // Dock 1 Custom Command Address #define DOCK2 0x41 // Dock 2 Custom Command Address #define VERNIER1 0x42 // Vernier 1 Custom Command Address #define VERNIER2 0x43 // Vernier 2 Custom Command Address #define MIDDLE1 0X44 // Middle 1 Custom Command Address #define MIDDLE2 0X45 // Middle 2 Custom Command Address #define IKSELECTOR 0x46 // Inverse Kinematics Selector Custom Command Address #define L1 154L // Define the length of length 1 (measurement is in mm) #define L2 125L // Define the length of length 2 (measurement is in mm) const uint8_t CMD_LIST_SIZE = 8; // Adding 8 commands (MOVE, DOCK1, DOCK2, VERNIER1, VERNIER2, MIDDLE1, MIDDLE2, IKSELECTOR) int globalIKSelector = 0; int globalPhi1; // Saves angle last written to mid1 Servo for use in Inverse Kinematics int globalTheta1; // Saves angle last written to end1 Servo for use in Inverse Kinematics int globalPhi2; // Saves angle last written to mid2 Servo for use in Inverse Kinematics int globalTheta2; // Saves angle last written to end2 Servo for use in Inverse Kinematics int globalCount = 1; // Used for conditional statements to prevent both docks from being undocked at the same time // Create variable speed servo objects VarSpeedServo mid1; // Joint 2 VarSpeedServo mid2; // Joint 3 VarSpeedServo end1; // Joint 1 VarSpeedServo end2; // Joint 4 VarSpeedServo dock1; // Dock 1 VarSpeedServo dock2; // Dock 2 void dock1Handler (uint8_t cmd, uint8_t param[], uint8_t n) { dock1.attach(2); // Attach dock1 servo to pin 2 if(param[0] == 1 ){ // Using boolean: if button = 1 = ON DOCK TO MODULE dock1.slowmove(41,40); // Move dock 1 Servo to angle of 41 degrees at speed 40/255 delay(2000); // Delay allows servo to get to position before detaching globalCount = 1; // Set Global Count to 1 when Dock 1 is connected } else{ // Else: button = 0 = OFF UNDOCK FROM MODULE if(globalCount%2==0){ // Takes remainder of division globalCount/2, to check if it's // odd or even // If it's even, it allows the dock to be undocked digitalWrite(10,HIGH); // Set's Push Pull Solenoid to High so that it retracts delay(1000); dock1.slowmove(90,40); // Move dock 1 Servo to angle of 90 degrees at speed 40/255 delay(1000); digitalWrite(10,LOW); // Set's Push Pull Solenoid to Low so power is not being drawn delay(500); } } dock1.detach(); // Detach dock1 servo from pin } // dock 1 Handler void dock2Handler (uint8_t cmd, uint8_t param[], uint8_t n) { dock2.attach(4); // Attach dock2 servo to pin 4 if(param[0] == 1){ // Using boolean: if button = 1 = ON DOCK TO MODULE dock2.slowmove(41,40); // Move dock 2 Servo to angle of 41 degrees at speed 40/255 delay(2000); // Delay allows servo to get to position before detaching globalCount = 2; // Set Global Count to 2 when Dock 2 is connected } else{ // Else: button = 0 = OFF UNDOCK FROM MODULE if(globalCount%2==1){ // Takes remainder of division globalCount/2 // If it's odd, it allows the dock to be undocked digitalWrite(9,HIGH); // Set's Push Pull Solenoid to High so that it retracts delay(1000); dock2.slowmove(90,40); // Move dock 2 Servo to angle of 90 degrees at speed 40/255 delay(1000); digitalWrite(9,LOW); // Set's Push Pull Solenoid to Low so power is not being drawn delay(500); } } dock2.detach(); // Detach dock2 servo from pin } // dock 2 Handler void vernier1Handler (uint8_t cmd, uint8_t param[], uint8_t n)//param[0] = unsigned byte (1-180) { end1.attach(3); // Attach end1 servo to pin 3 end1.slowmove(param[0],40); // Move end1 Servo to angle of param[0] degrees at speed 40/255 delay(2500); end1.detach(); // Detach end1 servo from pin 3 globalTheta1 = param[0]; // Saves end1 servo angle for use in Inverse Kinematics } // vernier 1 Handler void vernier2Handler (uint8_t cmd, uint8_t param[], uint8_t n)//param[0] = unsigned byte (1-180) { end2.attach(5); // Attach end2 servo to pin 5 end2.slowmove(param[0],40); // Move end2 Servo to angle of param[0] degrees at speed 40/255 delay(2500); end2.detach(); // Detach end2 servo from pin 5 globalTheta2 = param[0]; // Saves end2 servo angle for use in Inverse Kinematics } // vernier 2 Handler void middle1Handler (uint8_t cmd, uint8_t param[], uint8_t n)//param[0] = unsigned byte (1-180) { mid1.attach(12); // Attach mid1 servo to pin 12 mid1.slowmove(param[0],30); // Move mid1 Servo to angle of param[0] degrees at speed 30/255 delay(2500); mid1.detach(); // Detach mid1 servo from pin 12 globalPhi1 = param[0]; // Saves mid1 servo angle for use in Inverse Kinematics } // middle 1 Handler void middle2Handler (uint8_t cmd, uint8_t param[], uint8_t n)//param[0] = unsigned byte (1-180) { mid2.attach(11); // Attach mid2 servo to pin 11 mid2.slowmove(param[0],30); // Move mid2 Servo to angle of param[0] degrees at speed 30/255 delay(2500); mid2.detach(); // Detach mid2 servo from pin 11 globalPhi2 = param[0]; // Saves mid2 servo angle for use in Inverse Kinematics } // middle 2 Handler double forwardKinematicsX(){ // Calculates the current X Position of the endpoint on either end of Limbi int theta; int phi; if(globalIKSelector == 0){ // INVERSE KINEMATICS FOR SIDE 1 theta = globalTheta1; // Reads angle last written to end1 servo phi = 180 - globalPhi1; // Reads angle last written to mid1 servo } else{ // INVERSE KINEMATICS FOR SIDE 2 theta = globalTheta2; // Reads angle last written to end2 servo phi = 180 - globalPhi2; // Reads angle last written to mid2 servo } if(theta<=90){ // Adjusts angle value, due to servo position on Limbi theta = 90 - theta; } else{ theta = 360 - (theta - 90); } // Forward Kinematics equation for two DOF double xCalc = L1*cos(phi*PI/180) + L2*cos((phi+theta)*PI/180); return xCalc; // Returns current X Position } double forwardKinematicsY(){ // Calculates the current Y Position of the endpoint on Side 1 of Limbi int theta; int phi; if(globalIKSelector == 0){ // INVERSE KINEMATICS FOR SIDE 1 theta = globalTheta1; // Reads angle last written to end1 servo phi = 180 - globalPhi1; // Reads angle last written to mid1 servo } else{ // INVERSE KINEMATICS FOR SIDE 2 theta = globalTheta2; // Reads angle last written to end2 servo phi = 180 - globalPhi2; // Reads angle last written to mid2 servo } if(theta<=90){ // Adjusts angle value, due to servo position on Limbi theta = 90 - theta; } else{ theta = 360 - (theta - 90); } // Forward Kinematics equation for two DOF double yCalc = L1*sin(phi*PI/180) + L2*sin((theta+phi)*PI/180); return yCalc; // Returns current Y Position } // Inverse Kinematics Subroutine. Calculates Phi angle necessary for desired position int inverseKinematicsPhi(double x, double y){ // Inputs are desired X and Y Position // Inverse Kinematics Variable Calculations double C = (x*x + y*y - L1*L1 - L2*L2)/(2*L1*L2); double S = sqrt(1 - C*C); double K1 = L1 + L2*C; double K2 = L2*S; //Inverse Kinematics equation. Calculates Phi Angle necessary for desired X,Y Position double phi = 180 - ((atan2(y, x) - atan2(K2, K1))*180/PI); phi = constrain(phi, 0, 180); // Constrains the value of Phi between 1 and 180 int phiPass = (int)round(phi); // Rounds double to the closest integer return phiPass; // Returns angle to be written to mid1 Servo } // Inverse Kinematics Subroutine. Calculates Theta angle necessary for desired position int inverseKinematicsTheta(double x, double y){ // Inputs are desired X and Y Position // Inverse Kinematics Variable Calculations double C = (x*x + y*y - L1*L1 - L2*L2)/(2*L1*L2); double S = sqrt(1 - C*C); //Inverse Kinematics equation. Calculates Phi Angle necessary for desired X,Y Position double theta = (atan2(S,C))*180/PI; if(theta<=90){ // Adjusts theta angle due to servo position on Limbi theta = 90 - theta; } else{ theta = 360 - (theta - 90); } theta = constrain(theta, 0, 180); // Constrains the value of Phi between 1 and 180 int thetaPass = (int)round(theta); // Rounds double to the closest integer return thetaPass; // Returns angle to be written to end1 Servo } void moveHandler (uint8_t cmd, uint8_t param[], uint8_t n){ double x = forwardKinematicsX(); // Calls subroutine to calculate the current X Position double y = forwardKinematicsY(); // Calls subroutine to calculate the current Y Position if(param[0] == 1 && param[2] == 1){ // If the forward position on Joystick is pressed y =y + 5; // Increases the Y position, moving Limbi end piece forward } else if(param[0] == 2 && param[2] == 2){ // If the back position on Joystick is pressed y = y - 5; // Decreases the Y position, moving Limbi end piece backwards } else if(param[0] == 1 && param[2] == 2){ // If the right position on Joystick is pressed x = x + 5; // Increases the X position, moving Limbi end piece to the Right } else { // If the left position on Joystick is pressed x = x - 5; // Decreases the X position, moving Limbi end piece to the Left } int phiFinal = inverseKinematicsPhi(x, y); // Calls subroutine to calculate Phi necessary to get desired X,Y position int thetaFinal = inverseKinematicsTheta(x, y); // Calls subroutine to calculate Theta necessary to get desired X,Y position if(globalIKSelector == 0){ // INVERSE KINEMATICS FOR SIDE 1 end1.attach(3); // Attach end1 servo to Pin 3 mid1.attach(12); // Attach mid1 servo to Pin 12 end1.slowmove(thetaFinal, 20); // Move end1 Servo to calculated Theta mid1.slowmove(phiFinal, 20); // Move mid1 Servo to calculated Phi delay(1000); end1.detach(); // Detach Servo mid1.detach(); // Detach Servo globalTheta1 = thetaFinal; // Saves value written to end1 servo for use in Inverse Kinematics globalPhi1 = phiFinal; // Saves value written to mid1 servo for use in Inverse Kinematics } else{ // INVERSE KINEMATICS FOR SIDE 2 end2.attach(5); // Attach end2 servo to Pin 5 mid2.attach(11); // Attach mid2 servo to Pin 11 end2.slowmove(thetaFinal, 20); // Move end2 Servo to calculated Theta mid2.slowmove(phiFinal, 20); // Move mid2 Servo to calculated Phi delay(1000); end2.detach(); // Detach Servo mid2.detach(); // Detach Servo globalTheta2 = thetaFinal; // Saves value written to end1 servo for use in Inverse Kinematics globalPhi2 = phiFinal; // Saves value written to mid1 servo for use in Inverse Kinematics } } // moveHandler void ikSelectHandler (uint8_t cmd, uint8_t param[], uint8_t n) { globalIKSelector = param[0]; // Saves value of what end Inverse Kinematics will be performed on } ArxRobot::cmdFunc_t onCommand[CMD_LIST_SIZE] = {{MOVE,moveHandler}, {DOCK1,dock1Handler}, {DOCK2,dock2Handler}, {VERNIER1,vernier1Handler}, {VERNIER2,vernier2Handler}, {MIDDLE1,middle1Handler}, {MIDDLE2,middle2Handler}, {IKSELECTOR, ikSelectHandler}}; Packet motorPWM(MOTOR2_CURRENT_ID); // Initialize the packet properties to default values void setup() { Serial.begin(9600); // Default = 115200 ArxRobot.begin(); ArxRobot.setOnCommand(onCommand, CMD_LIST_SIZE); motorPWM.setAccuracy(1); // Change sensor accuracy from +/-2 DN to +/-1 DN motorPWM.setSamplePeriod(500); // Change sample period from 1 second to 0.5 seconds // Initialize servos dock2.attach(4); // Attach Servos to corresponding Pin end1.attach(3); end2.attach(5); mid1.attach(12); mid2.attach(11); dock2.slowmove(90,40); // Move Servo to Initial Position end1.slowmove(90,40); end2.slowmove(90,40); mid1.slowmove(90,40); mid2.slowmove(90,40); delay(5000); dock2.detach(); // Detach Servos from pin end1.detach(); end2.detach(); mid1.detach(); mid2.detach(); pinMode(9, OUTPUT); // Set Pins as Outputs for Solenoid Signal pinMode(10, OUTPUT); digitalWrite(9, LOW); // Initialize Solenoids to LOW or Off digitalWrite(10, LOW); delay(1000); globalPhi1 = 90; globalTheta1 = 90; globalPhi2 = 90; globalTheta2 = 90; } void loop(){ ArxRobot.loop(); #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) uint16_t pwm_reading = (uint16_t) OCR4D; // read 8-bit Output Compare Register Timer 4D and cast to 16-bit signed word motorPWM.sendSensor(pwm_reading); #else // Timer/Counter 0 registers set by UNO Bootloader (freq. = 1 Khz) // Timer/Counter Control Registers TCCR0B = 0x03 (Prescaler of 64) and TCCR0A (Fast PWM, TOP = 0xFF) = 0x03 uint16_t pwm_reading = (uint16_t) OCR0B; motorPWM.sendSensor(pwm_reading); #endif }
Libraries, Global Variables, and Initialization
Lets begin with the libraries being used in this code. The first library that is being used in this code is the “ArxRobot.h” library. This library is being used for it’s bluetooth capabilities, and ability to connect with the Arxterra App/Control Panel. The second Library being used is the “EEPROM” library. This library allows the Arduino to keep certain values when the board is turned off. The third library being used is the “Wire.h” library which allows for communication with I2C devices. The fourth library being used is the “VarSpeedServo.h” library. This library is being used to control the servos. The reason this library was chosen instead of the “Servo.h” library, is that this library allows the servos to be controlled with variable speed, and this way there wasn’t a need for a custom code to be written to control the speed of the servos. The “Servo.h” library supports up to 8 servos, and allows for simultaneous, asynchronous movement of all servos. The variable speed is from 1 to 255, with 1 being the slowest, and 255 being the fastest. The last library being used is the Math.h Library. This library allows for math functions and operations, and is used in the forward/inverse kinematics section of the code.
After the libraries were added to the code, we have our definitions, and creation of servos as objects. In this section, the addresses of our custom commands are defined. Two other definitions that were added are L1 and L2. L1 and L2 are the lengths of length 1 and length 2 of the Limbi in mm. Also in this section, all 6 servos were created as objects using the VarSpeedServo.h library.
Finally, the command list size was set to 8, and the global variables were set. The command list size is the number of custom commands that we are using, and it ties back the address of the command to the subroutine name. The global variables being used are the following. globalIKSelector–stores the value of the ikSelectHandler subroutine. This subroutine is a custom command that allows the user to choose what end of the Limbi the inverse kinematics will be performed on. GlobalPhi1, globalTheta1, globalPhi2, and globalTheta2 are place holders that hold the angle last written to the middle1 servo, end1 servo, middle2 servo, and end2 servo respectively. These global variables are being used because the angles are used over several subroutines for the inverse kinematics. The last global variable is globalCount. This variable is used for the docking subroutines and is used to ensure that both docks aren’t undocked at the same time.
During the initialization of the code, all of the pins are set to their corresponding servo, or are set as Output/Inputs. All of the joint servos are set to 90 degrees, which will result in the arm being completely straight. Dock 1 servo is set to a docked position, while Dock 2 servo is set to an undocked position. Pins 9 and 10 are set as Outputs, and set to LOW. These two pins are the pins that will be used for the solenoids on the docking mechanism.
Dock1Handler and Dock2Handler Subroutines
The Dock 1 command and Dock 2 commands perform the same function just on opposite sides of the Limbi.The Dock 1 code and Dock 2 code are almost identical. These two subroutines are called whenever the user presses the ON/OFF button with the command address 0x40, and 0x41 for Dock 1 and Dock 2 respectively. When the user presses the button, the Arxterra App or Control Panel send a 0 for OFF and a 1 for ON.
The first thing that happens when this is performed, is that the respective servo is attached to it’s corresponding digital pin.
Then there is a conditional statement that depends on the parameter sent by the user through the app or control panel. If the user sent a parameter of 1 (Or pressed the ON button), then this causes the Dock Servo to turn and therefore Lock the Limbi to the module. The dock servo turns to an angle of 41 degrees at a speed of 40/255. If the conditional statement reads a 1, it also causes the globalCount variable to set to 1 when Dock 1 is docked, and it sets the globalCount to 2 when Dock 2 is docked. This is important to note, because this is what prevents both docks from being undocked at the same time.
The second half of the conditional statement (the else part), will run if the user sent a parameter of 0 (Or pressed the OFF button). This section controls the undocking (or unlocking) between the Limbi and the module. However, as was explained in the previous paragraph, the undocking is controlled and both docks can’t be undocked at the same time. This means that in order for one dock to undock, the other dock must have been connected previously. As stated earlier, when Dock 1 is ON, it saves a value of 1 in globalCount, and when Dock 2 is ON, it saves a value of 2 in globalCount. In order for the undocking to take place, Dock 1 has another conditional statement that takes the remaider of a division between globalCount, and 2. If the answer is 0 (no remainder) then the actual undocking occurs. If thats not the case, then it doesn’t undock. The reason this is set this way is because it’s set this way for both Dock 1 and Dock 2, and it helps prevent both docks from being disconnected at the same time which would result in a loss of power.
Vernier 1 and 2 Handler, and Middle 1 and 2 Handler
These four subroutines all work the same way, and essentially do the same thing. On the Arxterra App/Control Panel, these commands were made into sliders with a value between 0 and 180. When the user moves any of these sliders, the respective subroutine is called, and the value sent from the slider is received in the subroutine as the zeroth element in the vector “param” (param[0]). Param[0] is the angle that we want the servo to move to. The respective servo is attached to it’s corresponding pin, and the angle is written to the servo, with a certain speed using the slowmove() command from the VarSpeedServo library. The servo is then detached, and the angle that was written to the servo is then saved in it’s corresponding globalVariable.
Forward/Inverse Kinematics
This is perhaps the most complex section of the code, and it involves several subroutines working in unison. These subroutines allow the forward or inverse kinematics to be performed on either the middle 1 and end 1 joints or the middle 2 and end 2 joints. One thing to note about this section is that whenever Phi corresponds to the middle 1 or middle 2 joint, and Theta corresponds to the end 1 or end 2 servo joints. This is seen in the figure below.
The first subroutine I will explain is the ikSelectHandler subroutine, as it is the simplest. This subroutine is a custom command that uses the Arxterra app/control panel. This is a select button that has an option between Side 1 and Side 2. When the subroutine is called, and the user selects an option, the vector element “param[0]” recieves either a 0 or a 1. This element value is saved in the global variable “globalIKSelector”. If globalIKSelector is a 0 that corresponds to side 1, and if it’s a 1 it corresponds to side 2. This is used later for determining what side to perform the Inverse Kinematics on.
MoveHandler Subroutine
The second subroutine that deals with the inverse kinematics is the MoveHandler subroutine. The overall goal of this subroutine is to move the limbi, with respect to either the middle 1 joint or middle 2 joint, in the forward, backwards, left or right direction. If globalIKSelector is a 0, then the reference point will be the middle 1 joint and the end point will be the end of the length on the end 1 joint. If the globalIKSelector is a 1, then the reference point will be the middle 2 joint, and the end point will be the end of the length on the end 2 joint.
I will only explain how the inverse Kinematics works on one side, as it is the same on both sides, and this way it will be easier to explain and understand. Using the middle 1 joint as our reference point, the location of this joint becomes (0,0) in an X,Y Plane, with the position of the end point on the end 1 joint being in the positive Y direction when fully extended.
The move command is called whenever the user presses either of the buttons on the joystick in the control panel/app. When this happens, the first thing that happens is that the MoveHandler calls the subroutines forwardKinematicsX and forwardKinematicsY. These subroutines use the globalPhi and globalTheta which are saved each time an angle is written to middle1 servo and end1 servo. Some arithmetic is performed on the angles to get the desired angle for the equation, and then the forward kinematics are calculated using these angles to determine what the current X,Y position of the end point is. The X and Y values are returned back to the MoveHandler.
Once the X and Y values are in the moveHandler, the code determines what position we wish to go to. The joystick in the app/control panel is meant for motors, and determines the direction and speed of the motor. The limbi code only utilizes the direction to determine what our desired X and Y position is. The moveHandler then has a conditional statement to determine whether we will be increasing or decreasing in the X or Y position. The vector param, has a length of 4, however the elements param[0] and param[2] contain the direction of the joysticks. Param[0] = 1 & param[2] = 1 corresponds to forward, param[0] = 2 & param[2] = 2 corresponds to backwards, param[0] = 1 & param[2] = 2 corresponds to right, and param[0] = 2 & param[2] = 1 corresponds to left. The conditional statement uses these parameters, and if it’s forward it will add only to the Y position, if it’s backwards, it will subtract only from the Y position, if it’s left it will subtract from the X position, and if it’s right it will add to the X position.
This X and Y position are now sent to the subroutines inverseKinematicsPhi and inverseKinematicsTheta. These subroutines perform inverse kinematics calculations on the X and Y position that was determined we desire the limbi to go to, and then calculate and angles for Phi and Theta that will move the endpoint to that location. These values are then returned to the Move Handler.
Once the MoveHandler has the final angles calculated by the inverse kinematics, it then writes the angles to the corresponding servos.
Mechanical/Hardware Design
There are three main mechanical assembly for Limbi: Arm, Docking mechanism, and the module. Rapid prototyping lead to several critical design changes.
Limbi Final Mechanical Design Blog Post
Verification & Validation Test Plan
For more details see the Test Plan Document
For the Limbi project we will verify that our design meets design requirements through the Verification Test Plan. We have four separate test cases. Each test case will have a test plan that walks the tester through how to test each requirement and provides a detailed success criteria which determines if the requirement passes verification. The test cases include TC-1, TC-2,TC-3, and TC-4. Test Case 1 is a measurement and inspection test that verifies easily verifiable requirements such as “will” requirements, size requirements, and requirements verifiable by data sheet. Test Case 2 is a test that verifies servo movement and mobility in simple tasks; this test will verify that the servos meet the requirements of basic movement and that the Arxterra app is correctly controlling each servo and moving it the desired amount. Test Case 3 tests complex servo movement which relies on inverse kinematics and includes getting the Limbi arm into the docking slot. Test Case 4 starts with the assumption that the cross has been inserted into Module 2 and is a test that verifies the ability of the Limbi to securely dock and transfer power between the Module and the arm. Test Case 2-4 will represent our mission profile and complete the task of validating that the right product was built for the mission. This mission plan will start with the assumption that the Limbi arm is docked to a secured base module. From there the Limbi arm will be maneuvered using the Arxterra app to come close to Module 2, insert the cross into Module 2, and then dock with Module 2 by rotating the cross and inserting the solenoid. Once the arm is docked to Module 2 (or with the assumption that the arm starts docked to Module 2) the Limbi arm will then maneuver Module 2 to be next to Module 1. Using the magnets on the module-to-module faces Module 1 and Module 2 will dock. Once Module 1 and Module 2 are docked, the Limbi arm will undock from Module 1 while keeping docked to Module 2. It will then move away from Module 1 to visually demonstrate that it is no longer being powered by Module 1 and has transferred to Module 2 as a power source.
Concluding Thoughts and Future Work
Future Work
- 3DOT version
- Scaled down (One-print assembly)
- 3DOT MCU
- Live video feed; re-design docking cross around TTL serial camera and add it to Mission Control Center
- Full range actuator (vs servo with just 180 deg range) for joints for an easier inverse kinematic
- Photo interrupter for feedback for the solenoid valve positive lock
- A better low power momentumless docking mechanism design that is closer to androgynous/universal
Rapid prototyping helped our team tremendously. Though we did not caught all (ie the limited range of servo restricted some of the movement of the arm causing last minute change with the dimensions of the limbs), we saw design flaws early in the design process because of rapid prototyping that saved us time and resources. We are also able to make adjustment such as tolerances and placement of parts early that help us kept the design process going.
Top 3 tips
1) Take ownership of the project (especially PM)
2) Don’t be afraid to spend money on sensors/actuators/modules and try your ideas. If you don’t get reimburse, keep it and use it for a different project.
3) Always acknowledge customer’s feedback/suggestions.
References/Resources
These are the starting resource files for the next generation of robots. All documentation shall be uploaded, linked to, and archived in to the Arxterra Google Drive. The “Resource” section includes links to the following material.