Jump to content

1.1.2 Magic Smoke Industries Infernal Robotics 2.0.2


sirkut

Recommended Posts

Thank you for that info, I guess I missed that I apologize for troubling you with something so simple. I had a suspicion it might have been ATM but because it wasn't causing an issue with any other mods I didn't check it. Thank you again for the help I'll check into that solution and will also as suggested try to inspire the ATM devs to address the issue.

No apology necessary. That's what we are all here for. Besides you asked, instead of just jumping on the thread bashing a mod "because it doesn't work" like so many. It's all about how you say something not necessarily what you say.

Link to comment
Share on other sites

I know it's largely a KIS problem, but working with IR parts is still very kraken inducing. I don't know if maybe you guys can help KospY out?

A little background on that, while I was manipulating something with the rovers arm, I realized that I really could use a joint at the end of it, because you know, wrist. So, I made a pair of those joints with the workshop (which I removed because it kept giving me wierd freezes), but when I accidentially put the joint on upsidedown/backwards (though I don't think it actually matters), I tried surface attaching the joint onto the container so that I could fix it, then boom, kraken attack.

Javascript is disabled. View full album

Output log during my hijinks with it: http://sta.sh/0287au63cvx0

Link to comment
Share on other sites

Hello

I have some more question regarding the Version of this mod on GitHub.

All versions on GitHub have still 1.0.2 as KSP version defined (develop branch, master branch and tag v0.21.3). But the ZIP File for 0.21.3 has already 1.0.3 as KSP version.

In CKAN the IR mod is marked as 1.0.4 compatible.

So I guess the ZIP Version and CKAN Version is differently to the versions on GitHub (master- and devleop-branch and version tag). Maybe the ZIP and CKAN version has been build locally without pushing the latest changes to the repository. Could this be?

Could you please update the repository with the latest stable version, so I can rebuild the same version like the one from CKAN?

Thanks a lot

GrafZahl

You are right, all zips i tried, kerbalstuff, curse and github cant be loaded on KSP 1.0.4, still says it´s for KSP 1.0.3. I thought must be an error compiling or uploading file. i cant test CKAN one but should be great if someone fix it. thanks in advantange.

Link to comment
Share on other sites

Great work again peeps. I am 95% through completely rebuilding my timberwolf for 1.04... I have a much requested video that I need to get to...

I'm also finding the KOS integration really useful. It makes things so much neater that I have decided to completely rewrite my animator code (also taking advantage of other new KOS syntax).

Two things that I've mentioned before and would still love to see at some point:

1) The ability to set the positions of uncontrolled IR joints in the VAB and SPH. I'll refer back to this image, where I have two uncontrolled pivotrons that I want to line up in the SPH so that the docking ports align and form a brace between the leg and the hips, preventing the hip from buckling on launch.

2) Have the order of control groups and servos preserved when re-ordering them via click and drag. I have quite a few servos, and when I'm setting up a set of complex positions (like keyframed leg positions) I have to skip up and down the list looking for the servo I want to set. It would be nice to be able to just go down the list each time (particularly since the vertical size of the group/servo list is limited, which makes for quite a lot of scrolling back and forth).

I suppose it's also worth mentioning that while I haven't had any major issues with the connection strength between IR parts and other parts, the *internal* physics joint in the IR parts still seems pretty strength-limited. I've welded and edited my mech parts so that their masses are puny (0.05 or less) but this isn't a complete solution for a few reasons. First, when using IR parts you usually want a "stationary" bit and a "moving" bit, and to do this the stationary bit has to be much heavier so that it has more inertia. Also, if you incorporate mod parts (in this case BD Armory) you have to account for their masses as well. Sure, these could be edited too, and I can do that for my own installation, but then redistributing the mechs would be a pain, because they wouldn't work properly for other people unless they also edit their mod parts.

I can also imagine people trying to build something like a crane... we have cranes that can easily lift many tons in real life, but the IR joints just don't have the strength to move that much mass around without bouncing or coming apart at the joint (again, talking about the *internal* IR joint, not the joints with other parts).

Link to comment
Share on other sites

