Automatic Train Control – what should it look like?

Hi everyone,

One of the big future features for Traintastic will be automatic train control. Right now, trains are fully manual, but eventually we want the software to handle things like routing, stopping, and scheduling.

There are a lot of possible directions this could go, and I’d love to hear what you would like to see:

  • Should trains follow a timetable (arrivals and departures at stations)?
  • Would you prefer block-based control (train detection sections, stop when occupied)?
  • Or something simpler, like automatic route following with just start and destination?
  • How important are features like priority routing, random delays, or train categories?

What does “automatic control” mean for your layout?

  • Do you imagine it running in the background while you drive some trains manually?
  • Or would you like the computer to run a full operating session on its own?

Please share your ideas, even just a wish list. This will help shape the design and ensure we build something that works for different operating styles.

That how i would recomand to design it:

each contact where the train stopps should have the train name or image over it when occupied. When leaving the contact then it should disapear from it and change the looks of the track where it is driving on to the next contact as example arrows pointing in the direction. when ariving on the next contact the track design should be reset to default and the train should apear again on the right contact.

Each contact or train should have the options like: stop or ignore, stop delay, waittime and posibly execution of some train functions.

From some expiriance on some Boards it would be recomended to use multiple contacts for releability like: first one to start breaking. second one is the standart one for checking it something is there. third one for emergency breaking.

A timetable would be cool it could be automaticly translated to the delays on each contact or train. But also allowing specific trains to only target specific location in automatic mode would be nice.

I thing the perfect method would be the mix of everyting together. When nothing is configured for a specific train then they sould just run automaticly on the block-based control but it should be then be possible to setup some routes or time tables on some trains as exampel:

  • Loco 1 go to Station1 and wait 20sec
  • Loco 1 go to Station3 and wait 10sec
  • Loco 1 go to Station11 and wait 30sec

or

  • Loco 3 be from 13:30 to 13:35 on station 1
  • Loco 3 be from 13:55 to 14:10 on station 5
  • Loco 3 be from 15:00 to 15:15 on station 5

I’m really impressed with what I’ve seen so far with this application. :clap: Here are just some starting ideas for automation. I have lots of ideas as I have used many different automation software.

Automation Development

I believe once the backbone of the application has been well tested for manual operation of train running, then automation should be the next top priority. It’s nice to have trains automatically running on the layout while an operator does some manual switching and when the operator happens to need a block in a route, knows the system will automatically stop the route until that block becomes clear.

This development should be done in stages – from basic to complex. Here is what could potentially be the first stages.

I agree with the comments/suggestions added by kamil00110.

Routes

First, need the ability to create and save routes. Initially, this would just entail selecting starting and ending blocks and the appropriate blocks between these two. These routes would act on any train type that is currently in the ‘Start’ block. Eventually, these could be scheduled, started by buttons on the Board and designated for only specific types of trains (ie, passenger, freight, etc)

Basic Automation

Start a Route by any number of options:

  • Manually – select the route and click Start
  • Via button on Board
  • Programmatically - Via another Route, sensor condition, block condition, script program
  • Scheduled – starts at a designated time.

What occurs when route is started:

If a train is in the starting block, then system attempts to reserve the blocks in the route and if say, the first two are available, start the train and proceed until it reaches the last available block. Here it stops and waits while it continually attempts to reserve the next couple of blocks (ie. Other trains have cleared those blocks). This dance continues until it reaches the final block where it stops based on the block sensors, or distance into the block. If the blocks selected have different paths to the same ending block, the system will decide which is the best path depending on different rules (blocks already in use, blocks designated for specific directions, blocks designated for different train types, priorities)

Some potential Next Steps for Route Development

Once, this basic automation is working, then start to add in the other complexities such as:

Add route specifics to a Route dialog box

  • Add conditions to for which train will run the route.
  • Add ability to do something when the route ends (ie. Start another route)
  • Run only for specific trains or group of trains. (would require the ability to create type of trains as a group and add the trains to the them) This would ensure that if a freight train was in starting block of a route it wouldn’t run if it was designed only for passenger trains.

Create different route types

  • Basic Route - (Default) Run once
  • Out and Back - start same route in reverse direction and complete x many time
  • Loop Route - Allow a route to be run x many times (start and end blocks are the same)

Each block in that route has specific block properties

  • When entering/exiting that block route then do something (ie. blow horn, play a sound, start a script, start another route)

