@@ -201,127 +201,147 @@ import (
201
201
" go.uber.org/multierr"
202
202
203
203
" go.viam.com/rdk/components/base"
204
- " go.viam.com/rdk/components/generic "
204
+ " go.viam.com/rdk/components/base/kinematicbase "
205
205
" go.viam.com/rdk/components/motor"
206
- " go.viam.com/rdk/config"
207
- " go.viam.com/rdk/registry"
208
206
" go.viam.com/rdk/resource"
209
- " go.viam.com/rdk/utils "
207
+ " go.viam.com/rdk/spatialmath "
210
208
)
211
209
212
- // Here is where we define our new model's colon-delimited-triplet (acme:demo:mybase)
210
+ // Here is where we define your new model's colon-delimited-triplet (acme:demo:mybase)
213
211
// acme = namespace, demo = family, mybase = model name.
214
212
var (
215
213
Model = resource.NewModel (" acme" , " demo" , " mybase" )
216
214
errUnimplemented = errors.New (" unimplemented" )
217
215
)
218
216
219
- // Constructor
220
- func newBase (ctx context .Context , deps registry .Dependencies , config config .Component , logger golog .Logger ) (interface {}, error ) {
221
- b := &MyBase{logger: logger}
222
- err := b.Reconfigure (config, deps)
223
- return b, err
224
- }
217
+ const (
218
+ myBaseWidthMm = 500.0 // Base has a wheel tread of 500 millimeters
219
+ myBaseTurningRadiusM = 0.3 // Base turns around a circle of radius .3 meters
220
+ )
225
221
226
- // Defines what the JSON configuration should look like
227
- type MyBaseConfig struct {
228
- LeftMotor string ` json:"motorL" `
229
- RightMotor string ` json:"motorR" `
222
+ func init () {
223
+ resource. RegisterComponent (base. API , Model, resource. Registration [base. Base , *Config] {
224
+ Constructor: newBase,
225
+ })
230
226
}
231
227
232
- // Validates JSON configuration
233
- func ( cfg * MyBaseConfig ) Validate ( path string ) ([] string , error ) {
234
- if cfg. LeftMotor == " " {
235
- return nil , fmt. Errorf ( ` expected "motorL" attribute for mybase %q ` , path)
228
+ func newBase ( ctx context . Context , deps resource . Dependencies , conf resource . Config , logger golog . Logger ) ( base . Base , error ) {
229
+ b := &myBase {
230
+ Named: conf. ResourceName (). AsNamed (),
231
+ logger: logger,
236
232
}
237
- if cfg. RightMotor == " " {
238
- return nil , fmt. Errorf ( ` expected "motorR" attribute for mybase %q ` , path)
233
+ if err := b. Reconfigure (ctx, deps, conf); err != nil {
234
+ return nil , err
239
235
}
240
-
241
- return []string {cfg.LeftMotor , cfg.RightMotor }, nil
236
+ return b, nil
242
237
}
243
238
244
- // Handles attribute reconfiguration
245
- func (base *MyBase ) Reconfigure (cfg config .Component , deps registry .Dependencies ) error {
246
- base.left = nil
247
- base.right = nil
248
- baseConfig , ok := cfg.ConvertedAttributes .(*MyBaseConfig)
249
- if !ok {
250
- return utils.NewUnexpectedTypeError (baseConfig, cfg.ConvertedAttributes )
239
+
240
+ // Reconfigure reconfigures with new settings.
241
+ func (b *myBase ) Reconfigure (ctx context .Context , deps resource .Dependencies , conf resource .Config ) error {
242
+ b.left = nil
243
+ b.right = nil
244
+
245
+ // This takes the generic resource.Config passed down from the parent and converts it to the
246
+ // model-specific (aka "native") Config structure defined, above making it easier to directly access attributes.
247
+ baseConfig , err := resource.NativeConfig [*Config](conf)
248
+ if err != nil {
249
+ return err
251
250
}
252
- var err error
253
251
254
- base .left , err = motor.FromDependencies (deps, baseConfig.LeftMotor )
252
+ b .left , err = motor.FromDependencies (deps, baseConfig.LeftMotor )
255
253
if err != nil {
256
254
return errors.Wrapf (err, " unable to get motor %v for mybase" , baseConfig.LeftMotor )
257
255
}
258
256
259
- base .right , err = motor.FromDependencies (deps, baseConfig.RightMotor )
257
+ b .right , err = motor.FromDependencies (deps, baseConfig.RightMotor )
260
258
if err != nil {
261
259
return errors.Wrapf (err, " unable to get motor %v for mybase" , baseConfig.RightMotor )
262
260
}
263
261
264
- // Stopping motors at reconfiguration
265
- return multierr.Combine (base.left .Stop (context.Background (), nil ), base.right .Stop (context.Background (), nil ))
262
+ geometries , err := kinematicbase.CollisionGeometry (conf.Frame )
263
+ if err != nil {
264
+ b.logger .Warnf (" base %v %s " , b.Name (), err.Error ())
265
+ }
266
+ b.geometries = geometries
267
+
268
+ // Stop motors when reconfiguring.
269
+ return multierr.Combine (b.left .Stop (context.Background (), nil ), b.right .Stop (context.Background (), nil ))
270
+ }
271
+
272
+ // DoCommand simply echos whatever was sent.
273
+ func (b *myBase ) DoCommand (ctx context .Context , cmd map [string ]interface {}) (map [string ]interface {}, error ) {
274
+ return cmd, nil
266
275
}
267
276
268
- // Attributes of the base
269
- type MyBase struct {
270
- generic.Echo
271
- left motor.Motor
272
- right motor.Motor
273
- logger golog.Logger
277
+ // Config contains two component (motor) names.
278
+ type Config struct {
279
+ LeftMotor string ` json:"motorL"`
280
+ RightMotor string ` json:"motorR"`
274
281
}
275
282
276
- // Implement the methods the Viam RDK defines for the base API (rdk:component:base)
283
+ // Validate validates the config and returns implicit dependencies,
284
+ // this Validate checks if the left and right motors exist for the module's base model.
285
+ func (cfg *Config ) Validate (path string ) ([]string , error ) {
286
+ // check if the attribute fields for the right and left motors are non-empty
287
+ // this makes them reuqired for the model to successfully build
288
+ if cfg.LeftMotor == " " {
289
+ return nil , fmt.Errorf (` expected "motorL" attribute for mybase %q` , path)
290
+ }
291
+ if cfg.RightMotor == " " {
292
+ return nil , fmt.Errorf (` expected "motorR" attribute for mybase %q` , path)
293
+ }
277
294
278
- // MoveStraight: unimplemented
279
- func (base *MyBase ) MoveStraight (ctx context .Context , distanceMm int , mmPerSec float64 , extra map [string ]interface {}) error {
280
- return errUnimplemented
295
+ // Return the left and right motor names so that `newBase` can access them as dependencies.
296
+ return []string {cfg.LeftMotor , cfg.RightMotor }, nil
297
+ }
298
+
299
+ type myBase struct {
300
+ resource.Named
301
+ left motor.Motor
302
+ right motor.Motor
303
+ logger golog.Logger
304
+ geometries []spatialmath.Geometry
281
305
}
282
306
283
- // Spin: unimplemented
284
- func (base * MyBase ) Spin (ctx context .Context , angleDeg , degsPerSec float64 , extra map [string ]interface {}) error {
307
+ // MoveStraight does nothing.
308
+ func (b * myBase ) MoveStraight (ctx context .Context , distanceMm int , mmPerSec float64 , extra map [string ]interface {}) error {
285
309
return errUnimplemented
286
310
}
287
311
288
- // SetVelocity: unimplemented
289
- func (base * MyBase ) SetVelocity (ctx context .Context , linear , angular r3 . Vector , extra map [string ]interface {}) error {
312
+ // Spin does nothing.
313
+ func (b * myBase ) Spin (ctx context .Context , angleDeg , degsPerSec float64 , extra map [string ]interface {}) error {
290
314
return errUnimplemented
291
315
}
292
316
293
- // Properties: unimplemented
294
- func (base * MyBase ) Spin (ctx context .Context , extra map [string ]interface {}) error {
317
+ // SetVelocity does nothing.
318
+ func (b * myBase ) SetVelocity (ctx context .Context , linear , angular r3 . Vector , extra map [string ]interface {}) error {
295
319
return errUnimplemented
296
320
}
297
321
298
- // SetPower: sets the linear and angular velocity of the left and right motors on the base
299
- func (base * MyBase ) SetPower (ctx context .Context , linear , angular r3 .Vector , extra map [string ]interface {}) error {
300
- // stop the base if absolute value of linear and angular velocity is less than .01
322
+ // SetPower computes relative power between the wheels and sets power for both motors.
323
+ func (b * myBase ) SetPower (ctx context .Context , linear , angular r3 .Vector , extra map [string ]interface {}) error {
324
+ b. logger . Debugf ( " SetPower Linear: %.2f Angular: %.2f " , linear. Y , angular. Z )
301
325
if math.Abs (linear.Y ) < 0.01 && math.Abs (angular.Z ) < 0.01 {
302
- return base .Stop (ctx, extra)
326
+ return b .Stop (ctx, extra)
303
327
}
304
-
305
- // use linear and angular velocity to calculate percentage of max power to pass to SetPower for left & right motors
306
328
sum := math.Abs (linear.Y ) + math.Abs (angular.Z )
307
- err1 := base .left .SetPower (ctx, (linear.Y -angular.Z )/sum, extra)
308
- err2 := base .right .SetPower (ctx, (linear.Y +angular.Z )/sum, extra)
329
+ err1 := b .left .SetPower (ctx, (linear.Y -angular.Z )/sum, extra)
330
+ err2 := b .right .SetPower (ctx, (linear.Y +angular.Z )/sum, extra)
309
331
return multierr.Combine (err1, err2)
310
332
}
311
333
312
- // Stop: stops the base from moving by stopping both motors
313
- func (base *MyBase ) Stop (ctx context .Context , extra map [string ]interface {}) error {
314
- base.logger .Debug (" Stop" )
315
-
316
- err1 := base.left .Stop (ctx, extra)
317
- err2 := base.right .Stop (ctx, extra)
318
-
334
+ // Stop halts motion.
335
+ func (b *myBase ) Stop (ctx context .Context , extra map [string ]interface {}) error {
336
+ b.logger .Debug (" Stop" )
337
+ err1 := b.left .Stop (ctx, extra)
338
+ err2 := b.right .Stop (ctx, extra)
319
339
return multierr.Combine (err1, err2)
320
340
}
321
341
322
- // IsMoving: checks if either motor on the base is moving with motors' IsPowered
323
- func (base * MyBase ) IsMoving (ctx context .Context ) (bool , error ) {
324
- for _ , m := range []motor.Motor {base .left , base .right } {
342
+ // IsMoving returns true if either motor is active.
343
+ func (b * myBase ) IsMoving (ctx context .Context ) (bool , error ) {
344
+ for _ , m := range []motor.Motor {b .left , b .right } {
325
345
isMoving , _ , err := m.IsPowered (ctx, nil )
326
346
if err != nil {
327
347
return false , err
@@ -333,25 +353,22 @@ func (base *MyBase) IsMoving(ctx context.Context) (bool, error) {
333
353
return false , nil
334
354
}
335
355
336
- // Stop the base from moving when closing a client's connection to the base
337
- func (base *MyBase ) Close (ctx context .Context ) error {
338
- return base.Stop (ctx, nil )
356
+ // Properties returns details about the physics of the base.
357
+ func (b *myBase ) Properties (ctx context .Context , extra map [string ]interface {}) (base .Properties , error ) {
358
+ return base.Properties {
359
+ TurningRadiusMeters: myBaseTurningRadiusM,
360
+ WidthMeters: myBaseWidthMm * 0.001 , // converting millimeters to meters
361
+ }, nil
339
362
}
340
363
341
- // Register the component with the Go SDK
342
- func init () {
343
- registry.RegisterComponent (base.Subtype , Model, registry.Component {Constructor: newBase})
344
-
345
- // VALIDATION: Uses RegisterComponentAttributeMapConverter to register a custom configuration struct that has a Validate(string) ([]string, error) method.
346
- // The Validate method will automatically be called in RDK's module manager to validate MyBase's configuration and register implicit dependencies.
347
- config.RegisterComponentAttributeMapConverter (
348
- base.Subtype ,
349
- Model,
350
- func (attributes config.AttributeMap ) (interface {}, error ) {
351
- var conf MyBaseConfig
352
- return config.TransformAttributeMapToStruct (&conf, attributes)
353
- },
354
- &MyBaseConfig{})
364
+ // Geometries returns physical dimensions.
365
+ func (b *myBase ) Geometries (ctx context .Context , extra map [string ]interface {}) ([]spatialmath .Geometry , error ) {
366
+ return b.geometries , nil
367
+ }
368
+
369
+ // Close stops motion during shutdown.
370
+ func (b *myBase ) Close (ctx context .Context ) error {
371
+ return b.Stop (ctx, nil )
355
372
}
356
373
```
357
374
0 commit comments