I found that if I copy a servo folder (eg the hinge pivotron flat folder) and edit the part config so that freeMoving = true and servoName = <Uncontrolled>, then I can get the desired behaviour: the part position can be set in the hangar, but it moves freely once launched.

However, it's a bit odd as to how it works. The servo itself does not show up in any group in the IR window in the SPH, but its position can be set by clicking the arrows to move the whole (default) group, even though the group itself is empty.

Also, all such mechanisms will be put into this group and will all move together when the group arrows are used, so it's not really a workaround unless you only need one!

Edited by allmhuran
Link to comment
Share on other sites

A query about KOS integration (also crossposted to the KOS reddit)...

It appears that an IRServo object is not of type PartModule.

Is there any relationship between an IRServo object and a base KOS object like part or module?

I see on the KOS documentation website that most of the "get servo" style commands only work for current focused vessel. Can I start with a KOS Vessel and somehow return its IRServo objects?

Similarly, can I find a part by its tag and somehow get the associated IRServo?

Link to comment
Share on other sites

A query about KOS integration (also crossposted to the KOS reddit)...

It appears that an IRServo object is not of type PartModule.

Is there any relationship between an IRServo object and a base KOS object like part or module?

I see on the KOS documentation website that most of the "get servo" style commands only work for current focused vessel. Can I start with a KOS Vessel and somehow return its IRServo objects?

Similarly, can I find a part by its tag and somehow get the associated IRServo?

It's not possible due to the way IR works, so its not a kOS limitation. Currently IR parses the active vessel for IR parts and although there may be more IR vessels in physics range, only the active vessel is parsed that is why the IR API can only be used for current vessel.

As for linking PartModule to IRServo - what is your intended use for it? All useful features of IR partmodule are much easier to access via IRServo.

Edited by Ziw
Link to comment
Share on other sites

It's not possible due to the way IR works, so its not a kOS limitation. Currently IR parses the active vessel for IR parts and although there may be more IR vessels in physics range, only the active vessel is parsed that is why the IR API can only be used for current vessel.

D:

So... Does this mean that...

a) If I want to have multiple robotic craft all working together, I can do it but I'd have to swap focus from vessel to vessel to initiate the KOS script on each one?

B) OR does it mean that as soon as you switch away from a vessel, any IR parts on that vessel stop functioning?

c) OR does it mean that as soon as you switch away from a vessel whose parts are being controlled by a KOS script, then those parts on that vessel will stop functioning?

As for linking PartModule to IRServo - what is your intended use for it? All useful features of IR partmodule are much easier to access via IRServo.

Yeah, there's no doubt the best way to make use of IR functionality is through an IRServo, the new addon functions are really useful.

However, working with the IRServos themselves (finding a particular servo, using them in combination with other KOS functions, etc) is made more difficult since they're not related (via composition or inheritance) with other KOS structures. Finding the servos on a ship, for example, can't be done using the "partsTagged" function. Finding the orientation of the base part and using the associated IRServo to respond based on that information becomes less elegant, and so on.

None of this is completely functionality-limiting. For example, I could create my own function for finding a servo by name...


declare function GetServoByName {
parameter pName.
for s in addons:IR:allsservos {
if s:name = pName { return s. }
}
}

But I'd have to be careful not to use two servos with the same name. Or I could do something similar to find the servo by the part tag by matching the KOS part UID attribute with the servo part UID attribute (edit, just tried this, doesn't work... the servo UID is different from the actual part UID).

And then I could create my own "class" which composes both the IRServo object and the base KOS part object... except it would just be a list, so not very elegant (and would also take up more instructions to use due to array access), eg:


declare THE_SERVO_BIT is 0.
declare THE_PART_BIT is 1.
declare MyComposedClassInstance is list().
MyComposedClassInstance:add(GetServoByName("myservoname")).
MyComposedClassInstance:add(ship:partstagged("myparttag")[0]).

// ...

if MyComposedClassInstance[THE_PART_BIT]:whatever = blahblah { MyComposedClass[THE_SERVO_BIT]:MoveTo(whatever, etc). }

