Executing Scripts from the Command Line

Executing Scripts at Startup

You can have a Python script specified on the command line executed when Choreonoid starts up. When a file with the .py extension is specified on the command line, the script is executed at startup.

choreonoid sample/python/SR1Walk.py

In the above example, SR1Walk.py is executed on the launched Choreonoid, and the simulation of the project constructed by the script starts.

You can also specify a script file explicitly with the --python option (or its short form -p).

choreonoid --python script.py

Combining with Project Files

It is also possible to specify a script together with a project file.

choreonoid project.cnoid script.py

In this case, the script is executed after the project is loaded, so you can execute a script that manipulates the items contained in the project.

Batch Execution and Control of the Termination

When you make a script perform some processing, you often want Choreonoid to exit as soon as the processing has finished. This is especially the case when Choreonoid is executed automatically from a shell script, because the next step cannot proceed until Choreonoid exits.

What you have to note here is that Choreonoid does not exit automatically even after the execution of a startup script has finished. This is because Choreonoid is a GUI application and keeps running its event loop. In ordinary interactive use, the application exits when the user closes the window, but in automatic execution there is nobody to do that.

The –batch Option

The --batch option is provided for this purpose.

choreonoid --batch project.cnoid script.py

When this option is specified, Choreonoid performs all the processing specified at startup and exits when no processing running automatically remains. In the above example, Choreonoid loads the project, executes the script, and exits automatically when the simulation started by the script has finished.

--batch also enables the non-interactive mode (--non-interactive). With it, the output to the message view is also output to the standard output, and the confirmation dialogs, which would block the processing when there is nobody to respond to them, are not shown.

The option is independent of whether the window is shown, so batch execution can also be performed with the window shown. If the window is not necessary, combine the option with --headless.

choreonoid --headless --batch project.cnoid script.py

Processing Handled as Running Automatically

The following are automatically handled as the “processing running automatically” that --batch waits for.

  • The processing of the startup options (loading a project, executing a startup script, and so on)

  • The execution of a simulation

  • The playback of an animation

  • A Python script running in the background mode

The simulation is detected by using the fact that a simulator item puts the related items into the continuous update state while it is running, so no special description is required on the script side.

Example of Executing a Simulation and Saving the Result

The following is an example of a script that executes the simulation of a project and saves its result as a motion data file.

from cnoid.Base import *
from cnoid.BodyPlugin import *

rootItem = RootItem.instance
simulatorItem = rootItem.findItem(SimulatorItem)

def onSimulationFinished(isForced):
    motionItem = rootItem.findItem(BodyMotionItem)
    if motionItem.motion.save("result.seq"):
        print("The simulation result has been saved.")
    else:
        MessageView.instance.putln(
            "Failed to save the simulation result.", MessageView.MessageType.Error)

simulatorItem.sigSimulationFinished.connect(onSimulationFinished)
simulatorItem.setRealtimeSyncMode(SimulatorItem.NonRealtimeSync)
simulatorItem.startSimulation()

Execute it as follows.

choreonoid --headless --batch project.cnoid script.py

Note that this script does not contain any description for the termination. The end of the simulation is received with sigSimulationFinished to save the result, and the termination of Choreonoid itself is left to --batch.

NonRealtimeSync is specified for setRealtimeSyncMode so that the simulation proceeds as fast as possible without synchronizing with the real time. This is usually preferable in batch execution.

Note

The above script assumes that the simulation result is recorded. If the recording mode of the simulator item is set so that nothing is recorded, no BodyMotionItem is created, so check the setting on the project side.

Writing a Script in an Event-driven Manner

As in the above example, write a script in an event-driven manner so that the completion of the processing is received with a signal. You must not wait for the completion by polling as follows.

# You must not write a script in this way
simulatorItem.startSimulation()
while simulatorItem.isRunning():
    time.sleep(0.001)
# The correct result cannot be obtained even if it is retrieved here

A startup script is called from inside the event loop of Choreonoid. Since the event processing does not proceed while the script does not return the control, writing such a loop prevents the recording of the simulation result and the processing performed at the end of the simulation from being executed at all. As a result, problems such as the retrieved data being empty occur.

Write the necessary processing in the functions connected to the signals, and make the body of the script return the control promptly.

Making the Batch Mode Wait for Your Own Processing

When a script performs processing that Choreonoid cannot detect automatically, such as periodic processing with a timer, Choreonoid exits in the middle of the processing as it is. In that case, declare the execution of the processing with App.beginOngoingProcess.

from cnoid.Base import *
from cnoid.QtCore import *

process = App.beginOngoingProcess("periodic processing of the script")
counter = 0

def onTimeout():
    global counter
    counter += 1
    print("count = %d" % counter)
    if counter == 5:
        timer.stop()
        process.finish()