Add ability have multiple Start and End blocks. This would allow automation of a point to point operation from one yard to another yard and the system would randomly select which train to start (if they all have the correct type of train in them) and randomly pick from any empty tracks at the end to finish in.

@Randy welcome to the forum! :slight_smile:

@kamil00110 and @Randy big thanks for the input this is really helpful! There is still a lot to do, so it may take a while. Routes and automatic driving are on the roadmap for the 0.5 version, for 0.4 I want to finish some hardware stuff.

Then most of the foundation is there to support routes and automation.

Under the hood Traintastic already has paths, this is a connection from one block to another, it is direction aware due to one-way restriction. It is possible that there are multiple paths in the same direction between blocks due to turnouts. (There is not path weighting yet.)

For the multiple blocks option I like to utilize the zone logic. Currently the zone has a few properties, but I thinking about giving it a type, so it be a station or staging yard etc. That would be nice to use within routes.

For train/route/block types, I’m planning to use a more generic approach, instead of a fixed default set I want to introduce labels/tags, then users can create the ones they need, e.g. label trains by category, company, country or epoche and even mix them.

Feedback is welcome :slight_smile:

p.s. @Randy which apps have you used?

@reinder I have used JMRI, iTrain and TrainController recently. There were some others I investigated about 5 years ago, but don’t remember what they were called.

Currently, I use TrainController v9 Silver for my automation and have been very happy with it, except for the fact that it is VERY expensive. They released a version 10 about a year ago, but now it required the user to be constantly connected to the internet to verify the license and the upgrade was going to be costly again. In December 2025, they released a version 11 and again was going to be even more expensive to go from 9 to 11. I had purposely held off upgrading due to this cost. Thank goodness I did as they just recently closing down the sales and ‘hopefully’ those with the internet key ‘should’ still be able to run their layouts. :grimacing: I’m sure a lot of customers are quite upset as this was sold as a commercial product and quite a number of museums use it to run their layouts. It’s too bad, because the newer versions had some really cool additions to the product.

I also run JMRI (mostly for DecoderPro for setting up my locos) but have many times attempted to setup automation. Unfortunately, the Dispatcher really lacks the umph to be a truly viable option. A complex process of any automation, needs to have a really robust UI. I shouldn’t have to write scripts for every thing I need it to monitor for automation to work.

iTrain is my next favorite software after TrainController, and now that there won’t be anymore updates to TrainController I will need to further develop in iTrains as I need automation sooner rather than later. I built my whole layout based on that concept (Manual operation with Automation running behind the scenes). It does support Digitrax/Loconet but not documented to the same degree as other hardware. I really believe that train automation is not as big in North America as it is in European so the emphasis is for hardware there.

So when I found Traintastic during a web search for model train automation software, I was delighted to find a group that was in the process of building an Open Source Project. I was quite intrigued. Unfortunately, it’s been over 15 years since I’ve done any computer programming so I wouldn’t be able to contribute that way, but I could certainly act as beta tester. Glad to help.

@Randy, good news :slight_smile: I’ve started with some basics for automatic train control.

Things I’ve added so far (not yet public):

Button to allow/disallow automatic driving:

Basic form of routes:

I want to keep the first version simple, just a list of blocks and an optional wait time.

The white text entries are user provided, the gray entries are resolved intermediate blocks. For path finding it uses Dijkstra’s algorithm, for distance it uses path cost.
The default path cost is 1, but every turnout in the path adds 1 for straight and 3 for diverged, changing direction has a cost of 1000. (These costs are just guesses, time will tell what is best I hope.)

If a path can’t be found, the vertical line in front of the block names will have a gap.

For blocks where the train has to wait (e.g. a station) I’m thinking about adding a dot to the line, this will make is visual easy to understand the rout quickly I think.

I’m going to demonstrate Traintastic October 17th in Ermelo, The Netherlands for the first time using the Demonstration and Test layout I’m building, try to get a very basic form of automatic driving ready before that. Beside the Traintastic work I also need to do some layout wiring… :upside_down_face:

Greetings,
Reinder

4 Likes

Really cool! I’m very much interested in the path finding code, will read it closely once it’s published!

Yesterday evening I’ve uploaded some code, test build can be found at: traintastic.org/download/develop/feature-automatic-train-control

For those who like to experiment, feel free to build a board layout and create some routes to see how it works. It is very basic, but my first goal is a automatic driving, route logic can be extended later :slight_smile:

Feedback is welcome :slight_smile:

@gfgit Path finding between blocks starts here: TrainRoute::resolve()