It's all just "quality of life" stuff as they say, but it's the kind of thing that makes things far easier to both write and, ultimately, read. I sit here wanting to go and rewrite all my animator stuff, but having to do all this sort of "prep work" up front means I feel like I'm not working on my project, but rather I'm sort of fighting the APIs, and that's not what I'm really motivated to be doing, so I don't bother starting."Ehhh stuff it I'ma go watch youtube instead :D".

- - - Updated - - -

And of course, it seems like if I want to control an unfocused craft's IR parts I can still do it using normal old KOS parts and modules... but as you said, the IR API is much nicer to use, it's a shame it can't be used remotely.

Edited by allmhuran
Link to comment
Share on other sites

D:

So... Does this mean that...

a) If I want to have multiple robotic craft all working together, I can do it but I'd have to swap focus from vessel to vessel to initiate the KOS script on each one?

B) OR does it mean that as soon as you switch away from a vessel, any IR parts on that vessel stop functioning?

c) OR does it mean that as soon as you switch away from a vessel whose parts are being controlled by a KOS script, then those parts on that vessel will stop functioning?

Hmm, I did not test but option c) is my best guess. After you switch vessels kOS loses references to previous vessel's IRServos so you cannot control it anymore.

Yeah, there's no doubt the best way to make use of IR functionality is through an IRServo, the new addon functions are really useful.

However, working with the IRServos themselves (finding a particular servo, using them in combination with other KOS functions, etc) is made more difficult since they're not related (via composition or inheritance) with other KOS structures. Finding the servos on a ship, for example, can't be done using the "partsTagged" function. Finding the orientation of the base part and using the associated IRServo to respond based on that information becomes less elegant, and so on.

None of this is completely functionality-limiting. For example, I could create my own function for finding a servo by name...


declare function GetServoByName {
parameter pName.
for s in addons:IR:allsservos {
if s:name = pName { return s. }
}
}

But I'd have to be careful not to use two servos with the same name. Or I could do something similar to find the servo by the part tag by matching the KOS part UID attribute with the servo part UID attribute (edit, just tried this, doesn't work... the servo UID is different from the actual part UID).

And then I could create my own "class" which composes both the IRServo object and the base KOS part object... except it would just be a list, so not very elegant (and would also take up more instructions to use due to array access), eg:


declare THE_SERVO_BIT is 0.
declare THE_PART_BIT is 1.
declare MyComposedClassInstance is list().
MyComposedClassInstance:add(GetServoByName("myservoname")).
MyComposedClassInstance:add(ship:partstagged("myparttag")[0]).

// ...

if MyComposedClassInstance[THE_PART_BIT]:whatever = blahblah { MyComposedClass[THE_SERVO_BIT]:MoveTo(whatever, etc). }

It's all just "quality of life" stuff as they say, but it's the kind of thing that makes things far easier to both write and, ultimately, read. I sit here wanting to go and rewrite all my animator stuff, but having to do all this sort of "prep work" up front means I feel like I'm not working on my project, but rather I'm sort of fighting the APIs, and that's not what I'm really motivated to be doing, so I don't bother starting."Ehhh stuff it I'ma go watch youtube instead :D".

- - - Updated - - -

And of course, it seems like if I want to control an unfocused craft's IR parts I can still do it using normal old KOS parts and modules... but as you said, the IR API is much nicer to use, it's a shame it can't be used remotely.

You have some valid points there. I'll try to add a link to PartModule from IRServo in kOS integration, should not be very hard, though kOS has a long release cycle so I don't know when you'll be able to test it.

Link to comment
Share on other sites

Roger that, in the meantime I want to build a script that will work for the current vessel as well as on other unfocused vessels, so I will still be using the old methods for that, and I might do a second one using the updated integration for a high efficiency, easier-for-others-to-understand version as well.

Link to comment
Share on other sites

I have an issue where max and min don't seem to be limiting a rotatron properly.

Some background to understand the context. My animator works by taking in a set of keyframed positions for a servo. It moves all the servos from keyframe to keyframe in the same amount of time by calculating the speed the servo needs to move based on the current position, the target position, and the time between keyframes.

