Skip to content

The Robot Class

Note

The main robot class can only be used once within your project

Danger

The safety features included within KevinbotLib are not guaranteed to always function.

It is always recommended to add a physical emergency stop system to your robot.

The KevinbotLib main robot class is the starting point for your robotics project. It integrates many components of KevinbotLib to make it easy to design your own robot.

Features

The KevinbotLib Robot class sets up some components of KevinbotLib to make designing a robot easier. The components are listed below.

  • Communication Client
  • Logging Configuration
  • Periodic log file rotations
  • Logging to HTTP Server
  • Control Console communication and management
  • Robot operational mode management
  • Safety features
  • Signal shutdown support (POSIX only)

Usage

  • Extend the BaseRobot class and add your own code.
  • Call YourRobotClassName().run() to start the robot's execution
  • All the components listed above will be started up automatically 😀

Warning

It is not recommended to override the run method, or any other private method marked with the @final decorator.

Shutdown signals

Note

The shutdown signals are only supported on POSIX OSes (like Linux or macOS). They are not supported on Windows due to the lack of user signals in the NT kernel.

SIGUSR1

This will trigger a graceful system shutdown similar to CTRL-C on the console

This should leave the robot in a state where it's ready for a code restart

Info

This will cause the application to end with exit code 64

SIGUSR2

This will trigger an emergency shutdown similar to hitting space on the Control Console

Info

This will cause the application to end with exit code 65

Example

If the robot code's PID (process id) is 1234, you can run the following command to gracefully shut it down:

kill -SIGUSR1 1234

Examples

Basic Robot Controller

examples/robot/robot.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from kevinbotlib.logger import Level
from kevinbotlib.robot import BaseRobot


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            opmodes=[
                "TestOp1",
                "TestOp2",
                "TestOp3",
                "TestOp4",
            ],  # robot's operational modes
            log_level=Level.TRACE,  # lowset logging level
            enable_stderr_logger=True,
            cycle_time=20,  # loop our robot code 20x per second - it is recommended to run much higher in practice
            metrics_publish_timer=0,  # the test robot doesn't use metrics - see the metrics_robot.py example for a metrics usage example
        )

    def robot_start(self) -> None:  # runs once as the robot starts
        super().robot_start()
        print(
            "Starting robot..."
        )  # print statements are redirected to the KevinbotLib logging system - please don't do this in production

    def robot_periodic(self, opmode: str, enabled: bool) -> None:
        super().robot_periodic(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'}... {opmode}")

    def opmode_init(self, opmode: str, enabled: bool) -> None:
        super().opmode_init(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} init... {opmode}")

    def opmode_exit(self, opmode: str, enabled: bool) -> None:
        super().opmode_exit(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} exit... {opmode}")

    def robot_end(self) -> None:  # runs as the robot propares to shutdown
        super().robot_end()
        print("Ending robot...")


if __name__ == "__main__":
    DemoRobot().run()

Robot Emergency Stop

examples/robot/estop_robot.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from kevinbotlib.logger import Level
from kevinbotlib.robot import BaseRobot


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            opmodes=[
                "TestOp1",
            ],  # robot's operational modes
            log_level=Level.TRACE,  # lowset logging level
            enable_stderr_logger=True,
            cycle_time=20,  # loop our robot code 20x per second - it is recommended to run much higher in practice
            metrics_publish_timer=0,  # the test robot doesn't use metrics - see the metrics_robot.py example for a metrics usage example
        )

        BaseRobot.register_estop_hook(lambda: print("E-STOP Hook 1"))  # usually used for hardware shutdowns
        BaseRobot.register_estop_hook(
            lambda: print("E-STOP Hook 2")
        )  # they will run in a thread - *MUST BE THREAD-SAFE*

    def robot_start(self) -> None:  # runs once as the robot starts
        super().robot_start()
        print(
            "Starting robot..."
        )  # print statements are redirected to the KevinbotLib logging system - please don't do this in production

        self.estop()

    def robot_periodic(self, opmode: str, enabled: bool) -> None:
        super().robot_periodic(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'}... {opmode}")

    def opmode_init(self, opmode: str, enabled: bool) -> None:
        super().opmode_init(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} init... {opmode}")

    def opmode_exit(self, opmode: str, enabled: bool) -> None:
        super().opmode_exit(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} exit... {opmode}")

    def robot_end(self) -> None:  # runs as the robot propares to shutdown
        super().robot_end()
        print("Ending robot...")


if __name__ == "__main__":
    DemoRobot().run()

Robot Controller with Metrics Publishing

examples/robot/metrics_robot.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from kevinbotlib.logger import Level
from kevinbotlib.robot import BaseRobot


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            opmodes=[
                "TestMetricsRobot",
            ],  # robot's operational modes
            log_level=Level.TRACE,  # lowest logging level
            enable_stderr_logger=True,
            cycle_time=5,  # loop our robot code 5x per second - it is recommended to run much higher in practice
            metrics_publish_timer=5.0,  # how often to publish new system metrics to the control console
        )
        BaseRobot.add_basic_metrics(self, update_interval=2.0)  # how fast to get new metrics internally

    def robot_start(self) -> None:  # runs once as the robot starts
        super().robot_start()
        print(
            "Starting robot..."
        )  # print statements are redirected to the KevinbotLib logging system - please don't do this in production

    def robot_periodic(self, opmode: str, enabled: bool) -> None:
        super().robot_periodic(opmode, enabled)

        print(self.metrics.getall())

    def opmode_init(self, opmode: str, enabled: bool) -> None:
        super().opmode_init(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} init... {opmode}")

    def opmode_exit(self, opmode: str, enabled: bool) -> None:
        super().opmode_exit(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} exit... {opmode}")

    def robot_end(self) -> None:  # runs as the robot propares to shutdown
        super().robot_end()
        print("Ending robot...")


