Here is "InMoov", the robot hand you can print and animate. You have a 3D printer, some building skills, This project is for you!! This is a hand builded for a job (still picture commercial), it was supposed to be mobile but not animated and mostly be able to take human hand positions. Cables or fishing rods might be added in order to control it, but it was not the purpose of this work at first. I am now working on another model(for the fun), trying to animate it, I really would like to do it with computer control but I have never worked in that field. For more parts and info, see:
http://www.thingiverse.com/thing:17773
http://www.thingiverse.com/thing:18939
Hand Jive script is now in MRL
jython -> examples -> Robots -> inMoovHandRobot.py
So this works for me - It
So this works for me - It starts an Arduino & Servo up.
You'll have to change the port from "/dev/ttyUSB0" to something like COM4 or COM2 for your Windows XP machine. And then provide the correct pin number attached to the servo, and load MRLComm.ino into the Arduino so MRL can control it.
Okay, got it! Yes you were
Okay, got it! Yes you were right the copy paste did put an extra space on the front of each sentence.
So this works, I looked at the configuration on the Gui to see what has been created, I still can't figure out how the routing works. But I guess lets do it by step.
Did the servo move ? If
Did the servo move ?
If so, that is great ! we are moving along - What do you want to do next? Get all the fingers involved? Build some hand gestures ? Do you have the microphone ? What next ! What next !
If not, that's ok - if you flip over to the arduino tab -> oscope -> do you see a trace ?
Thanks for creating this
Thanks for creating this post, didn't know how to start it. And you're right, it's better if others can get a use of it.
I like the title!
Okay I just sent you a log file, I did change the port to COM7, I modified my baud port according to yours, used the pin5 for my servo but something has a "mismatched input" right from the start.
And welcome to Python. One
And welcome to Python. One of the first things to know about the Python language is that it is VERY STRICT about indentation. It reminds me of my 3rd grade teacher, and beginning to write paragraphs (its sooo strict)
You have to make sure everything is on the same alignment, as it looks like in the segment I've pasted & the picture I've posted.
I have had problems when copying and pasting Python from a web post in the past - where an extra space or tab was added.
If you don't find anything like that, and it doesn't look mis-aligned - then I will attach the program as a file instead of copy & past from the post page.
Good Luck.
P.S.
Did you load MRLComm.ino on the arduino ? This is a Arduino script and it allows MRL to control the Arduino board.
I'd recommend loading it through the Arduino IDE, which I believe you have experience with.
The script can be found here - http://myrobotlab.googlecode.com/svn/trunk/myrobotlab/src/resource/Arduino/MRLComm/MRLComm.ino
You want to uploaded it to the Arduino.
Yay it move ! Yes it is not
Yay it move !
Yes it is not smooth movement. We can make smooth quick movements. In that case we don't want to increment, but just tell the servo where to go in one statement.
like this
It can be quicker to get things done with Python, instead of the GUI - but the GUI can be helpful too. One does not preclude the other. You won't have to "learn" Python. I barely know Python. But, if you see "Cause and Effect" and you saw the cause was a new line of Python - then you can "tweak" that python to get a different effect. It can be fun and easy to learn this way.
Since we have the copy & paste down I will make another script. This one will be with COM7 and the appropriate pins for all the fingers - then we will have a hand & hand gesture test.
Maybe we should think of appropriate hand functions -
I'll take a crack at a few of these - I think the code you sent me has all the pin numbers of the servos.
After a few hand movements are put into the Python script, when you get the microphone, you can activate it by words.
I had a thought were you can speak, the speech goes to text, the text is broken down into letters, and the letters are sent to the hand sign alphabet - then you'd have a speech to hand-sign robot .. that'd be pretty cool.
I should have looked at your
I should have looked at your code before coming up with ideas :D
Looks like you've implemented a lot already.
So, to go to the next level, I just ported some of your code. All the absolute hand position movements. This is just to test and see "cause and effect".
Hopefully, the "effect" will be several of your hand gestures repeated at 0.5 second intervals.
Fantastic work here, I didn't
Fantastic work here, I didn't expect so much. When I run the script every thing gets initialised, the arduino, the servos are all there, the script runs to it's end correctly.
But nothing much happens.
During create and start the Arduino , there is a short buzzing from the servos. I looked in the Arduino tab and the COM7 (I had modified the script to 7 not 6) is not marked, in the pins tab none of the pins are on. Then looking at the servo tabs, none of them are attached nor configured to the Arduino or to any pin.
So that is strange. I could rerun the script after configuring everything by hand. But of course the script tells me an error since every thing is already created.
How can I rerun the script without:
Ahhhh !
Panic ... hit the Help->About -> "GroG, it No Worky" button - that will send me a log.
Yeah, the fact that the Servo's don't show they are connected to the arduino on the appropriate pins, is something I'll take care of shortly... But they "should" be attached. The script is attaching them, its just the GUI doesn't know about the script doing it...
Anyway, in answer to your question, MRL can control as many Arduinos as you can think of unique names ;)
E.g. arduino01 arduino02 ..... arduin0N+1
It's not limited.. just limited on memory & cpu, but I suspect evenn with your windows XP box you could control several thousand, if you had that many USB ports..
Also if you computer runs out of USB ports MRL can attach and control other MRL instances running on other computers (but let's not go there ... yet ;)
Fantastic, we got it working
Fantastic, we got it working with MRL! It is amazing when it becomes alive all at once. Don't know what you think, but maybe you should replace the script uploaded on this thread by the working one? If others stumble on this, they can get something that works directly.
Is there a way to say to a gesture to wait until the one before is complete for to start, instead of just adding delay? The "shoulder" takes a delay of about (6000) in Aduino for to acomplish 0 to 180, when "thumb" takes only "800". (these delays are just as an exemple)
I removed the inline script
I removed the inline script and added a reference on your post - where the script can be found in MRL. It's better this way, because the script will always be the "most recent".
We can add appropriate timed delays in milliseconds (that is what Arduino uses), but the robot can't tell at the moment when a move has competed, because there is no feedback sensor, like a limit switch, a potentiometer, or an encoder.
If you want I can add 6 seconds after each gesture, or you can tell me specific timings if you want.
Just thought of an idea. If
Just thought of an idea.
If you get a Kinect (you have to get one with the seperate power supply to work with a computer's usb - or maybe they changed this) - http://www.ebay.co.uk/sch/i.html?_nkw=kinect+sensor
Then you can do gestures and the robot will mimic you. Like the Simon says game. Monkey see, monkey do... or I guess Monkey do, Robot do too ;)
Well, yes, but I don't want
Well, yes, but I don't want to have a monkey in my workshop, doing all what I do! The monkey looks for a brain remember. Or could I show him and he remembers the movement to play them back later?
Anyway I rmodified a little the gesture python script to have something more smooth and with a little more delays to accomplish full gestures. I will send it by mail so you can update it, if you feel.
Finally got a camera today with high frame rate with built-in microphone using USB port, so what should I do with that to give him ORDERS!!!! I also added extra power supply because with all the servos assembled it couldn't reach enough speed anymore.
Opencv still don't work of course, but I had hope maybe it would.
Hope your having fun, telling
Hope your having fun, telling your robot what to do ..
I think its great that you put your designs up on thingyverse ! If a chimp like myself wanted to print your designs would a 20cm X 20 cm Printrbot be enough?
Do you have a list of materials - e.g. size, type & number of servos ?
I have a wheelchair, which I've been meaning to connect to a MyRobotLab brain, on top of that base I think it would be great to build your torso - The school kids would be really inspired !
Thanks fo rthe new vidoes
Thanks fo rthe new vidoes !
I've updated the script. Here is a list of changes.
"all open | hand close | pinch mode | open pinch | hand open | hand rest | hand open two "
which corresponds to the methods you originally created in the Arduino sketch. I usually recommend transparency and a one to one correspondence to the methods. If you decide to make a handsup method I would recommend you add a "hands up" grammar to activate it. It just makes the program more clear.
ear.stopListening() when the gesture is done the ear is turned back on with ear.startListening(), this should help reduce the servo noise feedback interfering with a false command
Goodbye inMoov
Wow, Lots of things to do
Wow, Lots of things to do !
Well with offline communication going for a while - I'll post a list of things I need to do in order to make an InMoov Service & lots of new features....
I looked at the latest log file :
I noticed that you are running it from "C:/Program Files/Myrobotlab/intermediate.884.20121021.1606"
I have a tingly feeling that it doesn't like the space in the "Program Files"
Try moving it to:
C:\myrobotlab\intermediate.884.20121021.1606 - and try OpenCV again
Okay I moved it as you said
Okay I moved it as you said to C:\myrobotlab\intermediate.884.20121021.1606 but unfortunatly it still gives the same error.
I just sent you the log file.
I am sure it's a lot of work, but once again, there is no rush, it has to be fun, we are not at work!
Really with the new voice commands it's doing so much better. I have just ordered a special power supply, because I came to realise that if I don't have enough power, the servos tend to act strangely. 24 servos is a lot of Amps. Until now I worked myself with, small outlet supplies but if I want to power up everything at once, I need a strong source. I got a efuel 6V20A. I think it should do the trick.
I have the InMoov Service
I have the InMoov Service checked-in, which is to say it has
What is missing -
You will have to download the bleeding edge to get this - "not just hit the bleeding edge button" there is a difference.
What you should see -
After downloading the latest intermediate - when you start you should see the InMoov Service - right click on it and install - it will download all the dependencies.
Restart -
In the Jython panel type the following - and see what happens (adjust for the correct values of left & right boards)
let me know what happens
Yes I tried the superb InMoov
Yes I tried the superb InMoov service, and it loads all fine, but I have a few servos that can't work because of a lack of power supply. I guess a service can only work if everything is connected, and I can't uncheck some servos before launching the service. (Or can I?) So I won't be able to try until I get my new power supply.
But in the meantime I finally got OpenCV working, I went through all kinds of forums and post tonight about my problem, and solved it. I have WindowsXP., so I installed in system32, avutil-50.dll, and installed Microsoft C++2010. I had the 2008 version. And I was astonished when finally it worked! What a gamble.
Of course I went directly trying the gadgets I had read about like facetracking, finding a ball with InRange filters. And it works really great! Amazing when the head is following a purple box. So this is an open door to much more with MRL, I can't wait to make InMoov recognize some colored cubes and grab it!
Yay ! - and Grrr ... I'm
Yay ! - and Grrr ... I'm sure it was the MSVCRT.dll and its brothers ... I don't understand why Microsoft doesn't distribute this immediately. Maybe, I should put it in the repo.. Anyway, I'm very glad you figured it out.
For removing a service, you should be able to right click in the "current servies" and choose release. I've been meaning to do this right click menu item on the tabs too but the menu is not consitent (yet)
I'd like to get the service working before investing anymore time in the scripts. Makes things easier in the long run. If you ever find something which doesn't work always send a no-worky log file, thanks
I worked on InMoov service
I worked on InMoov service some more.
It should:
The following script should load everything including opencv - and initialize everthing properly (you'll have to change the values for your boards) I tested this as best I could with a single arduino and a single servo
I'm going to try this
I'm going to try this freshhhh release as soon it's downloaded, and let you know how it went. I really wonder how you can test all that with only one servo... It's a mystery and it will remain.
A group of elves make the
A group of elves make the software, and the dwarves test it.....
Ah, Ah they all could be
Ah, Ah they all could be called hairy something
Here are the values we should
Here are the values we should not override at the present time.
Bicep should never go more then 0 to 90
the omoplat 10 to 80.
rotate is delicate because if the arm is over the head it can do a full rotation 0 to 180, but if the arm is at the level of stomach, going to 0 it's like having the hand inside it's stomach. let say for now 40 to 180
this is what I currently have
this is what I currently have on initialization of the InMoov service :
How to add an OpenCV filter
How to add an OpenCV filter ?
Go to the OpenCV Service page here - http://myrobotlab.org/service/opencv#AddFilter
Thanks, pyramidedown was the
Thanks, pyramidedown was the one I needed.
Hi Hairygael, I've been a
Hi Hairygael,
I've been a bit distracted with some serial communication issues on some of the platforms (Linux) and have not put a lot of updates in the InMoov service.
As soon as I am done with the issues, I should be able to focus on InMoov.
Hope things are going well with you.
My future plans for the InMoov service are :
1. Full Initialization - once you start this service everything needed is ready.
2. A System test - test all servos sequentialy - I wonder if it will look like he is doing a strange dance move. This is helpful on startup to see if everything is functioning correctly, before you start playing with him.
3. A way to add dialog to Sphinx easily so a command can be easily mapped to an action.
4. OpenCV Lucas Kanade (LK) Tracking - say command - starts a tracking point - map it to a finger servo initially, so InMoov should move its finger proportionally to the tracking point.
If you have more ideas let me know.
Regards,
GroG.
This all sounds great! I was
This all sounds great! I was also away for a few days, so I haven't done anything much.
I received the power supply, and it seems to be great, I rebuilded a pcb with connectors to make sureit can support enough Amps in the wires. Did a test tonight and it all works, only rothead on pin 13 doesn't work. I switched of board and the problem remains, but only when I run MRL. If I use the Arduino.exe, it works. Could it be in the initialisation?
Your idea of a system test is very good, I suggest to make only a short and smooth move per servos, because for the new builders of InMoov it will be less stressfull. (wires might get stucked or stretched, the arms may not have enough room to move) I rather check smoothly, YOU NEVER KNOW. Ideally would be running in this order, thumb,index,majeure,ringfinger,pinky,wrist,bicep, rotate,shoulder, omoplate,neck, rothead, jaw,eyesup, eyesdown. We could have both arms simultaneous to make it less long.
"I was also think about how a moveArm method with 3 or 4 parameters would
be helpful like the moveHand method. The 3 parameters would be wrist -
elbow - shoulder, with an optional omoplat."
This also is a very good idea! Ideally would be 5 parameters because to acomplish grabing movements or lifting arm up, I need to use all five servos. Wrist, bicep,rotate,shoulder,omoplate
Then will come the head...
"3. A way to add dialog to Sphinx easily so a command can be easily mapped to an action.
4. OpenCV Lucas Kanade (LK) Tracking - say command - starts a tracking point - map it to a finger servo initially, so InMoov should move its finger proportionally to the tracking point."
I just can't wait to try all of that!
"If you have more ideas let me know."
You know I mentionned the speed of the servos, could we have something like what you have made with a range of speed between (1) and (5) 5 would be the fastest.
Gee, it's like I make a wish list to Santa Claus!
I am done with serial
I am done with serial problems for a while. Was not completely successful, but it's time to move on. Working on InMoov now.
The first thing I will do is make Servo speed adjustable.
The notation you suggested would be rather challenging
inMoov.moveHand("left",90{2} ,90{5} ,90{5} ,90{5} ,90{5}
I would propose the following - a setHandSpeed method with the following conventions:
It would use the same position signature as moveHand, however, a fractional amount (value between 0 <-> 1) would be supplied for proportional speed.
For example:
This would set the thumb speed of the left hand at 80% - all other fingers would move at full speed. The servo would remember and move at it's "set" speed until changed.
Will this work for you?
I understand the challenge
I understand the challenge you are talking about, this solution is fine with me. How will we proceed? Start the service, it will initialize every thing, test the servos, and then in jython I will write: inMoov.setHandSpeed("left", 0.8, 1,1,1,1)
inMoov.moveHand("left", 90, 90, 90, 90,90)
Then select another part to move and so on? Do you think I can test each move to make sure I'm happy with the position and speed before to go on the next part?
I tried to use the sliders for to define some standard position and it is a good solution, it helps to forsee what can happen, like two hands colliding, or the arm hitting the head...
It's always scary when you load the script and it starts to go, I'm always ready to unplugg the two arduinos.
Last night I was using the sliders for the head movements to understand the vision tracking, and all of a sudden, the bicep started to act but totally the wrong way, of course it broke a part. (All servos are powered even if I use only two of them). Luckily the printer can reprint the part easily.
Correct. We shall proceed
Correct. We shall proceed this way.
Since the Servos will now have "memory" you will only need to set the hand speed if you intend to change it. All subsequent moves will be at the last set speed.
Do you think I can test each move to make sure I'm happy with the position and speed before to go on the next part?
Yes, you need to clear the jython screen and put in only the code you want to test - once you like it save it in a text editor somewhere else. That would allow you to try the moves incrementally. (It would be nice to highlight the script and execute only that part - but this is a future enhancement)
I tried to use the sliders for to define some standard position and it is a good solution, it helps to forsee what can happen, like two hands colliding, or the arm hitting the head...
Excellent. Avoid hitting the head ;)
It's always scary when you load the script and it starts to go, I'm always ready to unplugg the two arduinos.
Hopefully the limits will make you feel safer, additionally you can "detach" and re- "attach" the servos through MRL. I noticed controlling continuous rotational servos, it was the only way to completely stop them.
I'm working on the speed stuff now, I'll let you know when I have an update you can try.
Before setHandSpeed....
There has to be smaller blocks....
I've just implemented Servo.setSpeed
I've tested it with 1 servo - and it "seems" to be working correctly.
It's should be smoother than moving the servo through Jython.
If you could test with 2 servos that would be great..
You'll have to update MRL of course.
If this make no sense, let me know...
Check this for some detail :
http://myrobotlab.org/service/servo
Simple tuto for the beginners of MRL
This a simple tuto for to start up MRL and InMoov in good conditions. I hope I didn't forget important things.
http://inmoov.blogspot.fr/2012/11/inmoovs-brain-at-work.html
Hi Hairygael,Have not heard
Hi Hairygael,
Have not heard from you for a while, hope things are well.
This post describes the current InMoov service - http://myrobotlab.org/service/inmoov
It will be the page people go to when they right click -> press info on InMoov
Hi GroG, I had too much work
Hi GroG,
I had too much work lately, and barely had anytime during my nights to check on updates. I can see that you have done a lot of work here. I'm glad you finally went through your issues with serial problems.
I need to link your "info" tuto on my blog for the new arrivers.
Okay, I hooked every servos to a self made connector board which should avoid bad electrical contacts during movements of parts. I still need to extend some servo cables because during some extremes or unexpected movements some of the wires are still too short.
I just tried with the last "bleeding edge", the service InMoov and it loads everything as expected, but none of the servos are "attached" since the arduino boards aren't configured before initialization. That's normal of course. In the service if I configure the boards and attach each servo one by one, it works. But doing :
So I tried inMoov = createAndStart("inMoov", "InMoov") directly in Jython but it gives me an error. I have sent you the log file.
Then I tried inMoov.initializeRight("atmega328p","COM8") and inMoov.initializeLeft("atmega1280","COM7")
I also get the same sort of errors. I am sure it must be a simple thing, but I can't seem to figure out what it is. I have sent you the log files.
I will try other workarounds this afternoon to see if I can trigger InMoov with speeds.
Wecome Back ! I saw the 2
Wecome Back !
I saw the 2 error logs :
There are 2 ways to start a Service.
Method 1: Use the GUI
Right click on the desired Service. If it needs installing, install it first. If the Service is installed, then you may right click and select start.
Next you will be asked for the name of the service. The name could be anything you want, but there are 2 rules. First, the name must be unique - no other service can have the same name. Second, any Python script must refer to this service with the same name.
I usually name the Services I start with a small letter at the beginning. I just do this from force of habit. So an instance of the InMoov service I usually name inMoov. But, that's just me :)
Method 2: Use Python
You were right to try the example on the Service page - but the example was wrong :P (sorry) .
I have updated it.
It was :
It needs to be :
Here are the initial
Here are the initial positions at start up that would suit me the best for both sides.
thumb 0
index 0
majeure 0
ringfinger 0
pinky 0
wrist 90
bicep 0
rotate 90
shoulder 30
omoplate 10
neck 90
rothead 90
Okay both sides can initiate
Okay both sides can initiate correctly. The initial positions at starts avoids very much the stress of "What is going to happen when plugged". It really avoids big arm movements and sets InMoov in a rest position.
Now what seems to be happening and needs to be looked at is:
-After the init. I get three left fingers detached from the arduino, thumb, index and majeure and can't seem to be able to reattach them unless the gui is restarted.
-rothead is not attached either. Trying to attach it has no effect.
-set speed works nice for the fingers but seems still a bit fast for omoplate. I haven't tested on shoulder rotate and bicep. This might be due to the fact the potentiometer is set outside the servo, and the servo needs to make more rotations to get to its position.
Initialization is different -
Initialization is different - You only initialize the parts which you want to control ... the other parts should lie dormant...
Also "left" and "right" are keys and you may initialize other systems e.g.
inMoov.initialize("left1", "uno","COM9")
might be appropriate if you had 2 left arms.. this would be relevant if you wanted to make a Shiva model ;)
Servo Problems...
I was going to suggest a few things. I think you had the right idea in disconnecting and re-attaching the servos to try to isolate the problem. A individual servo may be the root cause of the issues.
For my experience, I have found smaller servos to be more noisy.
Noisy power supplies can cause havoc with servos too. This can cause jitter of the servos, and the jittering can cause more noise (a vicious cycle).
It might be the Sketch code - but I'm not sure on how I would rectify it, if that were the case. I remember reading a article on letsmakerobots with oddbot & Fritsl ... Fritsl & Oddbot discovered that "shifting" the code around made a difference... From a software design perspective this is .. "horrible" :P ... But I'd be willing to experiment with anything which you think will make a difference.
I've used the Oscope on Arduino at the same time as running a servo and you can see some "noise" whenever the servo moves just on floating analog lines.
If it was me, I might try moving the electronics on a seperate power supply if possible.. you have to connect the grounds together, but going through a seperate power supply there is often filtering devices (including caps) which provide better isolation...
Let me know if I might help..
Hello, I have updated the
Hello,
I have updated the InMoov service page with the beginnings of a Head Tracking section .
I have a pan / tilt servo set up, and have tested the InMoov using the 2 servos and a Bare Bones Board Arduino clone (compatible with a Duemilanove Atmega 328). The Servos do not jitter, and I can control them with the sliders.
I did manage to move them fast enough with the sliders to black out my board. This is when the servos consume too much power and it resets the Arduino. When that happened I the servos were unresponsive and I had to detach and re-attach for control. The USB power source is usually clean but can offer very little current. Overall the servos were rock solid.
I DID get strange behavor when I used the Analog lines of the Arduino for a Oscope trace. This SHOULD NOT happen. Typically, I use the oscope trace as a system check. This verifies MRL can speak to the Arduino and the Arduino can talk back to MRL. The servos I had went crazy when I ran a trace.
Previously, tracing was done for 1/2 second in the SystemCheck ... I have now (version 938) taken that out. You should be able to update with the Bleeding edge button, and verify that the version (Help->About->Version) is 938 or above.
I'd recommend experimenting with the Head or some other part which has a low servo count, just to be safe.
Thought this might help...
Regards,
GroG
Great I have used the version
Great I have used the version 999 of MRL and have to say that the automatique repositionning of last set configuration of the GUI is very handy. Using the sliders on sides of the screen makes the gesture job easier and faster.
I'm trying on the moment different sound options for the microphone since I get trouble with the noise created by the servos for to get InMoov to understand the commands.
In your last post you mention you've updated the headtracking so I went straight for that, and I do get the start tracking working by voice command but after setting a point manually, the servos of the head are not reactif to moving objects. Though I checked if the servos had been correctly initialize and they were responding to the sliders.
With the batteries set as power source, there is no more eratic issues with the servos, so going further is now possible, even though I have a few of those cheap MG995 that burned lately. I will order some HK15298 hobbyking servos that should be a good exchange, they seem to have a good review.
You know you can make the
You know you can make the robot stop listening when your moving?
inMoov.pauseListening()
.... do movements - wait time .....
inMoov.resumeListening()
It won't get things wrong, but it won't try to interpret noises or speech either...
Tracking - correct - it's not attached yet.. I'm doing some clean-up.. still have some work to do..
I'm trying to put in some calibration routines. Pan tilt mechanisms are all very different.. Even if they are done with 2 servos - the axis & locations of how they are mounted together in relation to the camera causes very different behavior. My idea is to have it self-calibrate, where it uses feedback to adjust variables on how it will track objects..
Okay Grog, this is how InMoov
Okay Grog, this is how InMoov can see his hands with this gesture capture. I have blue silicone strips on the fingers, hopefully it is enough for to see his fingers. The strips aren't on the sides of fingers though...
inMoov = Runtime.createAndStart("
inMoov.captureGesture("I see my hands")
def I see my hands():
inMoov.moveHead(0,80)
inMoov.moveArm("left",72,84,
inMoov.moveArm("right",70,62,
inMoov.moveHand("left",50,26,
inMoov.moveHand("right",22,5,
Thanks for the picture. I'm
Thanks for the picture. I'm currently having some issues with LKOptical track filter - it won't track more than 1 point. But I think I'll have it figured out soon, then I'll be able to fully develop the Tracking service
(No subject)
Second view of InMoov's play
Second view of InMoov's play set with a toy removed
First Oscope with a finger
First Oscope with a finger sensor of foam.