Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LLM Pico Car: How I Built a Voice-Controlled To...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for komo_fr komo_fr
January 31, 2026

LLM Pico Car: How I Built a Voice-Controlled Toy Car Using Generative AI, Gradio, and Raspberry Pi Pico

This slide deck was used in my talk at PythonAsia Online Charity Talk H2.
"LLM Pico Car: How I Built a Voice-Controlled Toy Car Using Generative AI, Gradio, and Raspberry Pi Pico"

I introduce a small but fun project where a toy car moves based on voice or image instructions, powered by Python, multimodal inputs, and a Raspberry Pi Pico.

EventPage:
https://pythonasia.org/events/pythonasia-online-charity-talk-h2/

GitHub:
https://github.com/komo-fr/llm-pico-car

X(Twitter):
https://x.com/komo_fr

Avatar for komo_fr

komo_fr

January 31, 2026
Tweet

More Decks by komo_fr

Other Decks in Programming

Transcript

  1. w 5PNPLP'VSVLJ w 9 5XJUUFS !LPNP@GS w *MJWFJO+BQBO🇯🇵 w *XPSLBU#F1SPVE*OD

    w IUUQTXXXCFQSPVEKQ w *XPSLPOEBUBTDJFODFBOETZTUFNEFWFMPQNFOUQSPKFDUT  NBJOMZVTJOH1ZUIPO 4FMGJOUSPEVDUJPO 0VS1SPEVDUT 5FDI.FFUVQT 1ZUIPO-FSOJOH %FWFMPQFS%PDT 
  2. w --.1JDP$BS w .ZIPCCZQSPKFDU w "UPZDBSQPXFSFECZHFOFSBUJWF"* DPOUSPMMFECZWPJDFBOEJNBHFT w 8IBUMJCSBSJFTBOEUFDIOPMPHJFT*VTFE DPEFFYBNQMFT

     w 8IBU*GPDVTFEPO 👉*IPQFUIJTHJWFTZPVTPNFJEFBT GPSDPNCJOJOHHFOFSBUJWF"*BOE*P5 5PEBZTUBML 
  3. w 8IBUEJE*NBLF  w )PXEJE*NBLFJU  w 4UFQ*OQVUWPJDFGSPNBXFCBQQ w 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT

    w 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET w 3FTVMU w "QQMJDBUJPO*NBHF#BTFE$PNNBOET w 4VNNBSZ 0VUMJOF 
  4. w 8JUIHFOFSBUJWF"*  JUXPVMECFFBTJFSUPDPOWFSUOBUVSBMMBOHVBHFJOUPTUSVDUVSFEEBUB w 1SPNQUFYBNQMF l$POWFSUOBUVSBMMBOHVBHFJOTUSVDUJPOTJOUPBMJTUPGBDUJPOEJDUJPOBSJFTz (PTUSBJHIU BOEUVSOSJHIU [

    { "action": { "type": "forward", "distance_cm": 20,}}, { "action": { "type": "turn", "direction": "right", "angle": 90,}}, ...] /BUVSBM-BOHVBHF 4USVDUVSFE"DUJPOT .PWFGPSXBSEDN 5VSOSJHIUEFHSFFT (FOFSBUJWF"* $PSF*EFB 8IBUEJE*NBLF 
  5. 

  6. w ,JUSPOJL"VUPOPNPVT3PCPUJDT1MBUGPSN #VHHZ GPS1JDP w IUUQTLJUSPOJLDPVLQSPEVDUTBVUPOPNPVTSPCPUJDTQMBUGPSNGPSQJDP /PUVTFEJOUIJTQSPKFDU  CVUUIFGPMMPXJOHBSFBMTPBWBJMBCMF EJTUBODFTFOTPST

    TFSWPDPOOFDUPST BMJOFGPMMPXJOHTFOTPS .PUPSTXJUIXIFFMT º -&%T #V[[FS -&%T )BSEXBSF 1JDP$BS )PXEJE*NBLFJU 3BTQCFSSZ1J1JDP 
  7. w *EPO`UIBWFNVDIFYQFSJFODFXJUIFMFDUSPOJDT  TP*XBOUFEUPVTFBSFBEZNBEFLJU w -JUIJVNJPOCBUUFSJFTDBOCFEJ ffi DVMUUPNBOBHF  TP*XBOUFEUPVTFSFHVMBSCBUUFSJFT

    w 3BTQCFSSZ1J❌OFFETNPSFQPXFSʜ w 3BTPCFSSZ1J1JDP✅SVOTPOSFHVMBSCBUUFSJFT 👉4P*DIPTFUIJTLJU *UXPSLTXJUI3BTQCFSSZ1J1JDPBOESVOTPOSFHVMBSCBUUFSJFT 8IZ*DIPTFUIJTLJU )PXEJE*NBLFJU 
  8. w *EPO`UIBWFNVDIFYQFSJFODFXJUIFMFDUSPOJDT  TP*XBOUFEUPVTFBSFBEZNBEFLJU w -JUIJVNJPOCBUUFSJFTDBOCFEJ ffi DVMUUPNBOBHF  TP*XBOUFEUPVTFSFHVMBSCBUUFSJFT

    w 3BTQCFSSZ1J❌OFFETNPSFQPXFSʜ w 3BTPCFSSZ1J1JDP✅SVOTPOSFHVMBSCBUUFSJFT 👉4P*DIPTFUIJTLJU *UXPSLTXJUI3BTQCFSSZ1J1JDPBOESVOTPOSFHVMBSCBUUFSJFT 8IZ*DIPTFUIJTLJU )PXEJE*NBLFJU 3BTQCFSSZ1J1JDP8) XJUI8J'JBOEIFBEFSQJOT 
  9. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 0WFSBMMBSDIJUFDUVSF )PXEJE*NBLFJU 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 
  10. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 0WFSBMMBSDIJUFDUVSF )PXEJE*NBLFJU 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 
  11. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 0WFSBMMBSDIJUFDUVSF )PXEJE*NBLFJU 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 
  12. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 0WFSBMMBSDIJUFDUVSF )PXEJE*NBLFJU 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 
  13. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 0WFSBMMBSDIJUFDUVSF )PXEJE*NBLFJU 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 
  14. w *U`TEJ ff i DVMUUPSVOTQFFDISFDPHOJUJPOPSHFOFSBUJWF"*EJSFDUMZ POUIF1JDP$BS w QSPDFTTJOHJTIFBWZ w TPNFMJCSBSJFTBSFOPUBWBJMBCMFJO.JDSP1ZUIPO

     👉4P UIFTFUBTLTSVOPOBXFCTFSWFSPONZ.BD 5IF1JDP$BSIBOEMFTPOMZNPUPSTBOE-&%T w 5BMLJOHEJSFDUMZUPB1$CSFBLTUIFlQFUMJLFzGFFMJOH  👉4P*DSFBUFEBXFCCBTFE6*POBTNBSUQIPOF  MJLFUBMLJOHUPUIFDBSPWFSUIFSBEJP 8IZUIJTBSDIJUFDUVSF )PXEJE*NBLFJU 
  15. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* BVEJPEBUB TUSVDUVSFEBDUJPOT

    4UFQ 4UFQ 4UFQ 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 4UFQ*OQVUWPJDFWJBBXFCBQQ 
  16. w 8IBUJT(SBEJP  w "XFCBQQGSBNFXPSLUIBUNBLFTJUFBTZUPCVJMEEFNPBQQTGPS NBDIJOFMFBSOJOH w *UQSPWJEFTCVJMUJODPNQPOFOUTGPSWPJDFJOQVU JNBHFJOQVU BOE

    NPSF 👉:PVDBOCVJMEB6*FBTJMZXJUIPVUEFFQGSPOUFOELOPXMFEHF $SFBUJOHBXFCBQQXJUI(SBEJP 4UFQ*OQVUWPJDFWJBBXFCBQQ 
  17. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Audio Instructions"): # Define components audio_input = gr.Audio( sources=["microphone"], type="filepath", format="wav") with gr.Row(): audio_send_button = gr.Button("Submit", variant="primary") audio_reset_button = gr.Button("Reset", variant="secondary") ... # Define click event handlers audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps,],) ... # Launch the server demo.launch(server_name="0.0.0.0", share=True) HS"VEJP HS#VUUPO HS#VUUPO HS5FYUCPY HS5FYUCPY HS+40/ TUSVDUVSFEBDUJPOT $PEF(SBEJPCMPDLT 4UFQ*OQVUWPJDFWJBBXFCBQQ HS)5.- 
  18. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components audio_input = gr.Audio( sources=["microphone"], type="filepath", format="wav") with gr.Row(): audio_send_button = gr.Button("Submit", variant="primary") audio_reset_button = gr.Button("Reset", variant="secondary") audio_transcript_output = gr.Textbox(label="Transcription Result") audio_status_output = gr.Textbox(label="Processing Status") audio_commands_output = gr.JSON(label=“Generated Commandas") # Define click event handlers ... # Launch the server demo.launch(server_name="0.0.0.0", share=True) 4UFQ*OQVUWPJDFWJBBXFCBQQ $PEF6*MBZPVU 7PJDFJOQVU DPNQPOFOU #VUUPOT 3FTVMU DPNQPOFOUT HS"VEJP HS#VUUPO HS#VUUPO HS5FYUCPY HS5FYUCPY HS+40/ TUSVDUVSFEBDUJPOT HS)5.- 
  19. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components audio_input = gr.Audio( sources=["microphone"], type="filepath", format="wav") with gr.Row(): audio_send_button = gr.Button("Submit", variant="primary") audio_reset_button = gr.Button("Reset", variant="secondary") audio_transcript_output = gr.Textbox(label="Transcription Result") audio_status_output = gr.Textbox(label="Processing Status") audio_commands_output = gr.JSON(label=“Generated Commandas") # Define click event handlers ... # Launch the server demo.launch(server_name="0.0.0.0", share=True) 4UFQ*OQVUWPJDFWJBBXFCBQQ $PEF6*MBZPVU 7PJDFJOQVU DPNQPOFOU #VUUPOT 3FTVMU DPNQPOFOUT HS"VEJP HS#VUUPO HS#VUUPO HS5FYUCPY HS5FYUCPY HS+40/ TUSVDUVSFEBDUJPOT HS)5.- 
  20. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components audio_input = gr.Audio( sources=["microphone"], type="filepath", format="wav") with gr.Row(): audio_send_button = gr.Button("Submit", variant="primary") audio_reset_button = gr.Button("Reset", variant="secondary") audio_transcript_output = gr.Textbox(label="Transcription Result") audio_status_output = gr.Textbox(label="Processing Status") audio_commands_output = gr.JSON(label=“Generated Commandas") # Define click event handlers ... # Launch the server demo.launch(server_name="0.0.0.0", share=True) 4UFQ*OQVUWPJDFWJBBXFCBQQ $PEF6*MBZPVU 7PJDFJOQVU DPNQPOFOU #VUUPOT 3FTVMU DPNQPOFOUT HS"VEJP HS#VUUPO HS#VUUPO HS5FYUCPY HS5FYUCPY HS+40/ TUSVDUVSFEBDUJPOT HS)5.- 
  21. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components ... # Define click event handlers audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps,],) audio_reset_button.click(...) audio_input.start_recording(...) audio_input.stop_recording(...) # Launch the server demo.launch(server_name="0.0.0.0", share=True) $PEF&WFOUIBOEMJOH 4UFQ*OQVUWPJDFWJBBXFCBQQ $MJDL 
  22. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components ... # Define click event handlers audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps,],) audio_reset_button.click(...) audio_input.start_recording(...) audio_input.stop_recording(...) # Launch the server demo.launch(server_name="0.0.0.0", share=True) $PEF&WFOUIBOEMJOH 4UFQ*OQVUWPJDFWJBBXFCBQQ *OQVU 0VUQVUT $MJDL 
  23. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components ... # Define click event handlers audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps,],) audio_reset_button.click(...) audio_input.start_recording(...) audio_input.stop_recording(...) # Launch the server demo.launch(server_name="0.0.0.0", share=True) $PEF&WFOUIBOEMJOH 4UFQ*OQVUWPJDFWJBBXFCBQQ .click(handler, inputs=..., outputs=...) audio_transcript_output 'VODUJPOFYFDVUFEXIFODMJDLFE w TQFFDISFDPHOJUJPO w --.QSPDFTTJOH w TFOEDPNNBOETUPUIF1JDPDBS w ʜ audio_status_output audio_commands_output audio_input 
  24. import gradio as gr ... with gr.Blocks() as demo: ...

    with gr.Tab("Voice Input"): # Define components ... # Define click event handlers audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps,],) audio_reset_button.click(...) audio_input.start_recording(...) audio_input.stop_recording(...) # Launch the server demo.launch(server_name="0.0.0.0", share=True) $PEF-BVODIUIFTFSWFS 4UFQ*OQVUWPJDFWJBBXFCBQQ 
  25. audio_send_button.click(handler_audio_input, inputs=audio_input, outputs=[audio_transcript_output, audio_commands_output, audio_status_output, progress_lamps]) def handler_audio_input(audio_file): success, text

    = transcribe(audio_file) if success: yield text, None, "จࣈى͜͠׬ྃ", render_lamps(["s", "u", "u"]) commands = text_to_command(text) commands = convert_commands(commands) yield text, commands, "ίϚϯυੜ੒׬ྃ", render_lamps(["s", "s", "u"]) response = send_commands(commands) status = response.get("status") yield text, commands, status, render_lamps(["s"] * 3) else: yield text, None, "จࣈى͜͠ʹࣦഊ͠·ͨ͠ɻऴྃ͠·͢", render_lamps(["e", "u", "u"]) w 8JUI(SBEJP JU`TFBTZUPDSFBUFB6*UIBUSFUVSOTSFTVMUTTUFQCZTUFQ w *OUIFFWFOUIBOEMFS yieldJTVTFEUPSFUVSOJOUFSNFEJBUFSFTVMUT ᶈ3FUVSOBMMSFTVMUT &WFOUIBOEMFS 0VUQVUDPNQPOFOUT ᶃ3VOUSBOTDSJQUJPO ᶅ$POWFSUUIFUFYUJOUPTUSVDUVSFEBDUJPOTVTJOHHFOFSBUJWF"* ᶇ4FOETUSVDUVSFEBDUJPOTUPUIF1JDP$BS %JTQMBZJOH*OUFSNFEJBUF3FTVMUT 4UFQ*OQVUWPJDFWJBBXFCBQQ ᶆ3FUVSOUIF"*HFOFSBUFESFTVMU ᶄ3FUVSOUIFUSBOTDSJQUJPOSFTVMU 
  26. *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* 4UFQ 4UFQ

    4UFQ 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT TUSVDUVSFEBDUJPOT BVEJPEBUB 
  27. w 4FOEJOHSBXBVEJPEJSFDUMZUPBHFOFSBUJWF"*DBOCFFYQFOTJWFCFDBVTFPG"1*DPTUT 👉1FSGPSNUSBOTDSJQUJPOMPDBMMZVTJOHGBTUFSXIJTQFS from faster_whisper import WhisperModel model_size = "tiny"

    model = WhisperModel(model_size, device="cpu", compute_type="float32") ... segments, _ = model.transcribe(tmp_audio_path, language=None) text = "".join([segment.text for segment in segments]) ᶃ-PBEUIFNPEFM ᶄ3VOUSBOTDSJQUJPO "VEJPˠ5FYU 5SBOTDSJQUJPO 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT 
  28. w -BOH$IBJO w "1ZUIPOGSBNFXPSLGPSEFWFMPQJOH--.CBTFEBQQMJDBUJPOT  SFMFBTFEJO w 8JUI-BOH$IBJO UIF0QFO"*"1* HQU

    JTDBMMFE UPDPOWFSUOBUVSBMMBOHVBHFJOTUSVDUJPOTJOUPTUSVDUVSFEBDUJPOT 5FYUˠ4USVDUVSFEBDUJPOT (PTUSBJHIU BOEUVSOSJHIU [ { "action": { "type": "forward", "distance_cm": 20,}}, { "action": { "type": "turn", "direction": "right", "angle": 90,}}, ...] /BUVSBM-BOHVBHF 4USVDUVSFE"DUJPOT .PWFGPSXBSEDN 5VSOSJHIUEFHSFFT (FOFSBUJWF"* 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT 
  29. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) ᶃ#VJMEUIFQSPNQUUFNQMBUF ᶄ*OJUJBMJ[FUIFNPEFM ᶅ$POOFDUUIFQSPNQUBOENPEFM $PEF-BOH$IBJOQJQFMJOF 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT ᶆ*OWPLFUIFNPEFMUPHFOFSBUFBDUJPOT 
  30. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) ᶃ#VJMEUIFQSPNQUUFNQMBUF ᶄ*OJUJBMJ[FUIFNPEFM ᶅ$POOFDUUIFQSPNQUBOENPEFM $PEF-BOH$IBJOQJQFMJOF 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT ᶆ*OWPLFUIFNPEFMUPHFOFSBUFBDUJPOT 
  31. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) ᶃ#VJMEUIFQSPNQUUFNQMBUF ᶄ*OJUJBMJ[FUIFNPEFM ᶅ$POOFDUUIFQSPNQUBOENPEFM $PEF-BOH$IBJOQJQFMJOF 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT ᶆ*OWPLFUIFNPEFMUPHFOFSBUFBDUJPOT 
  32. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) ᶃ#VJMEUIFQSPNQUUFNQMBUF ᶄ*OJUJBMJ[FUIFNPEFM ᶅ$POOFDUUIFQSPNQUBOENPEFM $PEF-BOH$IBJOQJQFMJOF 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT ᶆ*OWPLFUIFNPEFMUPHFOFSBUFBDUJPOT 
  33. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) ᶃ#VJMEUIFQSPNQUUFNQMBUF ᶄ*OJUJBMJ[FUIFNPEFM ᶅ$POOFDUUIFQSPNQUBOENPEFM $PEF-BOH$IBJOQJQFMJOF 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT ᶆ*OWPLFUIFNPEFMUPHFOFSBUFBDUJPOT 
  34. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) Please convert the given instruction text into a JSON array of motion commands for the Pico Car. If the input text is not an instruction (for example, “Good morning!” or “Idiot!”), convert it into a command array that expresses the car’s emotion in response. Output only the command array — do not include any explanations or additional text. Each command should follow the format below: … 1SPNQUT 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT 1SPNQU 
  35. from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate ... prompt

    = ChatPromptTemplate( [ ("system", TEXT_TO_COMMAND_PROMPT), ("human", "ࢦࣔ: {text}"), ] ) llm = init_chat_model(model="gpt-4.1", temperature=0) chain = prompt | llm result = chain.invoke({"text": text}) instructions = result.content # Add RGB color info based on the LED color name (CSS color name) in the generated data instructions = _add_led_rgb(instructions) instructions = json.loads(instructions) Please convert the given Japanese instruction text into a JSON array of motion commands for the Pico Car. If the input text is not an instruction (for example, “Good morning!” or “Idiot!”), convert it into a command array that expresses the car’s emotion in response. Output only the command array — do not include any explanations or additional text. Each command should follow the format below: … 1SPNQUT 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT 1SPNQU 
  36. ͋ͳͨՄѪ͍ΘͶʙ w 8IFOUIFJOQVUJTOPUDMFBS  UIFQSPNQUBTLTUIFNPEFMUPSFTQPOEXJUIBOFNPUJPO VTJOHNPWFNFOUBOE-&%DPMPS 👉&WFOXJUIVODMFBSJOQVU UIFDBSCFIBWFTMJLFBTNBMMBOJNBM ͹͔͹͔ʂ ஥௚Γ͠Α͏

    :PVSFTPDVUF *EJPU -FU`TCFGSJFOETBHBJO .PWFGPSXBSEXJUIQJOL-&%T  TIBLJOHTJEFUPTJEF KPZ .PWFCBDLXBSEXJUICMVF-&%T TBEOFTT .PWFGPSXBSEXJUIHSFFO-&%T QPTJUJWFGFFMJOH ,FZQPJOUTPGUIFQSPNQU 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT 
  37. ͋ͳͨՄѪ͍ΘͶʙ w 8IFOUIFJOQVUJTOPUDMFBS  UIFQSPNQUBTLTUIFNPEFMUPSFTQPOEXJUIBOFNPUJPO VTJOHNPWFNFOUBOE-&%DPMPS 👉&WFOXJUIVODMFBSJOQVU UIFDBSCFIBWFTMJLFBTNBMMBOJNBM ͹͔͹͔ʂ ஥௚Γ͠Α͏

    :PVSFTPDVUF *EJPU -FU`TCFGSJFOETBHBJO .PWFGPSXBSEXJUIQJOL-&%T  TIBLJOHTJEFUPTJEF KPZ .PWFCBDLXBSEXJUICMVF-&%T TBEOFTT .PWFGPSXBSEXJUIHSFFO-&%T QPTJUJWFGFFMJOH ,FZQPJOUTPGUIFQSPNQU 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT *EJE/05FYQMJDJUMZEF fi OFUIJOHT MJLFTBENFBOTCMVFJOUIFQSPNQU 5IFNPEFMDIPPTFTBOBUVSBMDPMPSPOJUTPXO  MFBEJOHUPSJDIFSFYQSFTTJPOT 
  38. [ { "action": {"type": "forward", "distance_cm": 20}, "led": {"color": "green"}},

    { "action": {"type": "turn", "direction": "right", "angle": 90}, "led": {"color": "green"}}, ...] BDUJPOUVSOSJHIU ›  MFEHSFFO BDUJPONPWFGPSXBSE DN  MFEHSFFO &YBNQMFPGHFOFSBUFEBDUJPOEBUB TUDPNNBOE 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT OEDPNNBOE 
  39. [ { "action": {"type": "forward", "distance_cm": 20}, "led": {"color": "green",

    "rgb": (0, 255, 0) }}, { "action": {"type": "turn", "direction": "right", "angle": 90}, "led": {"color": "green", "rgb": (0, 255, 0)}}, ...] 1PTUQSPDFTTJOH$PMPSOBNFˠ3(# 4UFQ5SBOTDSJCFWPJDFBOEHFOFSBUFTUSVDUVSFEBDUJPOT $POWFSU$44DPMPSOBNFTUP3(# VTJOHTUBOEBSE$44SVMFT w 5IFNPEFMPVUQVUTPOMZDPMPSOBNFT3(#WBMVFTBSFBEEFEMBUFSJO1ZUIPO w $PMPSOBNFTBSFFBTJFSGPSUIF"*UPFYQSFTTFNPUJPOT  TP*MFUUIFNPEFMPVUQVU$44DPMPSOBNFTPOMZ w 3(#DPOWFSTJPOJTEPOFJO1ZUIPOUPLFFQUIJOHTTJNQMFBOEDPSSFDU 
  40. BVEJPEBUB *OQVUWPJDF WJBBXFCBQQ 3FDFJWFBOE FYFDVUFDPNNBOET 5SBOTDSJCFWPJDF BOEDPOWFSUJUJOUP TUSVDUVSFEBDUJPOT VTJOHHFOFSBUJWF"* 4UFQ

    4UFQ 4UFQ 8FCCSPXTFS POBTNBSUQIPOF 1JDP$POUSPMMFS4FSWFS ʢ.BDʣ 1JDP$BS4FSWFS ʢ3BTQCFSSZ1J1JDP$BSʣ 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET TUSVDUVSFEBDUJPOT 
  41. w 3BTQCFSSZ1J1JDPEPFTOPUSVOTUBOEBSE$1ZUIPO ŠJUTVQQPSUT.JDSP1ZUIPOJOTUFBE w "XFC"1*TFSWFSJTJNQMFNFOUFEVTJOH.JDSPEPU w .JDSPEPUJTBNJOJNBMXFCGSBNFXPSLJOTQJSFECZ'MBTL w .JDSPEPUTVQQPSUTCPUI$1ZUIPOBOE.JDSP1ZUIPO 

    TPJUDBOSVOEJSFDUMZPOUIF1JDP w 'PSUIJTQSPKFDU KVTUQMBDFmicrodot.py BCPVU,# POUIF1JDP 3VOOJOHB8FC"1*4FSWFSPO1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET 
  42. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET $SFBUFB.JDSPEPUBQQ 
  43. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE 4XJUDICFUXFFONPDL BOESFBMIBSEXBSF GPSFBTZUFTUJOH $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET $SFBUFB.JDSPEPUBQQ 
  44. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE 4XJUDICFUXFFONPDL BOESFBMIBSEXBSF GPSFBTZUFTUJOH $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT $SFBUFB.JDSPEPUBQQ ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET 
  45. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET $SFBUFB.JDSPEPUBQQ 'MBTLMJLFTZOUBY 
  46. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET $SFBUFB.JDSPEPUBQQ 'MBTLMJLFTZOUBY 
  47. from microdot import Microdot from pico_car import Car, RealCar car

    = Car() if settings.USE_MOCK else RealCar() app = Microdot() ... @app.route("/command", methods=["POST"]) async def handle_command(request): try: car.sound_freq(1000, 0.1) car.sound_freq(1200, 0.1) command_list = request.json car.run_commands(command_list) return {"status": "ok"} except Exception as e: handle_buggy_error() print_exception(e) return {"status": "error", "message": str(e)}, 500 ... app.run(port=5001) ᶃ1MBZBOPUJ fi DBUJPOTPVOE $SFBUFBOJOTUBODFPGUIFDVTUPN1JDP$BSDMBTT ᶄ&YFDVUFDPNNBOET )BOEMFFSSPST FH QMBZFSSPSTPVOE TUPQNPUPST -BVODIUIFTFSWFS $PEF.JDSPEPU4FSWFSPOUIF1JDP 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET %F fi OFBO"1*UPSFDFJWFBOE FYFDVUFDPNNBOET $SFBUFB.JDSPEPUBQQ 
  48. w &YFDVUFDPNNBOETPOFCZPOFJOBforMPPQ w &BDIDPNNBOEDPOUSPMTNPUPSTBOE-&%T w "DUVBMDPOUSPMJTIBOEMFECZUIF1JDP$BSLJUMJCSBSZ w 1JDP$BSLJUMJCSBSZ .JDSP1ZUIPO 

    IUUQTHJUIVCDPN,JUSPOJL-UE,JUSPOJL1JDP"VUPOPNPVT3PCPUJDT1MBUGPSN.JDSP1ZUIPO 1SPDFTTJOH$PNNBOETJO0SEFS 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET from PicoAutonomousRobotics import KitronikPicoRobotBuggy buggy = KitronikPicoRobotBuggy() ... buggy.motorOn("l", "f", SPEED) # left wheel: forward buggy.motorOn("r", "f", SPEED) # right wheel: forward buggy.motorOn("l", "f", SPEED) # left wheel: forward buggy.motorOn("r", "r", SPEED) # right wheel: reverse 'PSXBSE 5VSO3JHIU &YBNQMF.PUPSDPOUSPMDPEF MJCSBSZVTBHF 
  49. 4UFQ.FBTVSFEJTUBODFBOHMF ɹɹɹBGUFSNPWJOHGPSTFDPOE 4UFQ(FUDVSSFOUTFUUJOHT 4UFQ"EKVTUTFUUJOHT VTJOHNFBTVSFEWBMVFT w %FQFOEJOHPOUIFTVSGBDF GPSFYBNQMF  fl

    PPSPSDBSQFU  UIFDBSDBONPWFEJ ff FSFOUMZFWFOXJUIUIFTBNFDPNNBOE 👉*QSPWJEFBTFUUJOHTQBOFMJO(SBEJPUPBEKVTUQBSBNFUFSTTVDIBTNPUPSTQFFE "EKVTUJOH.PUPS4QFFE 4UFQ3FDFJWFBOEFYFDVUFDPNNBOET 
  50. 

  51. w (FOFSBUJWF"*º1JDPDBOTUJMMCFVTFEJONBOZNPSFXBZT w "EENPSFBDUJPOTUPUIF1JDP$BS w FH NPSF-&%QBUUFSOT PSXBHJUTUBJMXJUIBTFSWPNPUPS w $VTUPNJ[FUIF1JDP$BS`TQFSTPOBMJUZJOUIFQSPNQU

    w FH CZDIBOHJOHUIFQFSTPOBMJUZJOUIFQSPNQU  UIFTBNFXPSE l*EJPUz DBOUSJHHFS TBEOFTT CMVF-&% PSBOHFS SFE-&%  *EFBTGPSFYUFOTJPO 3FTVMU 
  52. w *OQVUBSPVUFJNBHFGSPNUIF6*CVJMUXJUI(SBEJP w l4z4UBSUQPJOU l(z(PBMQPJOU w 4FOEUIFQSPNQUBOEJNBHFUPUIFHFOFSBUJWF"* %SBXUIFSPVUFPOUIFDBOWBT BOEJOQVUJU ʢgr.ImageEditorʣ

    The image shows a drawn route. “S” marks the start, and “G” marks the goal. Convert this image into a JSON array of motion commands for the Pico Car. ... 1SPNQU $POUSPMMJOHUIF1JDP$BSXJUIBSPVUFJNBHF "QQMJDBUJPO*NBHF#BTFE$PNNBOET 
  53. 

  54. 

  55. 8IZUIJTBSDIJUFDUVSFEPFTOPUXPSLGPSNJDSPCJU "QQMJDBUJPO$POUSPMMJOHNJDSPCJUSPCPU BVEJPEBUB TUSVDUVSFEBDUJPOT 8FCCSPXTFS POBTNBSUQIPOF $POUSPMMFS4FSWFS ʢ.BDʣ $BS4FSWFS w

    'PSUIFNJDSPCJU UIFBSDIJUFDUVSFJTBCJUEJ ff FSFOU w #ZEFGBVMU UIFNJDSPCJUEPFT/05IBWF8J'JDPOOFDUJWJUZ w "TBSFTVMU SVOOJOHB8FC"1*TFSWFSPOUIFNJDSPCJUJT/05QSBDUJDBM NJDSPCJU 
  56. "OBMUFSOBUJWFBSDIJUFDUVSFGPSNJDSPCJU "QQMJDBUJPO$POUSPMMJOHNJDSPCJUSPCPU BVEJPEBUB TUSVDUVSFE BDUJPOT 8FCCSPXTFS POBTNBSUQIPOF $POUSPMMFS4FSWFS w 8JUIBXJSFEDPOOFDUJPO

    B.BDBOEBNJDSPCJUDBOFBTJMZFYDIBOHFEBUB VTJOHTFSJBMDPNNVOJDBUJPO 1Z4FSJBM  w NJDSPCJUTDBOBMTPDPNNVOJDBUFXJUIFBDIPUIFSXJSFMFTTMZ w 👉4P *VTFPOFNJDSPCJUDPOOFDUFEUPUIF.BDWJB64#  BOETFOEEBUBXJSFMFTTMZUPBOPUIFSNJDSPCJUPOUIFSPCPU $BS4FSWFS .BD NJDSPCJUᶃ NJDSPCJUᶄ TJNQMF EBUB TJNQMF EBUB 64# .BD 4FSJBMDPNNVOJDBUJPO XJSFMFTTMZ 
  57. 4VNNBSZ w *TIPXFEIPXUPDSFBUFBUPZDBS UIBUNPWFTVTJOHWPJDFBOEJNBHFTXJUIHFOFSBUJWF"* w 8JUI--.T UBTLTUIBUXFSFEJ ffi DVMUCFGPSFBSFOPXNVDIFBTJFS w

    8JUI(SBEJP IBOEMJOHWPJDFBOEJNBHFJOQVUCFDPNFTTJNQMF w 8JUI-BOH$IBJO JU`TFBTZUPJOUFHSBUFHFOFSBUJWF"*JOUPBQQMJDBUJPOT w 5IFDPNCJOBUJPOPGHFOFSBUJWF"*BOE3BTQCFSSZ1J1JDP IBTNBOZQPTTJCJMJUJFT