I get bored very easily trying to learn programming languages, so I usually only learn how to use the language to solve some immediate problem I’m facing. The result - I only learn the basics of a language and later do not want to work through a class that covers the stuff I know to get to the stuff I don’t. I also don’t have a way to know what I don’t know about the language.
The other easy way to supplement the Python (or anything else) you don’t know is to ask a Chat AI Microsoft Copilot, Google Gemini, or Anthropic’s Claude. All you have to do is type in “how do I … in Python” (or any other language).
Challenge accepted - I’ve been reading about coding with these large language models, but had as yet not tried to see what I would get.
Here is what I asked for:
and I have to say it made an impressive effort:
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile
from geometry_msgs.msg import Twist # Example message type (replace with actual message types)
from std_srvs.srv import Empty # Example service type (replace with actual service types)
# Custom message and service types (replace placeholders with actual message definitions)
from gopi5go_dave.msg import BatteryStatus
from gopi5go_dave.srv import Dock, Undock
# State definitions
INIT = "init"
DOCKED = "docked"
UNDOCKING = "undocking"
UNDOCKED = "undocked"
PLAYTIME = "playtime"
READY_TO_DOCK = "ready_to_dock"
DOCKING = "docking"
class Gopi5goDave(Node):
def __init__(self):
super().__init__("gemini_dave")
# Initialize state machine variables
self.current_state = INIT
self.last_state_change = None
# Create subscriptions with QoS profile 10 samples deep
qos = QoSProfile(depth=10)
self.battery_sub = self.create_subscription(BatteryStatus, "/battery_status", self.battery_callback, qos)
self.dock_sub = self.create_subscription(Twist, "/dock_status", self.dock_callback, qos)
# Create service clients
self.dock_client = self.create_client(Dock, "/dock")
self.undock_client = self.create_client(Undock, "/undock")
# Create a timer object for the main callback
self.timer = self.create_timer(1.0, self.main_callback)
# Open log file
self.log_file = open("/home/pi/logs/life.log", "a")
def main_callback(self):
# Get current battery status
battery_msg = self.battery_sub.receive_message()
if battery_msg is None:
self.get_logger().warn("No battery status message received!")
return
# State machine logic
if self.current_state == DOCKED and battery_msg.milliamps < -100:
self.transition_state(UNDOCKING, "Low battery, requesting undock")
self.send_undock_request()
elif self.current_state == READY_TO_DOCK and battery_msg.volts < 10.1:
self.transition_state(DOCKING, "Low voltage, requesting dock")
self.send_dock_request()
# Add logic for other state transitions based on messages and conditions
# (Optional) Update state based on dock_status message
def send_dock_request(self):
req = Dock.Request()
self.future = self.dock_client.send_request(req)
def send_undock_request(self):
req = Undock.Request()
self.future = self.undock_client.send_request(req)
def battery_callback(self, msg):
# (Optional) Update state based on battery message (handled in main_callback for this example)
pass
def dock_callback(self, msg):
# (Optional) Update state based on dock_status message (handled in main_callback for this example)
pass
def transition_state(self, new_state, message):
"""
Performs state transition and logs message with timestamp.
"""
self.last_state_change = self.get_clock().now().to_local_time().strftime("%Y-%m-%d %H:%M:%S")
self.current_state = new_state
self.get_logger().info(f"State transition: {self.last_state_change} - {self.current_state} - {message}")
self.log_file.write(f"{self.last_state_change} - {self.current_state} - {message}\n")
def main(args=None):
rclpy.init(args=args)
node = Gopi5goDave()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
rclpy.shutdown()
node
A valiant effort indeed, and perhaps would give a NOOB some hints how to code a state machine in a ROS callback. The Gemini engine ignored the multi-threaded, reentrant hint that was needed to get the node to function with so much asynchronous message passing going on during docking, undocking, and text-to-speech state transition announcements.
The subtlety of ROS programming is really my weakness at this point. The tutorials are too simplistic and the packaged ROS code of the Gurus is pages and pages of unintelligible parameters and asynchronous methods.
Interestingly, it does (almost) understand how to program the multithreaded executor concept:
import rclpy
from rclpy.node import Node
from rclpy.executors import MultiThreadedExecutor
class MyNode(Node):
def __init__(self):
super().__init__("my_node")
# Create a timer object for the main callback
self.timer = self.create_timer(1.0, self.main_callback)
def main_callback(self):
# Your main callback logic here
print("Main callback called")
def main(args=None):
rclpy.init(args=args)
# Create a MultiThreadedExecutor
executor = MultiThreadedExecutor()
# Create a node instance
node = MyNode()
# Add the node to the executor
executor.add(node)
# Run the executor
executor.spin()
# Destroy the node and executor
node.destroy_node()
executor.shutdown()
if __name__ == '__main__':
main()
Except programmed that way, it would fire up the node and immediately destroy it - missing a while loop:
Boy to older man:
“There’s gotta be a nuclear silo in here SOMEWHERE. . . .”
Man: “Why do you say that?”
Boy: “There’s so much incomprehensible stuff in here, it’s all gotta be Top Secret!”
Me, I’m still trying to figure out mutexes and classes. . .
[goes and gets some headache medicine]
People are very quick to dismiss the idea that software should be designed with a wide understanding of software engineering, software science, and domain libraries to guide the actual code production.
Like relying on the contact list in our phone can encourage not memorizing important phone numbers, AI “code generators” can encourage ignoring the full corpus of software education and the benefit of domain libraries.
For example, I asked for a “reentrant state machine” and was given a super simplistic, hard coded state machine that lacks protection against reentrance, and does not reflect nor enlighten to state machine science, style, and made no attempt to reuse the well tested ROS state machine library package. (Neither did I but I am aware of it, the risks and benefits of both approaches.)
Between the Internet enabling “anyone can code” and now AI code generators enabling “code from (incomplete) prose descriptions” the respect for education is further diminished, but the idea that an intelligent machine will take over the world is probably postponed a generation.