Since I can't use the new IRAddon functionality, the way I stop the motion is by setting the "max" and "min". If the new position is less than the current, I set min to the new position. If it is larger than current, I set max to the new position. Just to be safe, I also set min to current - 10, and max to current + 10 before doing this, so the servo has some "wiggle room".

So, what you'll see in a moment is the leg moving between three positions, the starting position followed by two keyframes. The servo to focus on in this case is the hip rotator. The keyframe time is one second.

It starts at 0.

It should then move from 0 to 30 in 1 second. Max will be set to 30 (plus a small margin of error) so that it stops there. This works.

After that, and using exactly the same function, it should move from 30 to 20 in 1 second. Min will be set to 20 so that it stops there. But for the second motion it doesn't stop at 20... it keeps going all the way to -10, even though min is properly set. I also have no idea why it stops at -10, since there is no command in the script that should cause it to stop other than by the part hitting the limit.

The only thing I can think of is that it doesn't like having a minimum which is above zero, and that it is therefore using the previous min of -10, even though the GUI shows the correct limit (20) as does querying the part module via KOS.

Zoomed in video: https://youtu.be/fKILvShsxOQ

- - - Updated - - -

OK, if I put in a wait of 0.001 between the line of code that sets the min, and the following line which starts the motion, the servo correctly stops at the new minimum. Since, if there is no wait, the two events happen simultaneously as far as the physics ticks are concerned, it seems like IR doesn't honour limits that are set "while the part is moving".

ie, this doesn't work:


if (pNewPos > s:GetField("rotation")) {
s:setfield("max", pNewPos + 0.05).
s:doaction("move +", true).
} else if (pNewPos < s:GetField("rotation")) {
s:setfield("min", pNewPos - 0.05).
s:doaction("move -", true).
}

But this does:


if (pNewPos > s:GetField("rotation")) {
s:setfield("max", pNewPos + 0.05).
wait 0.001. // added wait to force a physics tick
s:doaction("move +", true).
} else if (pNewPos < s:GetField("rotation")) {
s:setfield("min", pNewPos - 0.05).
wait 0.001. // added wait to force a physics tick
s:doaction("move -", true).
}

Is this intentional?

Edited by allmhuran
Link to comment
Share on other sites

...snip...

Zoomed in video: https://youtu.be/fKILvShsxOQ

- - - Updated - - -

OK, if I put in a wait of 0.001 between the line of code that sets the min, and the following line which starts the motion, the servo correctly stops at the new minimum. Since, if there is no wait, the two events happen simultaneously as far as the physics ticks are concerned, it seems like IR doesn't honour limits that are set "while the part is moving".

ie, this doesn't work:


if (pNewPos > s:GetField("rotation")) {
s:setfield("max", pNewPos + 0.05).
s:doaction("move +", true).
} else if (pNewPos < s:GetField("rotation")) {
s:setfield("min", pNewPos - 0.05).
s:doaction("move -", true).
}

But this does:


if (pNewPos > s:GetField("rotation")) {
s:setfield("max", pNewPos + 0.05).
wait 0.001. // added wait to force a physics tick
s:doaction("move +", true).
} else if (pNewPos < s:GetField("rotation")) {
s:setfield("min", pNewPos - 0.05).
wait 0.001. // added wait to force a physics tick
s:doaction("move -", true).
}

Is this intentional?

There is no way around it as all the limit checks and movements are done in FixedUpdate, so it does need to wait until physics tick.

Link to comment
Share on other sites

Gotcha, thanks for the confirmation. Not a huge problem, just means a bit of refactoring to set all limits in one tick and start all movements in the next instead of doing both at once servo by servo, otherwise the servos would slowly get out of synch.

Link to comment
Share on other sites

A followup on that question. I've tried determining the answer to this by experimentation but I am getting inconsistent results, probably because there is an uncontrolled variable which is hard to account for - namely, the fact that KOS is limited to 200 compiled instructions per tick, so I can't be sure when a tick might be occurring even if I don't have an explicit wait (likely to happen quite often because I have a lot of servos working together).

