I have built a simulation of real space rovers. My current implementation for the first rover looks like this

// a 6 wheel-rover
public class Rover1 {
    private Joint joint1; // discrete rotation -- for steering
    private Joint joint2; // continous rotation -- for wheel
    private Joint joint3; // discrete rotation -- for steering
    private Joint joint4; // continous rotation -- for wheel
    private Joint joint5; // discrete rotation -- for steering
    private Joint joint6; // continous rotation -- for wheel
    private Joint joint7; // discrete rotation -- for steering
    private Joint joint8; // continous rotation -- for wheel
    private Joint joint9; // discrete rotation -- for steering
    private Joint joint10; // continous rotation -- for wheel
    private Joint joint11; // discrete rotation -- for steering
    private Joint joint12; // continous rotation -- for wheel

    private RotateJoint(Joint joint, float angle)
    {
        //...something
    }

    private DriveJoint(Joint joint, float speed)
    {
        //...something
    }

    private StopAll()
    {
        //...something
    }

    private DoUniqueThingThatTheOtherROverCantDo()
    {
        //...something only for this class
    }
}

However, the other robot implementation looks like the following

    // a 4 wheel-rover
    public class Rover2 {
        private Joint joint1; // discrete rotation -- for steering
        private Joint joint2; // discrete rotation -- for steering
        private Joint joint3; // discrete rotation -- for steering
        private Joint joint4; // discrete rotation -- for steering
        private Joint joint5; // continous rotation -- for wheel
        private Joint joint6; // continous rotation -- for wheel
        private Joint joint7; // continous rotation -- for wheel
        private Joint joint8; // continous rotation -- for wheel

        private RotateJoint(Joint joint, float angle)
        {
            //...something
        }

        private DriveJoint(Joint joint, float speed)
        {
            //...something
        }

        private StopAll()
        {
            //...something
        }

        private DoSomethingThatTheOtherRoverCantDo()
        {
            //...something only for this rover
        }
    }

At the moment my implementation is not abstract enough. I wonder if there is a way to make my implementation of both rover abstract enough so that when I simulate the next rover, the implementation would be easy. I am considering of using interface (composition rather than inheritance) for specifying DriveJoint(), and StopAll(), and DoSomethingThatTheOtherRoverCantDo(). But how do I deal with differing number of Joint id (engineers decides the id number, not me) for differing rotational purpose? Could someone give a suggestion?

Thanks-ikel

Dani AI

Generated

Model joints as first-class, data-driven components and keep the rover as a composition of those components rather than a bunch of numbered fields. Each joint should carry metadata (id, name, role, axis, limits, invert/gear info) and expose capability interfaces only if it supports them. This separates the engineers' numeric id assignments from the control logic and makes adding a new rover a matter of configuration plus optional small feature modules. (Builds on 's collection idea and 's thought about moving rotation behavior into Joint.)

Define small capability interfaces and a simple aggregation API so callers can operate by id, by role/group, or by predicate. Use a factory or config loader to build the physical-to-logical mapping from JSON/XML so no ids are hard-coded. Keep per-rover special behavior as plugins (ISpecialFeature) attached to the Rover instance rather than as methods on a base class.

public enum JointRole { Steer, Wheel, Actuator }

public interface IJoint { int Id { get; } string Name { get; } JointRole Role { get; } }
public interface ISteerable { void RotateTo(float degrees); }
public interface IDriveable { void SetSpeed(float metersPerSecond); }

// Rover contains IDictionary<int,IJoint> and IDictionary<JointRole,List<IJoint>> for group ops

Example config-driven mapping (JSON snippet):

[
  { "id": 2, "name": "frontLeftSteer", "role": "Steer",  "minDeg": -45, "maxDeg": 45 },
  { "id": 5, "name": "frontLeftWheel",  "role": "Wheel",  "gearRatio": 10.5 }
]

Practical notes: abstract the hardware access behind an IHardwareController so simulation and real hardware share the same logic; use a command/telemetry pipeline for asynchronous moves and safety checks; keep units and limits in config and validate at load time. With this pattern, adding a new rover typically means a new mapping file and, at most, a small feature/plugin class instead of rewriting core logic.

Recommended Answers

All 4 Replies

Whenever I need variables like x1,x2,x3,x4...... I think of a collection like List or Array

Hi ddanbe,

Why didn't I think of that. It simplifies many things! Thank you. I knew it would be simple.

-ikel

I don't see an implementation of your Joint class. But to me it seems it could contain a continuous and discrete rotation. If I'm correct, this could simplify things further I guess.

ddanbe,

At the moment, Joint only contain id number and other things but not rotation behaviour. You're right there, I could simplify things further having an interface that define rotation in Joint including axis of rotation. Hmm... back to Enterprise Architect...

Thank you for your feedback!

-ikel

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.