Wow you implemented shortest path algorithm yourself!

I’m testing the build, do you have also a world I can download with many blocks already?

I did a simple layout with 5 blocks, path finding seems ok, now how can I tell train to execute the route?

Just made something to play with :slight_smile:

pathfinder 2026-09-25.ctw (2.3 KB)

Other nice thing to try is, create some routes, then alter the board, exit edit mode. The all routes will be updated.

You can’t, thats what I’m working on now :slight_smile:

Assigning a route to a train, what should be the rules?

Given the route: A -> B -> C -> D and block E not part of the route.

If a train is in A obviously allowed.
If a train is in B or C, I think it should be allowed, it can just start from there.
If a train is in D, it is the destination so it makes no sense
If a train is in E, it should not accept it.

Should the train travel direction match the route direction?
For B and C it should I think, for A it is questionable.

Feel free to share your thoughts :slight_smile:

Update

Made some progress on the Automatic Train Control feature.

  • Routes can now be assigned to trains.
  • Added a basic dispatcher, the dispatcher performs the path reservation for trains that have a route assigned.
  • Added an automatic option to the train (not yet fully functional).

When putting a train in automatic mode, it will be acquired by an Auto Throttle. The auto throttle (= train driver) will be responsible for controlling the train speed/direction just like human controlled throttles are. A human can still steal the control, this will disable automatic mode.

How does it work?

What Responsibility
Throttle Control train speed/direction
Dispatcher Block to block path reservation
TrainTracking Track train movements and route progress
Supervisor Validate if operations are allowed

The Supervisor does not yet exist, it will be responsible for checking if e.g. train (either manual or automatic) don’t violate e.g. speed limits, direction restriction, signals etc.

This clear separation of responsibilities makes it easy to support mixed automatic/manual traffic.

Currently all dispatching is automatically, but in the future I’d like to support (partly) manual dispatching as well. e.g. an automatic dispatched main line and staging area and manual dispatched station (using hardware buttons).

Greetings,
Reinder

pathfinder 2026-10-06.ctw (3.0 KB)

Hello, I’ve added dummy Z21 and inputs on all blocks to test it without real layout. Maybe I should connect it to traintastic-simulator too?

Anyway there seems to be a bug: I assign train to block, then assign route to train.

When I set train to automatic nothing happens. I have to toggle it off and on to make it reserve the first path.
Then I simulate destination block occupied, free origin block, path is correctly released with delay. Again to reserve next path I have to toggle automatic off and on.

EDIT: I didn’t check World automatic settings, it works correctly now!

@reinder there is another bug.

If you load the world I’ve attached, it should set train in a block and reserve the next block.

Train is automatic, if you uncheck automatic, server crashes!

I think it’s because loaded() does not call automatic OnSet callback so it does not acquire an automatic throttle.

But when disabling it asserts on m_throttle which is null.

Let me know if you experience same behavior

UPDATE:

If I assign Automatic throttle inside Train::loaded() if automatic is true, it crashes in other place.

Assigning throttle fires an event which if route is assigned ends in Dispatcher::evaluateTrain() but world is not fully loaded yet, especially TrainRoute::resolve() has not been called yet, making it crash because routePosition is zero but resolvedEntries is empty.

I’ve fixed this by checking train.route->valid which is still false so we don’t crash BUT we don’t get another event when route is actually loaded so train never gets evaluated on load.
I suggest we add a new LoadEventOrded::train which is after routes

aaaaaand there is another interesting thing XD I love debugging.

Automatic train with route assigned to block 1.
Path gets automatically reserved to block 2.

Now shutdown server and restart.
block 1 still contains train and is occupied, while block 2 is free but reserved for train.
Being reserved already, Dispatcher::evaluateTrain() skip reserving the path.
This means signals and turnouts between block 1 and block 2 are no more reserved after restart. But you cannot remove train reservation from block 2 because it’s not train head so it shows error dialog. Thus you cannot retrigger path evaluation unless you fully remove the train which is not good.
Think for example if you have another automatic train behind our train, if we remove our train to reassign it and reevaluate path, the other train could proceed and reserve same path before us.

EDIT: sometimes if I correctly assign everything and first block path gets reserved then I:

  • set automatic to false
  • cancel route
  • remove from start block
  • remove reservation on destination block

The path between start and next block stays reserved and there is no way to remove it

Hi @gfgit,

Many thanks for testing, there are still many cases that don’t work properly yet.

The load order is important indeed, the board map must be know before routes can be resolved. Same goes for train automatic, the auto throttle should be created after that.