The context is the same as before, but for simplicity assume that I have just the one servo, which moves through a series of positions.

Sometimes the position after the current target will be in the same direction as the target. For example, if moving from 0 to 20 and then to 40, I don't need to stop and reverse the servo at 20. Other times I will want to reverse the servo.

I may, however, want to change the speed even if I want to keep moving in the same direction.

The question is: when I want to keep moving in the same direction as smoothly as possible...

* do I just change the speed when it reaches the first target and force a physics tick? Or,

* do I change the speed, issue another ("move +", true), and force a physics tick? Or,

* do I need to issue a (move +, false), wait one tick, change the speed, wait one tick, and issue a (move +, true)? Or...

* etc.

It doesn't make much of a difference for one servo, maximum of about 1/8th of a second of pause time, which wouldn't be very noticable.

But when I have over a dozen servos working together I will hit the KOS IPU limit several times as I cycle through, which might mean that processing all of the servos takes many physics ticks, so fewer instructions and fewer forced ticks are definitely important.

Link to comment
Share on other sites

OK, I'm pretty confused now :D

First I tried this with a single servo:


set speed 1
move +
wait 1
set speed 2
move +
wait 1 // servo correctly moves at the new speed during this time. If I use a speed with the opposite sign then the servo reverses as desired.

So it looks to me like the new speed is picked up when a "move" command is processed, even though there's no wait between the "set speed 2" and the following "move +". But that might just be to do with the order that the commands happened to be processed.

In any case, what I believe we can say for sure is that the following should always work without having to rely on chance:


set speed 2
move + true
wait 1
set speed -2
// move + false
wait 0.001. // force a physics tick
move + true
wait 1

Right. Now, the test also demonstrates that I can always issue a "move +" command, I don't need to issue a "move -". If my speed is set to negative, the servo will move in the other direction. Cool.

Now, my mech's knee has two servos that work together to help keep the thing from flexing and bouncing too much. They are mounted opposite each other and face in opposite directions as a result. Therefore when they move the speed of one will be equal to and opposite of the other.

Here's the part I don't get. If I use the above logic then something goes wrong with the knee servos... one moves correctly, but the other one gets its direction confused - but only sometimes! doesn't pick up the new limit. But If uncomment the stop command ("move + false") then everything works.

Why would stopping the servo cause this particular servo to move in the correct direction pick up its new limit properly? Why would not stopping it (ie, simply setting a new speed and issuing a new move + command) cause it to move in the opposite direction fail to pick up the new limit? Nothing else is being changed.

I'm having trouble trying to put together a "simplest possible case", because I can't figure out where the problem is. Here's some possibly useful info.

Neither servo has its axis reversed.

I can reproduce the problem even if I'm only running these two servos, so I don't think it's due to "servo overload".

When running the animation, if I print the speed of the two knee servos then the correct speed (including sign) and limits are printed *both* when I include the stop and when I do not.

I always issue a "move +", never a "move -"

The coordinates are as follows:

For one knee servo: +030, +005, -026 (this one messes up when being told to move from 5 to -26 IF there is no stop issued).

For the other: -030, -005, +026 (this one moves properly)

Update: OK, seems like it might be something to do with min and max again, but I'm still trying to figure out why one servo behaves and the other does not.

Update2: Yep, the left servo is not picking up the new minimum on the third movement. It's still sitting at position +5 after the command to move, which is the previous minimum. So it's not a direction problem, it's a limit problem. But the servo does print the correct new limit of -26 right before the new "move +" is issued, and this is at least one physics tick after the minimum was set (a wait is always issued after the speed and limits are set and before the next move).

Proof that the wait is always issued, here's the actual animation loop. If the "StopAll(lleg)" is uncommented, then the parts behave properly. If it is commented out, they do not:



// simply issues a move + false to all servos in the list
function StopAll {
parameter pServoList.
for s in pServoList {
s[ISERVO]:doaction("move +", 0).
}
}

// lleg is a list() containing the two servos


declare w is 0.01. // wait time to force physics tick
declare df is 0.8. // frame time
StopAll(lleg). // issue a move + false on all servos prior to startup

