Anmäl dig till Roborock Challenge!

Python simulera en gräsklippare

Permalänk

Python simulera en gräsklippare

Hej,

Jag har fått en skoluppgift där jag ska simulera en robotgräsklippare i Python.
Det är tillåtet att ta hjälp, söka info på nätet och använda tex ChatGPT sålänge man kan resonera kring koden och visa att man har full förståelse för den. Hittills har jag klarat mig utan hjälp, men har nu stött på ett problem som jag inte lyckas lösa.

När linjen har studsat första gången mot väggen, så fortsätter den genom att skapa en sågtandsform. Jag antar att det har att göra med speedX =- speedX i villkorssatsen på något sätt. Vad jag vill är att när linjen har studsat, ska den fortsätta rakt fram tills den når en vägg eller ett hinder igen, och först då ändra riktning.

Vore tacksam om någon kan presentera en lösning för detta så att jag kan gå vidare.

import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from matplotlib.animation import FuncAnimation import math import random plot_map = [[0, 0, 0, 0, 0], [0, 0, 2, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] rows = len(plot_map) # 5 cols = len(plot_map[0]) # 4 plt.figure() # Assign color to value: 0 = green, 1 = cut, 2 = obsticle color_map = ListedColormap(['green', 'yellow', 'red'], 'indexed') print("Plot origo", plot_map[0][0]) # Plot grid plot_map.reverse() # Turn plot upside-down plt.pcolormesh(plot_map, edgecolors='k', linewidth=2, cmap=color_map) # Fine tune plot layout ax = plt.gca() # Get current axis object ax.set_yticks(range(0, rows+1, 1)) ax.set_xticks(range(0, cols+1, 1)) plt.title(f"Colored grid of size {rows}x{cols}") # Initiate list positionsX = [] positionsY = [] # Start positions positionsX.append(3) positionsY.append(0) # Set X-and Y length of map map_cols = len(plot_map[0]) map_rows = len(plot_map) # Random start angles for the line / first movement randAngle = random.uniform(0, math.pi) print("rand") # Speed in m/s speedV = 0.3 # Next movement function to FuncAnimation def animate(i, angle): # Speed for movement, set to -1 when change direction speedX = speedV * math.cos(randAngle) speedY = speedV * math.sin(randAngle) for x in range(1): # Check if outside map X (right side) if positionsX[-1] + speedX >= map_cols: speedX =- speedX print("Detected collision (X+) with ", positionsX[-1], " angle ", angle, speedX) # And check left side elif positionsX[-1] + speedX < 0: speedX =- speedX print("Detected collision (X-) with ", positionsX[-1], " angle ", angle, speedX) # Check if outside map Y (top) if positionsY[-1] + speedY >= map_rows: speedY =- speedY print("Detected collision (Y+) with ", positionsY[-1], " angle ", angle, speedY) # And check bottom elif positionsY[-1] + speedY < 0: speedY =- speedY print("Detected collision (Y-) with ", positionsY[-1], " angle ", angle, speedY) # Else continue in the same direction positionsX.append(positionsX[-1] + speedX) positionsY.append(positionsY[-1] + speedY) print("Moving ahead with X ", positionsX[-1], " Y ", positionsY[-1], " angle ", angle, " speedX ", speedX, " speedY ", speedY, "\n") plt.plot(positionsX, positionsY) plt.xlabel(positionsX) plt.ylabel(positionsY) # Declare and call the animation function ani = FuncAnimation(plt.gcf(), func=animate, fargs=(randAngle,), interval=1000) plt.show()

Permalänk
Medlem
Permalänk
Medlem
Permalänk
Medlem
Skrivet av Jull3Haxor:

Kolla PM.

Vad är poängen med att ha en offentlig tråd om man ger lösningen i PM?

Permalänk
Medlem
Skrivet av Jull3Haxor:

Kolla PM.

En förklaring av föreslagen metodik/algorithm kan vi väl få i alla fall. Annars får vi väl anse den här tråden avklarad.

Permalänk
Medlem

Ber om ursäkt, men delar avc svaret är skapat med hjälp av AI (en lat programmrare är en bra sådan stod det i något material för någon kurs..) därför valde jag att ta det privat. Men givetvis skall ni få ta del av hur lösningen såg ut och en förklaring.

Den fungerande koden:

Citat:

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib.animation import FuncAnimation
import math
import random

plot_map = [[0, 0, 0, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]

rows = len(plot_map) # 5
cols = len(plot_map[0]) # 4

plt.figure()
# Assign color to value: 0 = green, 1 = cut, 2 = obsticle
color_map = ListedColormap(['green', 'yellow', 'red'], 'indexed')
plot_map.reverse() # Turn plot upside-down
plt.pcolormesh(plot_map, edgecolors='k', linewidth=2, cmap=color_map)

# Fine tune plot layout
ax = plt.gca()
ax.set_yticks(range(0, rows+1, 1))
ax.set_xticks(range(0, cols+1, 1))
plt.title(f"Colored grid of size {rows}x{cols}")

# Start positions
positionsX = [3]
positionsY = [0]

# Set map boundaries
map_cols = len(plot_map[0])
map_rows = len(plot_map)

# Random start angle for movement
angle = random.uniform(0, math.pi)

# Speed in m/s
speedV = 0.3

# Animation function
def animate(i):
global angle # Angle is now mutable in the function

# Calculate speedX and speedY based on angle
speedX = speedV * math.cos(angle)
speedY = speedV * math.sin(angle)

# Update position
newX = positionsX[-1] + speedX
newY = positionsY[-1] + speedY

# Check for collisions with walls
if newX >= map_cols or newX < 0:
angle = math.pi - angle # Reverse direction in X-axis
speedX = -speedX # Reflect speedX
if newY >= map_rows or newY < 0:
angle = -angle # Reverse direction in Y-axis
speedY = -speedY # Reflect speedY

# Append new position
positionsX.append(positionsX[-1] + speedX)
positionsY.append(positionsY[-1] + speedY)

plt.plot(positionsX, positionsY, 'bo-')

# Start animation
ani = FuncAnimation(plt.gcf(), func=animate, interval=1000)
plt.show()

Vad har vi gjort här då?

För att få gräsklipparen att röra sig rakt fram efter en studs snarare än att bilda en sågtandsform behöver vi kontrollera hur vi ändrar riktningen vid kollisioner med väggarna. Istället för att bara sätta hastigheten till negativa värden vid varje kollision, kan vi uppdatera vinkeln när en kollision sker. Här är en förbättrad version som justerar riktningen baserat på kollisionens riktning.

När gräsklipparen träffar en vägg ändras vinkeln istället för hastigheten direkt.
Funktionen animate använder sedan vinkeln för att uppdatera hastighetskomponenterna, vilket gör att rörelsen fortsätter rakt fram.

Permalänk
Medlem
Skrivet av Jull3Haxor:

Ber om ursäkt, men delar avc svaret är skapat med hjälp av AI (en lat programmrare är en bra sådan stod det i något material för någon kurs..) därför valde jag att ta det privat. Men givetvis skall ni få ta del av hur lösningen såg ut och en förklaring.

Den fungerande koden:

Vad har vi gjort här då?

För att få gräsklipparen att röra sig rakt fram efter en studs snarare än att bilda en sågtandsform behöver vi kontrollera hur vi ändrar riktningen vid kollisioner med väggarna. Istället för att bara sätta hastigheten till negativa värden vid varje kollision, kan vi uppdatera vinkeln när en kollision sker. Här är en förbättrad version som justerar riktningen baserat på kollisionens riktning.

När gräsklipparen träffar en vägg ändras vinkeln istället för hastigheten direkt.
Funktionen animate använder sedan vinkeln för att uppdatera hastighetskomponenterna, vilket gör att rörelsen fortsätter rakt fram.

OK men nu ville ju TS förstå resonemanget och inte bara få ett AI-genererat svar som han kunnat frågat om själv.
Blir ganska fattigt diskussionsunderlag att bara copy-pasta ChatGPT

Visa signatur

Jag har en tigerrandig vakthund... akta er

Permalänk
Medlem
Skrivet av Tossefar:

OK men nu ville ju TS förstå resonemanget och inte bara få ett AI-genererat svar som han kunnat frågat om själv.
Blir ganska fattigt diskussionsunderlag att bara copy-pasta ChatGPT

Hela svaret var inte AI genererat och han fick lite mer än ni fick, samt förstod hur det hängde ihopp. Visst, kanske inte var optimalt, tänkte mig inte för kanske, my bad. skall inte göra på detta vis i framtiden, sorry. Men påpekar att det inte var en enkel c-p utan utan det låg arbete bakom det hela med, använde inte AI för att ge svraret utan för att snabba upp lite för egen del då jag är lat.

Permalänk

Tack för svaret. Nu har jag fått det att fungera hyfsat. Har även lyckats identifiera hinder (2:or) i kartan och den delen fungerar.

Däremot beter den sig konstigt vid vissa kollisioner med hinder. Vore tacksam om någon kan hjälpa mig att reda ut varför.

Sedan märker jag att efter att ha kört koden ett tag, så verkar det bli ganska tungt. Är det för att listan innehåller så mycket data? Finns det något sätt jag kunde gjort annorlunda?

import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from matplotlib.animation import FuncAnimation import math import random import numpy as np plot_map = [[0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0], [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0], [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0], [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] rows = len(plot_map) # 5 cols = len(plot_map[0]) # 4 plt.figure() # Assign color to value: 0 = green, 1 = cut, 2 = obsticle color_map = ListedColormap(['green', 'yellow', 'red'], 'indexed') print("Plot origo", plot_map[0][0]) # Plot grid plot_map.reverse() # Turn plot upside-down plt.pcolormesh(plot_map, edgecolors='k', linewidth=2, cmap=color_map) # Create lists and coordinates for obsticles obsticlesX = [] obsticlesY = [] arr = np.array(plot_map) for index, item in np.ndenumerate(arr): if item == 2: obsticlesX.append(index[1]) obsticlesY.append(index[0]) # Fine tune plot layout ax = plt.gca() # Get current axis object ax.set_yticks(range(0, (rows+1), 1)) ax.set_xticks(range(0, (cols+1), 1)) plt.title(f"Lawnmower (garden size {rows}x{cols})") # Initiate list positionsX = [] positionsY = [] # Start positions positionsX.append(3) positionsY.append(0) # Set X-and Y length of map map_cols = len(plot_map[0]) map_rows = len(plot_map) # Random start angles for the line / first movement angle = random.uniform(0, math.pi) print("rand") # Speed in m/s velocity = 0.3 #angle=20*math.pi/180 # Next movement function to FuncAnimation # the positionX/Y vectors should also be arguments of animate() # left as an exercise for the reader def animate(i, velocities): newAngle = random.uniform(0, math.pi/2) x, y = positionsX[-1], positionsY[-1] vX, vY = velocities # check that next position is in the grid if x+vX >= map_cols: vX = -velocity * math.cos(newAngle) vY = velocity * math.sin(newAngle) * random.randrange(-1, 2, 2) elif x+vX <= 0: vX = velocity * math.cos(newAngle) vY = velocity * math.sin(newAngle) * random.randrange(-1, 2, 2) if y+vY >= map_rows: vX = velocity * math.cos(newAngle * 2) vY = -velocity * math.sin(newAngle) elif y+vY <= 0: vX = velocity * math.cos(newAngle * 2) vY = velocity * math.sin(newAngle) # Iterate list of obsticles coordinates for a in range(len(obsticlesX)): # If target coordinates in between coordinates of obsticlelist if obsticlesX[a] <= x + vX <= obsticlesX[a] + 1 and obsticlesY[a] <= y + vY <= obsticlesY[a] + 1: newAngle2 = random.uniform(0, math.pi/2) vX = velocity * math.cos(newAngle2 * -1) vY = velocity * math.sin(newAngle2 * -1) # new position with possible bounced velocities x = x + vX y = y + vY # store velocities for next iteration # this is possible because velocities is an array # so is a mutable element velocities[0] = vX velocities[1] = vY positionsX.append(x) positionsY.append(y) plt.xlabel(f"Speed X: {vX}") plt.ylabel(f"Speed Y: {vY}") plt.plot(positionsX, positionsY, color='black') # Declare and call the animation function ani = FuncAnimation(plt.gcf(), func=animate, fargs=([velocity*math.cos(angle),velocity*math.sin(angle)],), interval=10) plt.show()

Permalänk
Medlem

Skall försöka..

Du kan försöka begränsa listlängd för positionerna ör att undvika att listorna `positionsX` och `positionsY` växer sig stora över tid kan du begränsa antalet sparade positioner.
Sedan kan du använd `set_data` för att updatera linjer: Istälet för at rita om listan varje steg. . Ett mer effektivt sätt är att uppdatera en linje på plats genom `set_data` istället för att lägga till nya punkter i `plot`. För att göra detta, skapa först en linjeobjekt utanför `animate`-funktionen och uppdatera dess data i varje animeringssteg. Vidare skulle du kunna testa att minska uppdateringsfrekvensen: Genom att öla intervallet mellan animeringsstegen i `FuncAnimation` för att minska CPU-belastningen.

I den mån det är möjligt kan minska detaljnivån i rutnätet, kanske moinska antalet hinder eller bredden på linjerna, det kan snabba upp randeringen.