timer = QTimer()
timer.setInterval(200)
timer.timeout.connect(onTimeout)
timer.start()

The string given to beginOngoingProcess is a description that represents the content of the processing. It is used in the diagnostic message described later. The declared processing is regarded as finished when finish of the returned handle is called or when the handle is released.

Return Code

The return code of Choreonoid is 1 if at least one error message has been output, and 0 otherwise. This is valid regardless of whether --batch is specified. Hence the success or failure can be determined from a shell script just as with an ordinary command.

Only error messages are taken into account here, and warning messages do not affect the return code. The messages of Choreonoid are used as follows.

  • Error … Indicates that the processing failed and its purpose was not achieved, such as when loading a file failed. The message is shown in red with the prefix “Error: “ in the message view.

  • Warning … Indicates that the processing itself could be continued but there is something to note, such as when a part of a setting differs from what is expected and a default value is used instead. The message is shown with the prefix “Warning: “ in the message view.

Hence the return code is 0 if the processing is completed even when warnings have been output. If you also want to detect warnings, parse the messages put to the standard output.

#!/bin/bash

choreonoid --headless --batch project.cnoid script.py

if [ $? -ne 0 ]; then
    echo "The simulation failed."
    exit 1
fi

If you want to specify the return code explicitly from the script side, or to exit in the middle of the processing, use App.exit.

from cnoid.Base import *

App.exit(2)

Note

App.exit requests the event loop to exit, and it does not terminate the process on the spot. The execution of the script continues after calling it, so call it at the end of the script.

When Choreonoid Does Not Exit

If Choreonoid does not exit even though --batch is specified, some processing that does not finish remains. Inputting Ctrl+C in the terminal outputs what Choreonoid was waiting for.

The batch mode was waiting for the following processes: continuous update of items

A common cause is that the time range mode of the simulator item is set to unlimited. With this setting, the simulation does not finish automatically, so it is not suitable for batch execution. Specify the time range on the project side, or set the mode so that the simulation continues while the controllers are active.

The same applies when you forget to finish the processing declared with App.beginOngoingProcess described above.

Headless Execution without Showing the Window

With the --headless option, Choreonoid starts without showing the main window (for the basics of this mode, refer to Launch Mode without Showing the Window ). In this mode, the output to the message view is put to the standard output, so the execution status, such as the output of scripts and error messages, can be checked on the terminal. Combined with the execution of scripts, this makes it possible to use Choreonoid like a command line tool.

choreonoid --headless project.cnoid script.py

Such headless execution can be utilized for purposes such as the following.

  • Batch execution of simulations

A series of processes — loading a project, executing a simulation, saving the results as log or motion data files, and exiting — can be performed fully automatically. Since no GUI rendering is performed, the execution overhead is also reduced.

  • Repeated execution under many conditions

By repeatedly launching Choreonoid from a shell script or the like, you can automatically execute many simulations while changing the parameters of the models and controllers, and collect the results. Environment variables and other means can be used to pass the execution conditions.

  • Automating processes on models and projects

Processes such as loading a model file and inspecting or converting its contents or performing kinematics calculations can be scripted and executed like a regular command.

  • Execution in remote environments without a GUI

Simulations can be executed in environments without a window system, such as computing servers and CI environments.

As described in Launch Mode without Showing the Window, in an environment where no window system is available, this mode is automatically enabled even if --headless is not specified. Even in that case, the vision sensor simulation by GLVisionSimulator can be executed without a window system by the rendering using EGL, and the hardware acceleration by the GPU is also enabled. This means that simulations using camera images and range images can be executed as they are in an environment without a GUI.

Also keep the following points in mind for headless execution.

  • Rendering on the GUI, such as that of the scene view, is not performed. If you want to automate processes involving GUI rendering (such as obtaining the rendering results of the scene view) without showing windows, there is a way to use a virtual display. Refer to Running the GUI without Showing Windows Using a Virtual Display for this.

  • --headless also enables the non-interactive mode (--non-interactive). No confirmation dialog is shown, and the corresponding processing proceeds with its default behavior.

  • The following options can also be used together.

For example, the following command executes the simulation of the project without showing the window, and Choreonoid also exits when the simulation finishes. No script is necessary.

choreonoid --headless --batch --start-simulation project.cnoid

Option to Load a Script as an Item

When the --python-item option is used, the script is not executed at startup but is loaded into the item tree as a Python script item.

choreonoid --python-item script.py

Since the loaded item is in the checked state, you can execute it by pressing the button of the ScriptBar. You can also execute it by selecting “Execute” from the context menu that is shown by right-clicking the item in the item tree view. A running script can be stopped with “Terminate” in the same menu. Refer to Python Script Item for the details.