declare fi is 0.
until (fi > 2) {
print fi.
PrepareFrame(fi, df, lleg). // this function sets all speeds and limits on all servos in the list
//StopAll(lleg).
wait w. // forces physics tick
RunFrame(lleg). // this function just issues a move command to all servos in the list
wait df * 2. // let the servos do their thing. Waiting double the frame time so I can easily see the stop positions.
set fi to fi + 1.
}
StopAll(lleg).

Edited by allmhuran
Link to comment
Share on other sites

First try increasing the IPU to something like 1000

It should not bother your CPU too much actually.

Your inconsistencies might be caused by instructions processed in different physics ticks.

Also do problems with max/min setting appear only for inverted servos?

Link to comment
Share on other sites

Nope, nothing is inverted. I think I have something figured out at least. Here's the simplest case I can come up with.

If I progress 0 > -20 > -40, then the move to -40 fails:


set s to ship:partstagged("subject")[0]:getmodule("MuMechToggle").

// move 0 to -20

s:setfield("min", -20). // set target
s:setfield("max", 20). // arbitrary since we're going negative
s:setfield("speed", -2). // negative speed.
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -20, is " + s:getfield("rotation"). // position is -20. Correct!

// move -20 to -40

s:setfield("min", -40). // keep going
s:setfield("max", 30). // still arbitrary
s:setfield("speed", -3). // still negative speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -40, is " + s:getfield("rotation"). // position is -20. Servo did not move!

But if I change the second movement direction so that the progression is 0 > -20 > +20 (which means there's an implicit stop and reverse) but use the exact same logic otherwise, it works!


set s to ship:partstagged("subject")[0]:getmodule("MuMechToggle").

// move 0 to -20

s:setfield("min", -20). // set target
s:setfield("max", 20). // arbitrary since we're going negative
s:setfield("speed", -2). // negative speed.
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -20, is " + s:getfield("rotation"). // position is -20. Correct!

// move -20 to 20

s:setfield("min", -40). // arbitrary since we're going positive
s:setfield("max", 20). // reversing
s:setfield("speed", 2). // positive speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be 20, is " + s:getfield("rotation"). // position is +20. Correct!

Now, here's where it gets really interesting. If I go positive twice (0 > 20 > 40) then that also works!


set s to ship:partstagged("subject")[0]:getmodule("MuMechToggle").

// move 0 to 20

s:setfield("max", 20). // set positive target
s:setfield("min", -20). // arbitrary since we're going positive
s:setfield("speed", 2). // positive speed.
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be 20, is " + s:getfield("rotation"). // position is 20. Correct!

// move 20 to 40

s:setfield("max", 40). // keep going
s:setfield("min", -30). // still arbitrary
s:setfield("speed", 3). // still positive speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be 40, is " + s:getfield("rotation"). // position is 40. Correct!

So the problem only seems to arise with the minimum limit, the maximum limit is fine, which is why on my mech one of the servos worked no matter what while the other didn't.

And finally, if I add a stop to the first script when going 0 > -20 > -40, then that also works even without an additional forced tick


set s to ship:partstagged("subject")[0]:getmodule("MuMechToggle").

// move 0 to -20

s:setfield("min", -20). // set target
s:setfield("max", 20). // arbitrary since we're going negative
s:setfield("speed", -2). // negative speed.
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -20, is " + s:getfield("rotation"). // position is -20. Correct!

s:doaction("move +", false). /////////// ADDED A STOP HERE /////////////

// move -20 to -40

s:setfield("min", -40). // keep going
s:setfield("max", 30). // still arbitrary
s:setfield("speed", -3). // still negative speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -40, is " + s:getfield("rotation"). // position is -40. CORRECT!!

- - - Updated - - -

And I just realized there was one more test to try. What if I am reversing direction from positive to negative, so that on the second movement we are using the minimum limit, but I don't first issue a stop? Well, that works too!


set s to ship:partstagged("subject")[0]:getmodule("MuMechToggle").

// move 0 to 20

s:setfield("min", -50). // arbitrary since we're going positive
s:setfield("max", 20). // target
s:setfield("speed", 2). // positive speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be 20, is " + s:getfield("rotation"). // position is 20, correct!

// reverse to -20

s:setfield("min", -20). // new target
s:setfield("max", 30). // arbitrary since we're going to reverse
s:setfield("speed", -2). // negative speed
wait 0.01. // tick
s:doaction("move +", true).
wait 2.

print "position should be -20, is " + s:getfield("rotation"). // position is -20. CORRECT!!

So the weird behaviour only happens under these conditions:

1) Move in a negative direction to some limit