Cancel/save/restore path reservations is not yet implemented. So feel free to open a PR :slight_smile:

Something else I want to change, the BlockRailTile contains path release logic and train detection logic. That needs to be moved to TrainTracking and the Dispatcher.

Greetings,
Reinder

We could start by adding 2 actions in block context menu:

  • emergency stop all trains in block
  • release next reserved path if train is not already inside it

Releasing reserved path will also release next block which at the moment stays reserved with current train.
We could also require that if train is in fromBlock, it must be stopped or gets automatically emergency stopped before releasing a path.

This could done like this:

  • release() fails if train is not stopped
  • we then fallback to delayedRelease() which stops train, waits a bit and then releases.

This should apply also to NXButtons which currently are the only way I know to manually release a path.

About saving/restoring path I don’t know yet how to proceed. Maybe we cancel all path on world close and on world reopen we reserve only automatic train routes? So block reserved state becomes a NoStore?
This is good if you close traintastic and later in another day you play again but railway layout is in different state so no point in rembering reservations.
But if traintastic suddenly crashed and you reopen it, it would be cool to keep reservations in last saved state

Gave it a bit of thought, what about a block reset option (only UI, not script).

It should give the user a popup to inform what is about to happen:

  • EStop all trains reserving the block.
  • Force release all paths
  • Mark the block Free, unless a sensor says otherwise.

This is a “fix” function, which is useful during development, goal should be that a “normal user” never has to use it. We could add a client developer option for it, so by default it is hidden.

Saving and restoring state is a challenge, world config/state is already saved separately, but there isn’t a “load without state” option yet. That is a nice recovery option if something goes wrong.

It would be nice to save the reserved path’s as well, then you can continue where you stopped last time. That is still my goal :slight_smile:


Did some more work on the test layout and AutoThrottle yesterday evening. Implemeting a very basic basic driving routine, hope to test it this evening.

Other thing I found is that worldEvent is never called, because the AutoThrottle object isn’t known to the World currently, so I’ll fix that too.

You need this function if for example you cancel a route but it already has reserved a path. Otherwhise there is not way to release the path without first removing the train.
Note that also there is no way to manually block another path unlsess you put some NX buttons in the board, then you can also release the path.
Now we have to think where user should put NX Buttons. For example in a station for sure you will have a button on each side of a block.
In a railway line with many blocks usually you don’t have turnouts so you don’t have NX Buttons. Here path reservation is optional, maybe to keep a level crossing closed until train passes?
But for coherence what you can manually release you should be allowed also to manually reserve I think.

The drag 'n drop path reservation is something I added temporary for testing Dinamo. I’ve just replaced it by train goto, now you can drag 'n drop a train to another block on the layout (or call it from Lua), it will create a temporary route and assign it to the train. Once the train completes the route (= stops in final block) the route will be deleted.

For the NX buttons my idea is:

By default the Dispatcher handles the path reservations, based on the train’s route. To make it more fun I plan add a manual dispatch option to the zone (changeable at runtime using a switch or Lua). When a zone is in manual dispatch mode, the user has to dispatch using NX buttons. The mainline can still be in auto mode, so the dispatcher will handle that.

This will allow mixed manual/automatic train driving and mixed manual/automatic dispatching :slight_smile:

For NX buttons this will mean they are only active if at least one adjacent block is in manual dispatch mode. (this is currently not the case)

For Lua then the same rule applies, for blocks that are in manual mode Lua can set paths. This allows to user to build their own dispatcher and/or do some magic with external hardware :slight_smile:

I want to add another traffic control object to Traintastic, the Supervisor. The supervisor will be responsible for validating all traffic control actions: train movements, turnouts, signals, paths etc. These “rules” also apply to the automatic dispatching and auto throttle. By separating the responsibilities mixing all kinds of manual/automatic features should be possible.

This will require “some” time to build and some refactoring, so it will be a multi stage project.


Internally a release path is needed for sure, but I’m not sure if we need it as an UI option. Paths set using NX Buttons can be released by setting the same path again using the NX buttons.

For automatic dispatcher paths, the dispatcher is responsible for releasing them too, this is not fully implemented yet.

Ideally the only reason for manual release (with supervisor override) is user/hardware error or bugs in Traintastic. So it should really be a last resort option I think with appropriate logging.

I’d hope my idea/vision is a bit clear, if not please ask :slight_smile: Feedback is also welcome :slight_smile:

Greetings,
Reinder