if __name__ == "__main__":
    DemoRobot().run()

Dynamic State Changes

examples/robot/dynamic_robot.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from kevinbotlib.logger import Level
from kevinbotlib.robot import BaseRobot


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            opmodes=[
                "OpModeThatWillEnable",
                "OpModeThatWillDisable",
                "OpModeThatWillEStop",
                "OpModeThatWill1stOpMode",
            ],  # robot's operational modes
            log_level=Level.TRACE,  # lowset logging level
            enable_stderr_logger=True,
            cycle_time=20,  # loop our robot code 20x per second - it is recommended to run much higher in practice
            metrics_publish_timer=0,  # the test robot doesn't use metrics - see the metrics_robot.py example for a metrics usage example
            allow_enable_without_console=True,  # * allow the robot to enable without the prescence of a control console
        )

    def robot_start(self) -> None:  # runs once as the robot starts
        super().robot_start()
        print(
            "Starting robot..."
        )  # print statements are redirected to the KevinbotLib logging system - please don't do this in production

    def robot_periodic(self, opmode: str, enabled: bool) -> None:
        super().robot_periodic(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'}... {opmode}")

    def opmode_init(self, opmode: str, enabled: bool) -> None:
        super().opmode_init(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} init... {opmode}")

        if opmode == "OpModeThatWillEnable":
            self.enabled = True

        if opmode == "OpModeThatWillDisable":
            self.enabled = False

        if opmode == "OpModeThatWillEStop":
            self.estop()

        if opmode == "OpModeThatWill1stOpMode":
            self.opmode = self.opmodes[0]

    def opmode_exit(self, opmode: str, enabled: bool) -> None:
        super().opmode_exit(opmode, enabled)

        print(f"OpMode {'enabled' if enabled else 'disabled'} exit... {opmode}")

    def robot_end(self) -> None:  # runs as the robot propares to shutdown
        super().robot_end()
        print("Ending robot...")


if __name__ == "__main__":
    DemoRobot().run()

See also

Robot Reference