2) Issue a new minimum limit which is lower than the first

3) Issue a new move order to continue in the same direction to the new limit without first issuing a stop order.

- - - Updated - - -

Also, notice that I'm always issuing "move +" commands and using the the sign of the speed to set direction. From a coding point of view this is much easier (and with fewer instructions) than always issuing a positive speed order and swapping between "move +" and "move -" commands, because:

1) The new speed is always (next_position - current_position) / desired_time / config_speed). There's no need to abs() it.

2) The move command requires no conditional test to check which type of move order to issue.

3) Since (to limit required ticks) we are setting all servo values in one pass and then moving all servos on the next, we'd need to also store the movement direction desired for each servo on the setup pass, and then look up that information for each servo on the movement pass, because the speed on the first pass would always be set to positive. There's nothing inherently telling us which way we need to go. If I just use the sign of the speed to set direction I don't need to worry about that, I just issue "move +".

This is why I haven't checked whether the weird behaviour also happens if I issue consecutive "move -" commands.

Edited by allmhuran
Link to comment
Share on other sites

Something that would help a lot would be to know exactly when certain fields take effect. Specifically, should min, max and speed changes only take effect after a move command has been issued (even if we're already moving)? Or should they take effect immediately on the next physics tick even if no new move command is issued? Is a stop meant to be necessary, or should things take effect even without a stop? Can move operations "stack" (could multiple "move + true" calls ever for some reason require multiple "move + false" calls to unwind) or should calling the same move command repeatedly have no effect? I know that servos sometimes get "stuck on" - I believe this happened if you issued a move + followed by a move - with no stop in between, the two commands would sort of fight and then any subsequent commands would be unpredictable... although this was in the previous version and I can't remember if that was the exact way to make it happen.

I've been trying to figure all of this out by experimentation but, again, depending on how I set up the code I get inconsistent results and it's really hard to determine why. Several methods which are logically identical but just make use of different code (order of logically-order-independent operations, whether using "move -" or negative speeds, whether I set the animation frame time to 0.6 or 0.65 seconds (??), etc) get different results, which is kinda driving me nutty :P

Edited by allmhuran
Link to comment
Share on other sites

Something that would help a lot would be to know exactly when certain fields take effect. Specifically, should min, max and speed changes only take effect after a move command has been issued? Or should they take effect immediately on the next physics tick even if no new move command is issued? Is a stop meant to be necessary, or should things take effect even without a stop? Can move operations "stack" (could multiple "move + true" calls ever for some reason require multiple "move + false" calls to unwind) or should calling the same move command repeatedly have no effect? I know that servos sometimes get "stuck on" - I believe this happened if you issued a move + followed by a move - with no stop in between, the two commands would sort of fight and then any subsequent commands would be unpredictable... although this was in the previous version and I can't remember if that was the exact way to make it happen.

I've been trying to figure all of this out by experimentation but, again, depending on how I set up the code I get inconsistent results and it's really hard to determine why. Several methods which are logically identical but just make use of different code (order of logically-order-independent operations, whether using "move -" or negative speeds, whether I set the animation frame time to 0.6 or 0.65 seconds (??), etc) get different results, which is kinda driving me nutty :P

Changes in speed should only be applied after a new Move command. Limits are checked every FixedUpdate (physics tick) and movement (Transform) is done there as well.

Using negative speed is a bit hacky, as it is not the intended behaviour, as you are not supposed to be able to set it below zero in the UI and you are using UI based functions (if you are not using the API). By the way, did you set the acceleration high enough that it would not affect your keyframe animation?

Is the inability to control unfocused crafts is the only thing that keeps you from using the API?

Link to comment
Share on other sites

Changes in speed should only be applied after a new Move command. Limits are checked every FixedUpdate (physics tick) and movement (Transform) is done there as well.

OK, that is super useful to know! Thanks! It does create a bit of an issue because with KOS scripts I can't say for certain when a tick will occur, which means I can't say for certain when my new limits will be enforced. Given I'm iterating over 8 servos per leg there's quite a lot going on, and if I want them to all operate in unison then not knowing exactly when things happen makes things difficult. I would suggest having both limits and speeds only take effect when a move command is issued to reduce the uncertainty (the uncertainty would be reduced down to a single pass over the servos where all move commands are issued and nothing else, which can probably be done in one tick or at most two), but if I was in your position I would not want to be working on the legacy implementation anymore ;)

Using negative speed is a bit hacky, as it is not the intended behaviour

Dawww, but it's so much BETTERERER :D

By the way, did you set the acceleration high enough that it would not affect your keyframe animation?

Yep, acceleration is set to 20 which seems to be fine. In any case, it wouldn't really matter, the servos simply wouldn't quite reach the desired target position. The behaviour I've been seeing is rather different from that... some servos don't move, some end up in the wrong position entirely, etc, depending on how I lay out the code. Also, I could even factor in the acceleration value when calculating the necessary speed... a bit of math but nothing too fancy... but I'd need to know the units of the acceleration setting to do this.

Is the inability to control unfocused crafts is the only thing that keeps you from using the API?

Yes indeed, that is the one and only reason. In wake of my 0.90 Mechwarrior video I was asked (dozens of times) to recreate the Mechwarrior 2 intro. A couple of scenes require that I control at least two and preferably three mechs at a time. So while I could write the code using the new API just for the timberwolf itself and I bet it would work fantastically, I wouldn't be able to make the video I was rebuilding it for :P

- - - Updated - - -

In case text isn't as clear as code (and it isn't!), here's the challenge in pseudocode form.

Assume that for any single servo we want to do this:


calculate/lookup and set new limits // a/T
calculate and set new speed // b/T
determine direction // c/T
move // d/T
wait until desired time
loop

Now suppose that each step in the sequence takes some fraction of a physics tick as shown in the comment.

Given the information you've just provided, the best order of doing this would be as follows.


calculate and set next speed
determine next direction
Wait until desired time
calculate/lookup and set set new limits
apply direction
move
loop.

But if we're working with multiple servos at once, then the trivial solution:


for each servo
calculate and set new speed
determine and save direction

wait until desired time

for each servo
caculate/lookup and set new limits
apply direction
move

loop

Is not good, because for N servos, the N'th servo will quite a long way out of synch with the first... N(b/T + c/T + d/T) in the worst case.

This would be much better:


for each servo
calculate and set new speed and direction // use sign of speed to set direction
calculate/lookup and set new limits

wait until desired time

for each servo
move

loop

Now the N'th servo is only N(d/T) ticks out of synch with the first for that frame, and not out of synch at all if N(d/T) < T.

Edited by allmhuran
Link to comment
Share on other sites

That is a truly excellent idea, I hadn't come across that feature yet. I can also refactor things to decrease the frame change loop to the minimum possible set of operations, it just gets a bit more verbose/inelegant ;)

Link to comment
Share on other sites

Writing some more code here and I realized we seem to have a contradiction implied at the moment, so I'd like to see if we can clear that up.

In a previous post I mentioned that if I set a limit and issued a move within the same physics tick, the limit did not seem to be applied to the motion, which you seemed to confirm. But more recently you mentioned that "Limits are checked every FixedUpdate (physics tick) and movement (Transform) is done there as well".

I'm having a bit of trouble reconciling those two. If limits are checked every physics tick, then doesn't that imply that if I set a limit and start a movement in a single tick, the limit should be applied to that movement in the very next physics tick? But if that's true then I don't understand the result shown in that previous video, where the limit is not applied even though many dozens of ticks are going to go by during the course of the motion.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...